Android鎖屏淺析(自用)

本文原創,轉載請註明出處:http://blog.csdn.net/qinjuning



      最近終於成功的擺脫了FM收音機,邁向了新的模塊:鎖屏、狀態欄、Launcher---姑且稱之爲“IDLE”小組,或許叫手機

 美容小組,要是能施展下週星星同學的還我漂漂拳,豈不快哉。 OK,閒話打住,咱開始正文。

    

      本文主要內容:


          1、分析鎖屏界面的組成 ;

          2、基於源代碼分析鎖屏相關類 ;

          3、提出一種在框架取消鎖屏的方法 。

 

      花了一些時間研究Android原生的鎖屏框架---Keyguard,今天就慢慢的講解下我自己對這個模塊的總結,因爲目前還處於

  理論學習的狀況,很多細節以及功能上的實現有待後續的補充完整。

 

     本文分析適合Android2.2和2.3版本,Android4.0尚不清楚。整個鎖屏源碼基本上完全一樣,只是改變了文件存放路徑而已。

     本文分析版本具體是Android2.3版本。


     源文件路徑主要有兩個:

             frameworks\base\policy\src\com\android\internal\policy\impl\   ---->鎖屏框架

             frameworks\base\core\java\com\android\internal\widget\          ----> 提供了一些的自定義View.


  一、鎖屏界面的組成


       通常 Android手機上大家常見的界面只有一種,成功後即可解鎖進入界面了。其實在Android手機中,正常的鎖屏界面由

 兩種不同性質的界面組成:


    第一種界面稱之爲LockScreen界面(爲了敘述方便,我們姑且稱爲“解鎖界面),即我們通常所見到的界面,手機廠商一般定製

          該界面。界面如下所示:

                                                 


          該界面對應自定義View的是LockScreen.java類

           路徑位於:frameworks\policies\base\phone\com\android\internal\policy\impl\LockScreen.java

               

   第二種界面稱之爲UnLockScreen(爲了後文敘述方便,我們姑且稱爲“開鎖界面”),一般由Android源碼提供,有如下四種

             ①、圖案開鎖界面 ---- PatternUnlockScreen.java類     (自定義LinearLayout)

                      路徑位於:frameworks\policies\base\phone\com\android\internal\policy\impl\PatternUnlockScreen.java

                      界面顯示爲:

                                                        


             ②、PIN開鎖界面 ---- SimUnlockScreen.java 類           (自定義LinearLayout)

                      路徑位於:frameworks\policies\base\phone\com\android\internal\policy\impl\SimUnlockScreen.java

                      界面顯示爲:   (圖片省略)

                                             

             ③、密碼開鎖界面 ---- PasswordUnlockScreen.java類   (自定義LinearLayout)

                      路徑位於:frameworks\policies\base\phone\com\android\internal\policy\impl\PasswordUnlockScreen.java

                      界面顯示爲:

                                              


             ④、GoogleAccount 開鎖界面 ,即Google賬戶開鎖界面。一般用於當用戶輸入密碼錯誤次數超過上限值時,系統會提示

       你輸入Google賬戶去開鎖。注意:開啓它需要你手動設置賬戶與同步,否則該界面是不會出來的。

                    對應的源文件是: AccountUnlockScreen.java類    (自定義LinearLayout)

                    路徑位於:frameworks\policies\base\phone\com\android\internal\policy\impl\AccountUnlockScreen.java   

                    界面顯示爲:

                                                              


    可以按照如下辦法選擇開啓哪一種開鎖界面: 設置—>位置和安全—>設置屏幕鎖定  ,具體選擇那種開鎖界面。

 

    顯示規則

         當然,這兩種界面的組合也是有很多變化的,總的規則如下:

            首先顯示LockScreen界面,接着判斷是否開啓了UnLockScreen界面,如果設置了UnLockScreen界面,則進入對應的

    UnLockScreen界面去解鎖,纔算成功解鎖。但,存在一種特殊的情況,就是假如我們選擇了圖案  UnLockScreen界面,是不會

    顯示LockScreen界面,而只會顯示UnLockScreen界面。

 

   二、鎖屏界面的實現


       我們知道, 任何一種界面都是由各種View/ViewGroup(當然包括自定義的)組成的,然後根據系統對應的狀態值的改變去更新

  這些View的顯示狀態,鎖屏界面自然也是如此。鎖屏界面的實現最頂層是採用了FrameLayout去控制的,當然內部也嵌套了很

  多層,內嵌層數的增多的一點好處就是我們可以分開而治,具體針對每層去做相應的更新。難處就是看代碼看的很蛋疼。


       當界面複雜時,我不得不提Google爲開發人員提供的一款優秀工具了---Hierarchy Viewer ,通過它,我們很清晰的弄明白整

  個View樹的繼承層次,一個佈局結構,當然,看源代碼也是必須的。

       關於Hierarchy Viewer的使用請參考該博客:

               《Android 實用工具Hierarchy Viewer實


       整個鎖屏界面的繼承層次如下(部分以及設置了圖案開鎖界面),更加完整的圖請使用Hierarchy Viewer 工具查看。

 


 


      上圖中比較重要的幾個視圖說明如下:

       LockPatternKeyguardView 繼承至FrameLayout :作爲LockScreen和UnLockScreen的載體,用來控制顯示LockScreen

                      還是UnLockScreen界面。

       LockScreen 繼承至FrameLayout   

       PatterUnlockScreen    ViewGroup類型 : 圖案解鎖界面

       KeyguardViewHost繼承至FrameLayout, 該ViewGroup作爲頂層View,作爲WindowManager的裝飾對象添加至窗口。 

        它和LockPatternKeyguardView關係相當於DecorView和我們Activity內設置的資源佈局一樣。  


