仿QQ黑屏,鎖屏,程序切換之後的手勢密碼鎖定,加強版

這是一篇我個人在EOE發的帖子《仿QQ黑屏,鎖屏,程序切換之後的手勢密碼鎖定,》,由於eoe的blog系統實在是無言以對,我就把eoe上面的帖子以及blog是都轉到csdn上來,原帖地址:http://www.eoeandroid.com/thread-313331-1-1.html

加強版:原來是不管你怎麼操作,都是5分鐘鎖定一次,但是客戶要求,只要不觸碰的時候再5分鐘鎖定一次看過第一版的同學,直接拉到帖子最後看,加強版的實現吧,沒看過的,建議從頭看
----------------------------------------------------------------第一版,我是華麗分割線---------------------------------------------------------------------------
需求:項目裏要用類似的如題的功能,然後在那基礎上加一個5分鐘鎖定一次

先說下我原來的思路,

黑屏和鎖屏,5分鐘的切換都好說,黑屏和鎖定有廣播通知,5分鐘自己開線程操作;但是android有長按home(menu)或者home之後再切換回來的功能,這個就要利用activity的生命週期,我開始的思路是錯的,沒有利用好生命週期,最後反編譯qq的apk看代碼纔看出來的;原思路核心代碼如下:
黑屏和鎖屏,代碼是網上搜索的,也貼出來,看下:
/**
         * screen狀態廣播接收者
         */
        private class ScreenBroadcastReceiver extends BroadcastReceiver {
                private String action = null;

                @Override
                public void onReceive(Context context, Intent intent) {
                        action = intent.getAction();
                        if (Intent.ACTION_SCREEN_ON.equals(action)) {
                                mScreenStateListener.onScreenStateChange(true);
                        } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
                                mScreenStateListener.onScreenStateChange(false);
                        }
                }
        }

/**
         * 請求screen狀態更新
         */
        public void requestScreenStateUpdate(ScreenStateListener listener) {
                mScreenStateListener = listener;
                startScreenBroadcastReceiver();
                firstGetScreenState();
        }

        /**
         * 第一次請求screen狀態
         */
        private void firstGetScreenState() {
                PowerManager manager = (PowerManager) mContext
                                .getSystemService(Activity.POWER_SERVICE);
                if (isScreenOn(manager)) {
                        if (mScreenStateListener != null) {
                                mScreenStateListener.onScreenStateChange(true);
                        }
                } else {
                        if (mScreenStateListener != null) {
                                mScreenStateListener.onScreenStateChange(false);
                        }
                }
        }

        /**
         * 停止screen狀態更新
         */
        public void stopScreenStateUpdate() {
                mContext.unregisterReceiver(mScreenReceiver);
        }

        /**
         * 啓動screen狀態廣播接收器
         */
        private void startScreenBroadcastReceiver() {
                IntentFilter filter = new IntentFilter();
                filter.addAction(Intent.ACTION_SCREEN_ON);
                filter.addAction(Intent.ACTION_SCREEN_OFF);
                mContext.registerReceiver(mScreenReceiver, filter);
        }

        /**
         * screen是否打開狀態
         *
         * @param pm
         * @return
         */
        private static boolean isScreenOn(PowerManager pm) {
                boolean screenState;
                try {
                        screenState = (Boolean) mReflectScreenState.invoke(pm);
                } catch (Exception e) {
                        screenState = false;
                }
                return screenState;
        }



5分鐘一次:
private static Timer timer ;
        private static TimerTask timerTask;
        private static boolean isRunning;
        /**
         * 5分鐘一次彈出
         *
         * @param
         * @return void
         * @throws
         */
        public void startVerify(){
                if(timer == null){
                        timer = new Timer();
                }
                if(timerTask == null){
                        timerTask = new TimerTask() {
                                @Override
                                public void run() {
                                                // 使用handler去通知主線程判斷是否開啓鎖定界面
                                        mHandler.sendEmptyMessage(0);
                                }
                        };
                }
                if(!isRunning){
                        timer.schedule(timerTask, 60*1000, 60*1000);
                        isRunning = true;
                }
        }



private Handler mHandler = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                        verify();
                        LogUtil.i("GestureActivity.IS_SHOW-->"+GestureActivity.IS_SHOW);
                }
        };
        /**         * 開啓檢測界面         *          * @param          * @return void         * @throws         */
public void verify(boolean isPause) {
boolean isTopRunning = isRunningForeground(getApplicationContext());
if(isPause){                        if(!isTopRunning){
// 判斷檢測界面是否已經運行
if(!GestureActivity.IS_SHOW)
{                                        Intent intent = new Intent();                                        intent.setClass(getApplicationContext(), GestureActivity.class);                                        intent.putExtra(GestureActivity.INTENT_MODE, GestureActivity.GESTURE_MODE_VERIFY);                                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);                                        startActivity(intent);                                }                        }                }else{                        // 判斷是否在前臺運行                        if(isTopRunning){                                // 判斷檢測界面是否已經運行                                if(!GestureActivity.IS_SHOW){                                        Intent intent = new Intent();                                        intent.setClass(getApplicationContext(), GestureActivity.class);                                        intent.putExtra(GestureActivity.INTENT_MODE, GestureActivity.GESTURE_MODE_VERIFY);                                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);                                        startActivity(intent);                                }                        }                }        }


錯誤的程序切換核心代碼:思路1:onstop中
@Override
        protected void onPause() {
                super.onPause();
                if(isVerify()){
                        mApplication.verify(true);
                }
        }

思路2:在onrestart中
@Override
        protected void onRestart() {
                super.onRestart();
                if (isVerify()) {
                        mApplication.verify(true);
                }
        }



如上代碼,按home健沒問題,但是長按調出最近程序列表的時候就會有問題,會直接彈出來密碼解鎖界面

qq已經實現了,就去看看大牛們是怎麼做的,晚上回去反編譯qq的apk,反編譯出來的資源如下


大企業,代碼寫的一般都會非常規範,一般略懂鳥語的一些常用的東西就能看的明白:
1、首先要看manifest中的activity,首先要定位到解鎖界面是那個類

2、因爲qq所以界面都會響應這操作,所以這個判斷肯定是在一個基類裏,可以找到一個baseactivity裏面有這個判斷的方法,而且必須是在生命週期中,用jd-jui來定位一下代碼看看base中有這樣的代碼,如圖


如圖確實是有一個方法是開啓鎖定界面,然後看了下生命週期,有調用到該方法的地方,那麼下一步就是把新建一個工程,吧qq的src放進去(主要爲了使用eclipse的search功能,也可以用sourceinsight)
3、來分析base的onstart,onresume,onstop(onstart,onresume的操作是一致的)
分析看圖中


在這三個生命週期中用到了一個重要的類GesturePWDUtils,一會分析這個類
多餘代碼我都刪掉了,代碼中的註釋是我加的,純屬個人YY,如果有差錯,也請高擡貴手

public class GesturePWDUtils
{
        // 應該是判斷當前app是否在最前端
  public static boolean getAppForground(Context paramContext)
  {
    boolean bool = getAppSharedPreferences(paramContext).
                    getBoolean("gesturepassword_appforground", false);
    if (QLog.isColorLevel())
    {
            
      String str = "getAppForground.uin=,isAppFroground=" + bool;
      QLog.d("Q.gesturelock.util", 2, str);
    }
    return bool;
  }
  // 應該是判斷當前賬號的是否設置了 手勢密碼鎖定
  public static boolean getJumpLock(Context paramContext, String paramString)
  {
    boolean bool = true;
    SharedPreferences localSharedPreferences = getSharedPreferences(paramContext);
    String str1 = "gesturepassword_gesture_mode" + paramString;
    int i = localSharedPreferences.getInt(str1, 21);
    if (i == 21)
    {
      String str2 = "gesturepassword_gesture_state" + paramString;
      if (localSharedPreferences.getInt(str2, 0) != 2)
        break label146;
    }
    while (true)
    {
      if (QLog.isColorLevel())
      {
        String str3 = "getJumpLock.uin=" +
      paramString + ",isjumplock=" + bool;
        QLog.d("Q.gesturelock.util", 2, str3);
      }
      return bool;
      if ((i == 20) &&
    (localSharedPreferences.
                    getBoolean("gesturepassword_locking", false)))
        continue;
      label146: bool = false;
    }
  }
  // 判斷當前app是否在最前端,可以看代碼,qq把一些activity過濾掉了,
  public static boolean isAppOnForeground(Context paramContext)
  {
    ActivityManager localActivityManager = (ActivityManager)paramContext.getApplicationContext().getSystemService("activity");
    String str1 = paramContext.getApplicationContext().getPackageName();
    Object localObject = localActivityManager.getRunningAppProcesses();
    int i;
    if (localObject == null)
      i = 0;
    while (true)
    {
      return i;
      Iterator localIterator = ((List)localObject).iterator();
      while (true)
        if (localIterator.hasNext())
        {
          localObject = (ActivityManager.RunningAppProcessInfo)localIterator.next();
          if (((ActivityManager.RunningAppProcessInfo)localObject).importance != 100)
            continue;
          if (!((ActivityManager.RunningAppProcessInfo)localObject).processName.equals(str1))
          {
            String str2 = ((ActivityManager.RunningAppProcessInfo)localObject).processName;
            String str3 = str1 + ":qzone";
            if (!str2.equals(str3))
            {
              String str4 = ((ActivityManager.RunningAppProcessInfo)localObject).processName;
              String str5 = str1 + ":picture";
              if (!str4.equals(str5))
              {
                String str6 = ((ActivityManager.RunningAppProcessInfo)localObject).processName;
                String str7 = str1 + ":web";
                if (!str6.equals(str7))
                {
                  String str8 = ((ActivityManager.RunningAppProcessInfo)localObject).processName;
                  String str9 = str1 + ":video";
                  if (!str8.equals(str9))
                  {
                    String str10 = ((ActivityManager.RunningAppProcessInfo)localObject).processName;
                    String str11 = str1 + ":zebra";
                    if (!str10.equals(str11))
                    {
                      String str12 = ((ActivityManager.RunningAppProcessInfo)localObject).processName;
                      String str13 = str1 + ":qqreader";
                      if (!str12.equals(str13))
                        continue;
                    }
                  }
                }
              }
            }
          }
          i = 1;
          break;
        }
      i = 0;
    }
  }
  // 重點,保存當前app是否在最前端的狀態
  public static void setAppForground(Context paramContext, boolean paramBoolean)
  {
    if (QLog.isColorLevel())
    {
      String str = "setAppForground.uin=,isAppFroground=" + paramBoolean;
      QLog.d("Q.gesturelock.util", 2, str);
    }
    SharedPreferences.Editor localEditor1 = getAppSharedPreferences(paramContext).edit();
    SharedPreferences.Editor localEditor2 = localEditor1.putBoolean("gesturepassword_appforground", paramBoolean);
    boolean bool = localEditor1.commit();
  }
}




ok,分析完了代碼,知道了解決方法,來改造我自己的代碼,主要是程序切換的代碼(生命週期中),代碼如下:SpOperationUtil是我對SharedPreferences的封裝,大家看方法名就應該明白,getBooleanInfof的意識是獲取boolean值,默認值爲false

@Override
        protected void onStart() {
                super.onStart();
                isForeGround = SpOperationUtil.getBooleanInfof(CTX, ISFOREGROUND_KEY);
                if(!isForeGround){
                        if (isVerify()) {
                                mApplication.verify();
                        }
                }
                if(!isForeGround){
                        SpOperationUtil.saveBoolean(CTX, ISFOREGROUND_KEY, true);
                }
        }

        @Override
        protected void onResume() {
                super.onResume();
                isForeGround = SpOperationUtil.getBooleanInfof(CTX, ISFOREGROUND_KEY);
             if(!isForeGround){
                     if (isVerify()) {
                                 mApplication.verify();
                         }
             }
             if(!isForeGround){
                     SpOperationUtil.saveBoolean(CTX, ISFOREGROUND_KEY, true);
             }
        }
        String ISFOREGROUND_KEY = "ISFOREGROUND_KEY";
        boolean isForeGround = true;
        @Override
        protected void onStop() {
                super.onStop();
                isForeGround = mApplication.isRunningForeground(CTX);
               
                if(!isForeGround){
                        SpOperationUtil.saveBoolean(CTX, ISFOREGROUND_KEY, isForeGround);
                }
        }




這樣的話,功能就完成......代碼分析先到這,中午整理下,寫個demo,上傳上來

----------------------------------------------------------------第一版,我是華麗分割線---------------------------------------------------------------------------

加強版:只有沒操作之後,5分鐘鎖定一次,注意demo中爲了方便測試設置的是30秒,下面是思路和解決方法:
1、監聽每一個activity的觸碰事件,獲取最新一次的觸碰時間,和上一次時間相比,如果沒超過5分鐘,就把監聽先停掉然後再重新開啓,代碼如下:
base中攔截觸碰時間
@Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        mApplication.setLastTouchTime();
        return super.dispatchTouchEvent(ev);
    }
   MApplication 中
// 上一次觸碰時間
    long lastTimeMillis = 0;
    /**
     * 設置上一次監聽時間
     *
     * @param
     * @return void
     * @throws
     */
    public void setLastTouchTime(){
        // 最新的觸碰時間
        long currentTimeMillis = System.currentTimeMillis();
        if(lastTimeMillis == 0){
            // 第一次觸碰
            lastTimeMillis = currentTimeMillis;
            // 開啓監聽
            startVerify();
        }else{
            //時間差
            long temp = currentTimeMillis - lastTimeMillis;
            // 如果時間差小於5分鐘,就先停掉前一次的監聽,再重新開啓
            if(temp < 1000 * 60 * 5){
                stopVerify();
                startVerify();
            }
            // else 如果大於,那麼上一次的監聽在運行着,5分鐘之後自然會鎖定
        }
    }




2、注意:MainActivity中

// 過濾掉本activity 這個界面不用監聽
        setVerify(false);

       


最後代碼地址:http://download.csdn.net/detail/lsmfeixiang/7859569

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章