三、鎖屏機制的類結構說明


             看了幾天代碼,才稍微的理清了下頭緒。看完後給我的感覺就是代碼之間太BT了,幾個類的唯一實例傳來傳去,太容易混

   亂了。接下來我們分析下一些主要的類及其重要的函數,更多函數實現,大家可以自己參考源代碼。


       PS : 由於這些類的結構圖比較簡單,因此就沒畫類圖了。主要是從源碼角度來分析這些代碼邏輯。


     1、 KeyguardScreen 類    接口    


       功能:該接口的主要功能是爲每個需要顯示的界面:LockScreen或者UnLockScreen定義了四個方法,使其在不同的狀態能夠

          得到相應處理。優點就是:   利用設計原則的面向接口編程,減少對具體對象的依賴。

      路徑:\frameworks\base\policy\src\com\android\internal\policy\impl\KeyguardScreen.java

     其源代碼釋義如下:

[java] view plaincopy
  1. /** 
  2.  * Common interface of each {@link android.view.View} that is a screen of 
  3.  * {@link LockPatternKeyguardView}. 
  4.  */   
  5. public interface KeyguardScreen {  
  6.     /** Return true if your view needs input, so should allow the soft 
  7.      * keyboard to be displayed. */  
  8.     boolean needsInput(); //View是否需要輸入數值,即該界面需要鍵盤輸入數值   
  9.     /** This screen is no longer in front of the user.*/  
  10.     void onPause();//當該界面不處於前臺界面時調用,包括處於GONE或者該界面即將被remove掉  
  11.     /** This screen is going to be in front of the user. */  
  12.     void onResume();//相對於onPause()方法,當該界面不處於前臺界面時調用,處於VISIBLE狀態時調用  
  13.     /** This view is going away; a hook to do cleanup. */  
  14.     void cleanUp();//該界面即將被remove掉 ,即不在需要  
  15. }  


   2、KeyguardScreenCallback類  接口


      功能:每個需要顯示的界面:LockScreen或者UnLockScreen都保存了該對象的唯一實例,用來向控制界面彙報情況。

      路徑:frameworks\base\policy\src\com\android\internal\policy\impl\KeyguardScreenCallback.java

       其源代碼釋義如下:

[java] view plaincopy
  1. /** Within a keyguard, there may be several screens that need a callback 
  2.  * to the host keyguard view. 
  3.  */   
  4. public interface KeyguardScreenCallback extends KeyguardViewCallback {  
  5.     /** Transition to the lock screen*/  
  6.     void goToLockScreen();  //當前界面跳轉爲LockScreen ,而不是UnLockScreen  
  7.     /** Transition to the unlock screen.*/  
  8.     void goToUnlockScreen();//LockScreen成功開鎖 ,是否需要顯示UnLockScreen,否則,直接開鎖成功。  
  9.     //忘記了開鎖圖案,即我們需要跳轉到Google 賬戶去開鎖。  
  10.     void forgotPattern(boolean isForgotten);  
  11.     boolean isSecure();//當前機器是否安全,例如:設置了圖案、密碼開鎖等     
  12.     //該函數還不太懂,可能是是否只需要驗證UnlockScreen界面,即可成功開鎖。  
  13.     boolean isVerifyUnlockOnly();  
  14.     /**Stay on me, but recreate me (so I can use a different layout).*/  
  15.     void recreateMe(Configuration config); //重新根據手機當前狀態,顯示對應的Screen.  
  16.     /** Take action to send an emergency call. */  
  17.     void takeEmergencyCallAction();  //緊急呼叫時的處理行爲.  
  18.   
  19.     /** Report that the user had a failed attempt to unlock with password or pattern.*/  
  20.     void reportFailedUnlockAttempt(); //在UnLockScreen界面登陸失敗時處理  
  21.   
  22.     /** Report that the user successfully entered their password or pattern.*/  
  23.     void reportSuccessfulUnlockAttempt();//在UnLockScreen界面登陸成功時處理  
  24.   
  25.     /** Report whether we there's another way to unlock the device. 
  26.      * @return true */  
  27.     boolean doesFallbackUnlockScreenExist();  
  28. }  

      其唯一實現類位於LockPatternKeyguardView類的內部類(稍後講到)。


   3、KeyguardViewCallback類  接口


         功能: 提供了一些接口用來接受用戶操作Screen的結果。

         路徑:frameworks\base\policy\src\com\android\internal\policy\impl\KeyguardViewCallback.java

         其源代碼釋義如下:

[java] view plaincopy
  1. /** 
  2.   * The callback used by the keyguard view to tell the {@link KeyguardViewMediator}  
  3.   * various things. 
  4.   */  
  5.  public interface KeyguardViewCallback {  
  6.   
  7.      /** Request the wakelock to be poked for the default amount of time. */  
  8.      void pokeWakelock();  //保存屏幕在一定時間內處於亮屏狀況 , 默認時間爲5s或者10s  
  9.      /** Request the wakelock to be poked for a specific amount of time.  */  
  10.      void pokeWakelock(int millis);//根據給定時間值,使屏幕在該事件段內保持亮屏狀況  
  11.   
  12.      /** Report that the keyguard is done. 
  13.       * @param authenticated Whether the user securely got past the keyguard. 
  14.       *   the only reason for this to be false is if the keyguard was instructed 
  15.       *   to appear temporarily to verify the user is supposed to get past the 
  16.       *   keyguard, and the user fails to do so. */  
  17.      //成功的完成開鎖,可以進入手機界面了。參數爲ture表示是否正大光明的開鎖,例如:圖案正確,密碼輸入正確。  
  18.      void keyguardDone(boolean authenticated);   
  19.      /**Report that the keyguard is done drawing. */  
  20.      void keyguardDoneDrawing(); //整個鎖屏界面draw()過程繪製完成時,回調該方法.  
  21.  }  


                      其唯一實現類是   KeyguardViewMediator類(稍後講到)


    4、 KeyguardWindowController類 接口


         功能:提供通用 接口,判斷該界面是否需要顯示輸入法窗口。

         其源代碼釋義如下:

[java] view plaincopy
  1. /** 
  2.  * Interface passed to the keyguard view, for it to call up to control 
  3.  * its containing window. 
  4.  */  
  5. public interface KeyguardWindowController {  
  6.     /** Control whether the window needs input -- that is if it has 
  7.      * text fields and thus should allow input method interaction. */  
  8.     void setNeedsInput(boolean needsInput);  //是否需要顯示輸入法,爲true表示需要。該方法可以想上層報到是否需要顯示輸入法窗口  
  9. }  

         其唯一實現類是KeyguardViewManager類(稍後講到)。

       

    5、KeyguardViewManager類 


        功能:包裝了WindowManager功能了,提供了添加、刪除鎖屏界面的功能。

          其源代碼釋義如下:     

[java] view plaincopy
  1.  public class KeyguardViewManager implements KeyguardWindowController {  
  2.      ...  
  3.      private WindowManager.LayoutParams mWindowLayoutParams;  
  4.      private boolean mNeedsInput = false//是否需要輸入法 , 默認不需要  
  5.   
  6.      private FrameLayout mKeyguardHost;   //該ViewGroup作爲頂層View,作爲WindowManager添加至窗口  
  7.      private KeyguardViewBase mKeyguardView; //具體窗口內容。  
  8.      //以上兩種的關係相當於DecorView和我們Activity內設置的資源文件一樣  
  9.   
  10.      private boolean mScreenOn = false//是否處於亮屏狀態  
  11.      //構造函數,初始化各種屬性  
  12.      public KeyguardViewManager(Context context, ViewManager viewManager,  
  13.              KeyguardViewCallback callback, KeyguardViewProperties keyguardViewProperties, KeyguardUpdateMonitor updateMonitor) {  
  14.         ...  
  15.      }  
  16.      /** 
  17.       * Helper class to host the keyguard view. 
  18.       */  
  19.      private static class KeyguardViewHost extends FrameLayout {  
  20.          ... //KeyguardViewHost類  
  21.      }  
  22.   
  23.      /** 
  24.       * Show the keyguard.  Will handle creating and attaching to the view manager 
  25.       * lazily. 
  26.       */  //顯示鎖屏界面  
  27.      public synchronized void show() {         
  28.          if (mKeyguardHost == null) {  
  29.              ...  
  30.              mViewManager.addView(mKeyguardHost, lp);  
  31.          }  
  32.          if (mKeyguardView == null) {  
  33.              ...  
  34.              mKeyguardHost.addView(mKeyguardView, lp);  
  35.              if (mScreenOn) {  
  36.                  mKeyguardView.onScreenTurnedOn();  
  37.              }  
  38.          }  
  39.         ...  
  40.      }  
  41.      ...  
  42.   
  43.      /*** Hides the keyguard view */  
  44.      public synchronized void hide() { //隱藏鎖屏界面,也就是說我們成功的解鎖了  
  45.          if (mKeyguardHost != null) {  
  46.              mKeyguardHost.setVisibility(View.GONE);  
  47.                  ...  
  48.              }  
  49.      }  
  50.      //鎖屏界面是否處於顯示狀態  
  51.      public synchronized boolean isShowing() {  
  52.          return (mKeyguardHost != null && mKeyguardHost.getVisibility() == View.VISIBLE);  
  53.      }  
  54.  }  
  55.   
  56.   
  57. }  
  58.         

   6、 KeyguardUpdateMonitor.java類 


       功能:該類的主要功能就是根據監視系統狀態值的改變(例如:時間、SIM卡狀態、電池電量;使用廣播監聽),根據這種狀態

           值的改變回調監聽了該狀態信息的對象實例。

       其源代碼釋義如下:     

[java] view plaincopy
  1. public class KeyguardUpdateMonitor {  
  2.     ...  
  3.     private int mFailedAttempts = 0;  //當前登錄事,已經失敗的次數  
  4.     private ArrayList<InfoCallback> mInfoCallbacks; //保存所有監聽對象 InfoCallback  
  5.     private ArrayList<SimStateCallback> mSimStateCallbacks ; //保存所有監聽對象  SimStateCallback  
  6.     private static class SimArgs {  //Sim狀態信息  
  7.       ...  
  8.     }  
  9.     /** 
  10.      * Callback for general information relevant to lock screen. 
  11.      */  
  12.     interface InfoCallback {  
  13.         //電池電量信息改變:參數含義分別如下:是否顯示電量信息  、 是否插入電影充電、 當前電池電量值  
  14.         void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel);  
  15.         void onTimeChanged(); //時間發生了改變  
  16.         //網絡運營商狀態發生了改變 ,例如從中國移動2G變爲中國移動3G,或者無服務等 ;  
  17.         void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn);   
  18.         /** Called when the ringer mode changes. */  
  19.         void onRingerModeChanged(int state);  
  20.         /** 電話狀態發生了改變  值可能爲:EXTRA_STATE_IDLE、EXTRA_STATE_RINGING、EXTRA_STATE_OFFHOOK*/  
  21.         void onPhoneStateChanged(String newState);  
  22.     }  
  23.     /** Callback to notify of sim state change. */  
  24.     interface SimStateCallback {  
  25.         void onSimStateChanged(IccCard.State simState); //Sim卡信息發生了改變,例如有正常狀況變爲ABSENT/MISSING狀態  
  26.     }  
  27.   
  28.     /*** Register to receive notifications about general keyguard information 
  29.      * (see {@link InfoCallback}. */  
  30.     public void registerInfoCallback(InfoCallback callback) {  
  31.         if (!mInfoCallbacks.contains(callback)) {  
  32.             mInfoCallbacks.add(callback);  //註冊一個監聽器  
  33.         } ...  
  34.     }  
  35.    ...  
  36.  }  



     7, LockPatternKeyguardView類  (自定義ViewGroup)

           功能:作爲LockScreen和UnLockScreen界面的載體,控制顯示哪個界面。

             其源代碼釋義如下:     

[java] view plaincopy
  1. public class LockPatternKeyguardView extends KeyguardViewBase {  
  2.     ...  
  3.     private View mLockScreen;  
  4.     private View mUnlockScreen;  
  5.   
  6.     private boolean mScreenOn = false;//是否亮屏  
  7.   
  8.     enum Mode {  
  9.         //當前顯示界面的Mode Lock 或者UnLock  
  10.     }  
  11.     enum UnlockMode {  
  12.         ...//開鎖界面的幾種不同Mode   
  13.     }  
  14.     //構造函數  
  15.     public LockPatternKeyguardView( ...) {       
  16.         //KeyguardScreenCallback的實現對象  
  17.         mKeyguardScreenCallback = new KeyguardScreenCallback() {  
  18.            ...  
  19.         };  
  20.         ...  
  21.     }  
  22.     public void reset() {  
  23.        ...//重置顯示界面  
  24.     }  
  25.     private void recreateLockScreen() {  
  26.     ...//重新構建LockScreen  
  27.     }  
  28.     private void recreateUnlockScreen() {  
  29.        ...//重新構建UnlockScreen  
  30.     }  
  31.     private void recreateScreens() {  
  32.        ...//重新構建該視圖  
  33.     }  
  34.     public void verifyUnlock() {  
  35.        ...   
  36.     }  
  37.     public void cleanUp() {  
  38.     ... //清理資源對象  
  39.     }  
  40.     private boolean isSecure() {  
  41.        ...//手機設置是否處於安全狀態  
  42.     }  
  43.     private void updateScreen(final Mode mode) {  
  44.        ...//根據參數(Lock/unLock),判斷顯示爲LockScreen或者UnlockScreen界面  
  45.     }  
  46.     View createLockScreen() {  
  47.     ...//創建lockScreen  
  48.     }  
  49.     View createUnlockScreenFor(UnlockMode unlockMode) {  
  50.        ...//根據不同的Unlock Mode , 創建不同的UnlockScreen  
  51.     }  
  52.     private Mode getInitialMode() {  
  53.        ...//得到初始化的狀態Mode (lock or unlock).  
  54.     }  
  55.     /** Given the current state of things, what should the unlock screen be? */  
  56.     private UnlockMode getUnlockMode() {  
  57.        ...//返回開鎖的狀態Unlock Mode  
  58.     }  
  59.     private void showTimeoutDialog() {  
  60.         ... //輸入密碼超過一定次數時,提示30s後在登錄的對話框  
  61.     }  
  62.     private void showAlmostAtAccountLoginDialog() {  
  63.        ... //顯示Google 賬戶登錄對話框  
  64.     }  
  65. }  



       8、KeyguardViewBase類 抽象類  (自定義ViewGroup)


            功能:爲LockPatternKeyguardView提供了一組通用的方法 。需要值得注意的方法就是他對某些KeyEvent的監聽,

      當他消費監聽到這些KeyEvent,我們的App就監聽不到這些KeyEvent了 。常用的有KEYEVENT_VOLUME_UP/DOWN等。

 

[java] view plaincopy
  1. public abstract class KeyguardViewBase extends FrameLayout {  
  2.      ...  
  3.   @Override  
  4.     public boolean dispatchKeyEvent(KeyEvent event) {  
  5.         ...  
  6.         if (interceptMediaKey(event)) {  
  7.             return true;  
  8.         }  
  9.         return super.dispatchKeyEvent(event);  
  10.     }  
  11.   
  12.     private boolean interceptMediaKey(KeyEvent event) {  
  13.         final int keyCode = event.getKeyCode();  
  14.         if (event.getAction() == KeyEvent.ACTION_DOWN) {  
  15.             switch (keyCode) {  
  16.                 ...//more keyevent  
  17.                 case KeyEvent.KEYCODE_VOLUME_UP:  
  18.                 case KeyEvent.KEYCODE_VOLUME_DOWN: {  
  19.                     ...  
  20.                     // Don't execute default volume behavior  
  21.                     return true//直接返回不在向下傳遞處理  
  22.                 }  
  23.             }  
  24.         }   
  25.         return false;  
  26.     }  
  27.  }  

   9、 KeyguardViewProperties.java 接口

                 功能:提供了創建界面的通用方法。

[java] view plaincopy
  1.   public interface KeyguardViewProperties {    
  2.     //創建一個KeyguardViewBase實例 , 實際是指LockPatternKeyguardView實例  
  3.     KeyguardViewBase createKeyguardView(Context context,  
  4.             KeyguardUpdateMonitor updateMonitor,  
  5.             KeyguardWindowController controller);  
  6.   
  7.     boolean isSecure();  
  8. }  
                

          其唯一實現類是是LockPatternKeyguardViewProperties類(稍後講到)。


  10、LockPatternKeyguardViewProperties類 

                源代碼釋義如下:

[java] view plaincopy
  1. public class LockPatternKeyguardViewProperties implements KeyguardViewProperties {  
  2.    ...  
  3.    //創建一個LockPatternKeyguardView對象  
  4.    public KeyguardViewBase createKeyguardView(Context context,  
  5.            KeyguardUpdateMonitor updateMonitor,  
  6.            KeyguardWindowController controller) {  
  7.        return new LockPatternKeyguardView(context, updateMonitor,  
  8.                mLockPatternUtils, controller);  
  9.    }  
  //=============================================

  //  OK ,我知道你看的很糾結了,具體需要時參考源代碼看是最明智的。

  //=============================================


    我知道代碼貼的太多了,沒辦法,誰讓它理解起來就那麼費勁呢 ? 你可別犯愁,真正核心的類可還沒出來。。


    12、KeyguardViewMediator核心類 ,該類是唯一實現了KeyguardViewCallback的類。

           功能:  功能:該類提供了一些接口,由PhoneWindowManager)去訪問控制Keyguard.... 

           該類的初始化是在PolicyWindowManager的構造函數中創建的。如下:

[java] view plaincopy
  1. public class PhoneWindowManager implements WindowManagerPolicy {  
  2.    ...  
  3.   /** {@inheritDoc} */  //由SystemServer調用  
  4.   public void init(Context context, IWindowManager windowManager,  
  5.           LocalPowerManager powerManager) {  
  6.       ...//初始化該實例  
  7.       mKeyguardMediator = new KeyguardViewMediator(context, this, powerManager);  
  8.   }  
  9.  }  
  10.    

       參照源代碼,把一些重要的屬性和方法的大意給分析下:
 

[java] view plaincopy
  1. public class KeyguardViewMediator implements KeyguardViewCallback, KeyguardUpdateMonitor.SimStateCallback {  
  2.     private boolean mSystemReady;  //啓動成功 由SystemServer調用  
  3.   
  4.     /**Used to keep the device awake while to ensure the keyguard finishes opening before 
  5.      * we sleep.*/ //在需要顯示鎖屏界面時,保持屏幕在某個時間段內爲暗屏狀態    
  6.     private PowerManager.WakeLock mShowKeyguardWakeLock;  
  7.     private KeyguardViewManager mKeyguardViewManager; //KeyguardViewManager實例  
  8.     /**  * External apps (like the phone app) can tell us to disable the keygaurd.*/  
  9.     //是否允許其他App禁止鎖屏 , 例如來電時 禁止鎖屏  
  10.     private boolean mExternallyEnabled = true;  
  11.     //處於鎖屏狀態 , 即顯示鎖屏  
  12.     private boolean mShowing = false;  
  13.     // true if the keyguard is hidden by another window  
  14.     private boolean mHidden = false//被其他窗口掩蓋 , 例如來電時鎖屏被掩蓋  
  15.     private boolean mScreenOn = false// 是否亮屏  
  16.   
  17.     public KeyguardViewMediator(Context context, PhoneWindowManager callback,  
  18.             LocalPowerManager powerManager) {  
  19.         ...  
  20.         //構造相關實例對象  
  21.         mKeyguardViewProperties = new LockPatternKeyguardViewProperties(  
  22.                 new LockPatternUtils(mContext), mUpdateMonitor);  
  23.   
  24.         mKeyguardViewManager = new KeyguardViewManager(  
  25.                 context, WindowManagerImpl.getDefault(), this,  
  26.                 mKeyguardViewProperties, mUpdateMonitor);  
  27.         //解鎖成功後發送的Intent  
  28.         mUserPresentIntent = new Intent(Intent.ACTION_USER_PRESENT);  
  29.         mUserPresentIntent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);  
  30.     }  
  31.     /** Called to let us know the screen was turned off. 
  32.      *   @param why either {@link WindowManagerPolicy#OFF_BECAUSE_OF_USER}, 
  33.      *   {@link WindowManagerPolicy#OFF_BECAUSE_OF_TIMEOUT} or 
  34.      *   {@link WindowManagerPolicy#OFF_BECAUSE_OF_PROX_SENSOR}. 
  35.      */  
  36.     //屏幕變灰暗  , 原因有如下:以及對應的邏輯處理。  
  37.     // 1、OFF_BECAUSE_OF_USER : 用戶按下POWER鍵 , 當前是否處於鎖屏界面,若是(mShowing)則重置顯示界面,否則重新顯示鎖屏界面  
  38.     // 2、OFF_BECAUSE_OF_TIMEOUT : 屏幕超時,常見情況就是一段時間沒有操作屏幕,手機處於灰暗狀態。          處理行爲:     
  39.     //     發送Action值爲DELAYED_KEYGUARD_ACTION的廣播,因爲該類註冊了該Intent廣播,接受到時會調用doKeyguard()方法鎖屏  
  40.     // 3、OFF_BECAUSE_OF_PROX_SENSOR:接打電話時,距離感應太近導致暗屏,此時由於PowerManager那兒已經處理了暗屏,不需要做任何事  
  41.     // 最後,如果以上邏輯都不成立,調用 doKeyguard()方法顯示屏幕                         
  42.     public void onScreenTurnedOff(int why) {  
  43.        ...  
  44.     }  
  45.     /** 
  46.      * Let's us know the screen was turned on. 
  47.      */  
  48.     public void onScreenTurnedOn() {  
  49.         synchronized (this) {  
  50.             ...  
  51.             notifyScreenOnLocked();  //通知亮屏  
  52.         }  
  53.     }  
  54.     /** Enable the keyguard if the settings are appropriate. */  
  55.     private void doKeyguard() {  
  56.         synchronized (this) {  
  57.             ...  
  58.             showLocked();//顯示鎖屏界面  
  59.         }  
  60.     }  
  61.     //該方法的調用時機就是當按下POWER鍵時,系統會回調該方法 keyCode值一般爲 26  
  62.     public boolean onWakeKeyWhenKeyguardShowingTq(int keyCode) {  
  63.         //操作按鍵是否能喚醒屏幕   
  64.         if (isWakeKeyWhenKeyguardShowing(keyCode)) {  
  65.             // give the keyguard view manager a chance to adjust the state of the  
  66.             // keyguard based on the key that woke the device before poking  
  67.             // the wake lock  
  68.             wakeWhenReadyLocked(keyCode);//開始喚醒屏幕咯  
  69.             return true;  
  70.         } else {  
  71.             return false;  
  72.         }  
  73.     }  
  74.     /** {@inheritDoc} */  //在一定時間內保存屏幕爲亮屏狀態  
  75.     public void pokeWakelock(int holdMs) {  
  76.         ...  
  77.     }  
  78.     //表示成功得分完成了解鎖操作  
  79.     public void keyguardDone(boolean authenticated, boolean wakeup) {  
  80.         synchronized (this) {  
  81.             //發送給Handler 進行異步處理  
  82.             Message msg = mHandler.obtainMessage(KEYGUARD_DONE);  
  83.             msg.arg1 = wakeup ? 1 : 0;  
  84.             mHandler.sendMessage(msg);  
  85.             if (authenticated) {  
  86.                 mUpdateMonitor.clearFailedAttempts(); //清除錯誤登錄次數  
  87.             }  
  88.             ...  
  89.         }  
  90.     }  
  91.     //Handler對象 , 異步處理  
  92.     private Handler mHandler = new Handler() {  
  93.         @Override  
  94.         public void handleMessage(Message msg) {  
  95.             switch (msg.what) {  
  96.               ...  //異步處理  
  97.             }  
  98.         }  
  99.     };  
  100.     //異步處理完成開鎖成功  
  101.     private void handleKeyguardDone(boolean wakeup) {   
  102.         handleHide(); //釋放該Keyguard對應的窗口  
  103.         mWakeLock.release();  
  104.         mContext.sendBroadcast(mUserPresentIntent); //解鎖成功,發送Intent信息  
  105.     }  
  106.     //顯示鎖屏界面  
  107.     private void handleShow() {  
  108.         synchronized (KeyguardViewMediator.this) {  
  109.             ...  
  110.             mKeyguardViewManager.show();  
  111.             mShowing = true;  
  112.             ...  
  113.         }  
  114.     }  
  115.     private void handleHide() {  
  116.         synchronized (KeyguardViewMediator.this) {  
  117.             //去除鎖屏界面對應的窗口  
  118.             mKeyguardViewManager.hide();  
  119.             mShowing = false;  
  120.            ...  
  121.         }  
  122.     }  
  123.     //設置狀態欄enable狀態 , 例如:能否被下拉等  
  124.     private void adjustStatusBarLocked() {  
  125.             ...  
  126.             // if the keyguard is shown, allow the status bar to open  
  127.             // only if the keyguard is insecure and is covered by another window  
  128.             boolean enable = !mShowing || (mHidden && !isSecure());  
  129.             mStatusBarManager.disable(enable ?StatusBarManager.DISABLE_NONE : StatusBarManager.DISABLE_EXPAND);  
  130.         }  
  131.     }  
  132. }  

 

     該類的很多方法都是由PhoneWindowManager調用訪問的。


         基本上把一些重要的類及其相關的方法給走了一遍吧,對看源碼的還是很有幫助的。因爲我的理解還不是很深入,可能有偏頗的

  地方。希望以後能夠組件完善起來。 阿門 !


  

  問題:如何在框架中, 解除鎖屏 ?


       引入:該問題是在CSDN論壇上回答一位網友引發的,最開始由於我也只是簡單看了下,因此也就事論事回答了一個小問題。

  隨着看的越來越深入,慢慢的也在心裏長生了漣漪。經過嘗試、燒雞驗證,發現如下辦法行的通,而且效果還比較好。風險應該

  比較小吧。 希望正在在框架修改的同學,慎重行事。


       基本思路:毫無疑問,我的想法就是每次需要顯示Keyguard---鎖屏界面時,我們並不真正的去鎖屏,而只是提供了一個空的

  方法去給系統調用,讓系統覺得我們“鎖屏”了,同樣也不去真正的隱藏“鎖屏”界面,提供一個空殼給系統調用。由於可能涉及

  到其它問題,例如:能否下拉狀態欄,按下POWER鍵後,屏幕很快休眠等。Come on ,我們需要統一做處理。


    所有步驟函數都發生在KeyguardViewMediator 類中,註釋部分爲我們所添加的。


      Step 1、  取消  真正的去鎖屏實現

[java] view plaincopy
  1. //該方法會顯示鎖屏界面,我們使其成爲一個空殼子  
  2. private void handleShow() {  
  3.     synchronized (KeyguardViewMediator.this) {  
  4.         if (DEBUG) Log.d(TAG, "handleShow");  
  5.         if (!mSystemReady) return;  
  6.   
  7.         playSounds(true);  
  8.         //Begin : Modifid by qinjuning       
  9.           
  10.         //mKeyguardViewManager.show();  //     
  11.         //mShowing = true;              //  
  12.         //adjustUserActivityLocked();   //  
  13.         //adjustStatusBarLocked();     //取消對狀態欄的控制  
  14.           
  15.         //End  
  16.         try {  
  17.             ActivityManagerNative.getDefault().closeSystemDialogs("lock");  
  18.         } catch (RemoteException e) {  
  19.         }  
  20.         mShowKeyguardWakeLock.release();  
  21.     }  
  22. }  

      Step 2、  取消  真正的去隱藏鎖屏實現       

[java] view plaincopy
  1. //真正的隱藏屏幕實現  
  2. private void handleHide() {  
  3.     synchronized (KeyguardViewMediator.this) {  
  4.         if (DEBUG) Log.d(TAG, "handleHide");  
  5.         if (mWakeAndHandOff.isHeld()) {  
  6.             Log.w(TAG, "attempt to hide the keyguard while waking, ignored");  
  7.             return;  
  8.         }  
  9.         // only play "unlock" noises if not on a call (since the incall UI  
  10.         // disables the keyguard)  
  11.         if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)) {  
  12.             playSounds(false);  
  13.         }  
  14.         //Begin : Modifid by qinjuning    
  15.           
  16.         //mKeyguardViewManager.hide();  
  17.         //mShowing = false;   
  18.         //adjustUserActivityLocked();         
  19.         //adjustStatusBarLocked();         //取消對狀態欄的控制  
  20.           
  21.         //End  
  22.     }  
  23. }  

    以上兩步行動後,存在一個Bug(問題),就是喚醒屏幕後,會在指定的時間內屏幕由亮變暗,我們還需要做如下修改

    

     Step 3、按下POWER鍵時, 解除屏幕由亮變暗的Bug

[java] view plaincopy
  1. private void handleWakeWhenReady(int keyCode) {  
  2.     synchronized (KeyguardViewMediator.this) {  
  3.         if (DBG_WAKE) Log.d(TAG, "handleWakeWhenReady(" + keyCode + ")");  
  4.   
  5.         // this should result in a call to 'poke wakelock' which will set a timeout  
  6.         // on releasing the wakelock  
  7.         if (!mKeyguardViewManager.wakeWhenReadyTq(keyCode)) {  
  8.             // poke wakelock ourselves if keyguard is no longer active  
  9.             Log.w(TAG, "mKeyguardViewManager.wakeWhenReadyTq did not poke wake lock, so poke it ourselves");  
  10.             //Begin : Modifid by qinjuning    
  11.             //pokeWakelock();  //按下POWER鍵時, 解除屏幕由亮變暗的Bug     
  12.             //End  
  13.         }  
  14.         /** 
  15.          * Now that the keyguard is ready and has poked the wake lock, we can 
  16.          * release the handoff wakelock 
  17.          */  
  18.         mWakeAndHandOff.release();  
  19.   
  20.         if (!mWakeLock.isHeld()) {  
  21.             Log.w(TAG, "mWakeLock not held in mKeyguardViewManager.wakeWhenReadyTq");  
  22.         }  
  23.     }  
  24. }  

  

       經過真機測試是通過的,但其他風險並不清楚。 這個方法只是提供了一個學習的途徑吧。大家慎重行事。

    上面Step 1、以及Step 2可以由如下方法代替:

           將屬性mExternallyEnabled 設置爲 false, 接下來需要顯示界面時都不會繼續走下去,如下函數:

[java] view plaincopy
  1. /** 
  2.  * Enable the keyguard if the settings are appropriate. 
  3.  */  //顯示界面  
  4. private void doKeyguard() {  
  5.     synchronized (this) {  
  6.         // if another app is disabling us, don't show  
  7.         if (!mExternallyEnabled) {   //mExternallyEnabled 爲false   
  8.             if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");  
  9.   
  10.             // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes  
  11.             // for an occasional ugly flicker in this situation:  
  12.             // 1) receive a call with the screen on (no keyguard) or make a call  
  13.             // 2) screen times out  
  14.             // 3) user hits key to turn screen back on  
  15.             // instead, we reenable the keyguard when we know the screen is off and the call  
  16.             // ends (see the broadcast receiver below)  
  17.             // TODO: clean this up when we have better support at the window manager level  
  18.             // for apps that wish to be on top of the keyguard  
  19.             return;  
  20.         }  
  21.         ...  
  22.     }  
  23. }  
 

      該方法的一個缺點就是,假如存在重新調用了setKeyguardEnabled()設置該值,一切都是白搭( 但從源碼看,這點不可能

 出現,因爲存在另一個判斷依據:變量mNeedToReshowWhenReenabled , 其初始值爲false,只有成功禁止鎖屏之後才置爲

 true )。 此,我們可以仿照這個方法,主動添加一個私有變量,禁止顯示鎖屏界面,即禁止doKeyguard()方法繼續走下去。

 

     OK ,本文到此爲止。講的比較抽象。 大家看代碼時多加理解纔是王道 。

 

     顯然易見,手機廠商,基於框架只需要修改LockScreen這個自定義ViewGroup即可,其他的一套Google已經爲我們

   封裝好了。


      在框架層修改肯定不是最好的,對於第三方的App而言,實現不了該功能。還好,SDK爲我們提供了接口類去處理隱藏鎖屏接口

的方法,該類是KeyguardManager類,關於該類的簡介請參考該博客:

                                  KeyguardManager簡介 》


     我們可以通過KeyguardManager類實例獲得一個KeyguardManager.KeyguardLock對象,進而調用相應方法去取消鎖屏界面

 和示鎖屏界面。

    KeyguardManager.KeyguardLock的兩個方法說明如下:


          public void disableKeyguard ()

            功能:取消鎖屏界面顯示,同時禁止顯示鎖屏界面。除非你顯示調用了reenableKeyguard()方法使能顯示鎖屏界面。

            public void reenableKeyguard ()

            功能: 使能顯示鎖屏界面,如果你之前調用了disableKeyguard()方法取消鎖屏界面,那麼會馬上顯示鎖屏界面。 


     這兩個方法最終都會調用到KeyguardViewMediator類的setKeyguardEnabled(boolean enable)方法。

            參數說明: enable = false  對應於disableKeyguard()方法,

                             enable = true  對應於reenableKeyguard()方法。

         

          該方法原型爲: 位於KeyguardViewMediator類中

[java] view plaincopy
  1. /** 
  2.  * Same semantics as {@link WindowManagerPolicy#enableKeyguard}; provide 
  3.  * a way for external stuff to override normal keyguard behavior.  For instance 
  4.  * the phone app disables the keyguard when it receives incoming calls. 
  5.  */   
  6. public void setKeyguardEnabled(boolean enabled) {  
  7.     synchronized (this) {  
  8.         mExternallyEnabled = enabled;  //保存值,該值會在doKeyguard()時用到,如果爲false ,則不進行鎖屏  
  9.   
  10.         if (!enabled && mShowing) {  
  11.             if (mExitSecureCallback != null) {//該判斷爲false  
  12.                 ...   
  13.                 return ;  
  14.             }  
  15.             mNeedToReshowWhenReenabled = true;  //置爲真,以便下次調用  
  16.             hideLocked(); //已經顯示了鎖屏界面,則取消隱藏界面  
  17.         } else if (enabled && mNeedToReshowWhenReenabled) { //重新顯示鎖屏界面  
  18.             mNeedToReshowWhenReenabled = false;  
  19.   
  20.             if (mExitSecureCallback != null) {//該判斷爲false  
  21.                 
  22.             } else {  
  23.                 showLocked(); //顯示隱藏界面  
  24.                 ...  
  25.             }  
  26.         }  
  27.     }  
  28. }  


       使用這兩個方法時,記得加上如下權限android.permission.DISABLE_KEYGUARD


   爲了在亮屏時,達到取消顯示界面的效果,我們還需要知道 一下兩個廣播:

         屏幕變暗以及屏幕點亮的廣播


                    android.intent.action.SCREEN_ON  --- 屏幕變亮

                    android.intent.action.SCREEN_OFF ---- 屏幕點暗


         關於這兩個廣播的說明請參考如下博客:http://www.2cto.com/kf/201111/109815.html

    

  於是在監聽到屏幕變暗/變亮時,通過KeyguardManager 類實現即可。對與用戶而言,就相當於解除了鎖屏界面了。

   可能代碼如下:

[java] view plaincopy
  1. //屏幕變暗/變亮的廣播 , 我們要調用KeyguardManager類相應方法去解除屏幕鎖定  
  2. private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver(){  
  3.     @Override  
  4.     public void onReceive(Context context , Intent intent) {  
  5.         String action = intent.getAction() ;  
  6.           
  7.         Log.i(TAG, intent.toString());  
  8.           
  9.         if(action.equals("android.intent.action.SCREEN_OFF")  
  10.                 || action.equals("android.intent.action.SCREEN_ON") ){  
  11.             mKeyguardManager = (KeyguardManager)context.getSystemService(Context.KEYGUARD_SERVICE);  
  12.             mKeyguardLock = mKeyguardManager.newKeyguardLock("zdLock 1");   
  13.             mKeyguardLock.disableKeyguard();  
  14.             startActivity(zdLockIntent);  
  15.         }  
  16.     }  
  17.       
  18. };  


       關於參考/設計一個好的解鎖界面以及仿正點鬧鐘滑動解鎖,請看我的這篇博客:

       《 Android自定義鎖屏實現----仿正點鬧鐘滑屏解鎖

 

        PS:如果覺得本文對你有幫助,請給頂一下。

    

 

       最後,可能有些同學在做App時,可能想獲取系統的登錄次數等,例如:登錄失敗次數等 ; Android系統已經 爲我們提供好

   了框架去處理,具體對應類是DevicePolicyManager類,關於該類的具體使用請參見該博客:

                 《【Android設備管理】 利用DevicePolicyManager執行屏幕鎖定 》 。


        我也不再羅嗦了 ,大家認真學習吧 。 後面我會仔細分析下鎖屏框架的一些具體處理函數 。


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