Android框架淺析之鎖屏(Keyguard)機制原理


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

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

本文主要內容: android


一、分析鎖屏界面的組成 ; 編程

二、基於源代碼分析鎖屏相關類 ; 安全

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

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

理論學習的情況,不少細節以及功能上的實現有待後續的補充完整。 框架

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

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


源文件路徑主要有兩個:

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

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


1、鎖屏界面的組成


一般 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界面。

2、鎖屏界面的實現


咱們知道, 任何一種界面都是由各類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內設置的資源佈局同樣。


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


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

亂了。接下來咱們分析下一些主要的類及其重要的函數,更多函數實現,你們能夠本身參考源代碼。


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


一、 KeyguardScreen 類 接口


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

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

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

其源代碼釋義以下:

  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. }
/**
 * Common interface of each {@link android.view.View} that is a screen of
 * {@link LockPatternKeyguardView}.
 */ 
public interface KeyguardScreen {
    /** Return true if your view needs input, so should allow the soft
     * keyboard to be displayed. */
    boolean needsInput(); //View是否須要輸入數值,即該界面須要鍵盤輸入數值 
    /** This screen is no longer in front of the user.*/
    void onPause();//當該界面不處於前臺界面時調用,包括處於GONE或者該界面即將被remove掉
    /** This screen is going to be in front of the user. */
    void onResume();//相對於onPause()方法,當該界面不處於前臺界面時調用,處於VISIBLE狀態時調用
    /** This view is going away; a hook to do cleanup. */
    void cleanUp();//該界面即將被remove掉 ,即不在須要
}


二、KeyguardScreenCallback類 接口


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

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

其源代碼釋義以下:

  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. /** Report that the user had a failed attempt to unlock with password or pattern.*/
  19. void reportFailedUnlockAttempt(); //在UnLockScreen界面登錄失敗時處理
  20. /** Report that the user successfully entered their password or pattern.*/
  21. void reportSuccessfulUnlockAttempt();//在UnLockScreen界面登錄成功時處理
  22. /** Report whether we there's another way to unlock the device.
  23. * @return true */
  24. boolean doesFallbackUnlockScreenExist();
  25. }
/** Within a keyguard, there may be several screens that need a callback
 * to the host keyguard view.
 */ 
public interface KeyguardScreenCallback extends KeyguardViewCallback {
    /** Transition to the lock screen*/
    void goToLockScreen();  //當前界面跳轉爲LockScreen ,而不是UnLockScreen
    /** Transition to the unlock screen.*/
    void goToUnlockScreen();//LockScreen成功開鎖 ,是否須要顯示UnLockScreen,不然,直接開鎖成功。
    //忘記了開鎖圖案,即咱們須要跳轉到Google 帳戶去開鎖。
    void forgotPattern(boolean isForgotten);
    boolean isSecure();//當前機器是否安全,例如:設置了圖案、密碼開鎖等   
    //該函數還不太懂,多是是否只須要驗證UnlockScreen界面,便可成功開鎖。
    boolean isVerifyUnlockOnly();
    /**Stay on me, but recreate me (so I can use a different layout).*/
    void recreateMe(Configuration config); //從新根據手機當前狀態,顯示對應的Screen.
    /** Take action to send an emergency call. */
    void takeEmergencyCallAction();  //緊急呼叫時的處理行爲.

    /** Report that the user had a failed attempt to unlock with password or pattern.*/
    void reportFailedUnlockAttempt(); //在UnLockScreen界面登錄失敗時處理

    /** Report that the user successfully entered their password or pattern.*/
    void reportSuccessfulUnlockAttempt();//在UnLockScreen界面登錄成功時處理

    /** Report whether we there's another way to unlock the device.
     * @return true */
    boolean doesFallbackUnlockScreenExist();
}

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


三、KeyguardViewCallback類 接口


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

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

其源代碼釋義以下:

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

     /** Request the wakelock to be poked for the default amount of time. */
     void pokeWakelock();  //保存屏幕在必定時間內處於亮屏情況 , 默認時間爲5s或者10s
     /** Request the wakelock to be poked for a specific amount of time.  */
     void pokeWakelock(int millis);//根據給定時間值,使屏幕在該事件段內保持亮屏情況

     /** Report that the keyguard is done.
      * @param authenticated Whether the user securely got past the keyguard.
      *   the only reason for this to be false is if the keyguard was instructed
      *   to appear temporarily to verify the user is supposed to get past the
      *   keyguard, and the user fails to do so. */
     //成功的完成開鎖,能夠進入手機界面了。參數爲ture表示是否正大光明的開鎖,例如:圖案正確,密碼輸入正確。
     void keyguardDone(boolean authenticated); 
     /**Report that the keyguard is done drawing. */
     void keyguardDoneDrawing(); //整個鎖屏界面draw()過程繪製完成時,回調該方法.
 }


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


四、 KeyguardWindowController類 接口


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

其源代碼釋義以下:

  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. }
/**
 * Interface passed to the keyguard view, for it to call up to control
 * its containing window.
 */
public interface KeyguardWindowController {
    /** Control whether the window needs input -- that is if it has
     * text fields and thus should allow input method interaction. */
    void setNeedsInput(boolean needsInput);  //是否須要顯示輸入法,爲true表示須要。該方法能夠想上層報到是否須要顯示輸入法窗口
}

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

五、KeyguardViewManager


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

其源代碼釋義以下:

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

     private FrameLayout mKeyguardHost;   //該ViewGroup做爲頂層View,做爲WindowManager添加至窗口
     private KeyguardViewBase mKeyguardView; //具體窗口內容。
     //以上兩種的關係至關於DecorView和咱們Activity內設置的資源文件同樣

     private boolean mScreenOn = false; //是否處於亮屏狀態
     //構造函數,初始化各類屬性
     public KeyguardViewManager(Context context, ViewManager viewManager,
             KeyguardViewCallback callback, KeyguardViewProperties keyguardViewProperties, KeyguardUpdateMonitor updateMonitor) {
        ...
     }
     /**
      * Helper class to host the keyguard view.
      */
     private static class KeyguardViewHost extends FrameLayout {
         ... //KeyguardViewHost類
     }

     /**
      * Show the keyguard.  Will handle creating and attaching to the view manager
      * lazily.
      */  //顯示鎖屏界面
     public synchronized void show() {       
         if (mKeyguardHost == null) {
             ...
             mViewManager.addView(mKeyguardHost, lp);
         }
         if (mKeyguardView == null) {
             ...
             mKeyguardHost.addView(mKeyguardView, lp);
             if (mScreenOn) {
                 mKeyguardView.onScreenTurnedOn();
             }
         }
        ...
     }
     ...

     /*** Hides the keyguard view */
     public synchronized void hide() { //隱藏鎖屏界面,也就是說咱們成功的解鎖了
         if (mKeyguardHost != null) {
             mKeyguardHost.setVisibility(View.GONE);
                 ...
             }
     }
     //鎖屏界面是否處於顯示狀態
     public synchronized boolean isShowing() {
         return (mKeyguardHost != null && mKeyguardHost.getVisibility() == View.VISIBLE);
     }
 }


}

六、 KeyguardUpdateMonitor.java類


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

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

其源代碼釋義以下:

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

    /*** Register to receive notifications about general keyguard information
     * (see {@link InfoCallback}. */
    public void registerInfoCallback(InfoCallback callback) {
        if (!mInfoCallbacks.contains(callback)) {
            mInfoCallbacks.add(callback);  //註冊一個監聽器
        } ...
    }
   ...
 }



7, LockPatternKeyguardView類 (自定義ViewGroup)

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

其源代碼釋義以下:

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

     private boolean mScreenOn = false;//是否亮屏

     enum Mode {
         //當前顯示界面的Mode Lock 或者UnLock
     }
     enum UnlockMode {
         ...//開鎖界面的幾種不一樣Mode 
     }
     //構造函數
     public LockPatternKeyguardView( ...) {     
         //KeyguardScreenCallback的實現對象
         mKeyguardScreenCallback = new KeyguardScreenCallback() {
            ...
         };
         ...
     }
     public void reset() {
        ...//重置顯示界面
     }
     private void recreateLockScreen() {
    	...//從新構建LockScreen
     }
     private void recreateUnlockScreen() {
        ...//從新構建UnlockScreen
     }
     private void recreateScreens() {
        ...//從新構建該視圖
     }
     public void verifyUnlock() {
        ... 
     }
     public void cleanUp() {
    	... //清理資源對象
     }
     private boolean isSecure() {
        ...//手機設置是否處於安全狀態
     }
     private void updateScreen(final Mode mode) {
        ...//根據參數(Lock/unLock),判斷顯示爲LockScreen或者UnlockScreen界面
     }
     View createLockScreen() {
    	...//建立lockScreen
     }
     View createUnlockScreenFor(UnlockMode unlockMode) {
        ...//根據不一樣的Unlock Mode , 建立不一樣的UnlockScreen
     }
     private Mode getInitialMode() {
        ...//獲得初始化的狀態Mode (lock or unlock).
     }
     /** Given the current state of things, what should the unlock screen be? */
     private UnlockMode getUnlockMode() {
        ...//返回開鎖的狀態Unlock Mode
     }
     private void showTimeoutDialog() {
         ... //輸入密碼超過必定次數時,提示30s後在登陸的對話框
     }
     private void showAlmostAtAccountLoginDialog() {
        ... //顯示Google 帳戶登陸對話框
     }
 }



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


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

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

  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. private boolean interceptMediaKey(KeyEvent event) {
  12. final int keyCode = event.getKeyCode();
  13. if (event.getAction() == KeyEvent.ACTION_DOWN) {
  14. switch (keyCode) {
  15. ...//more keyevent
  16. case KeyEvent.KEYCODE_VOLUME_UP:
  17. case KeyEvent.KEYCODE_VOLUME_DOWN: {
  18. ...
  19. // Don't execute default volume behavior
  20. return true; //直接返回不在向下傳遞處理
  21. }
  22. }
  23. }
  24. return false;
  25. }
  26. }
public abstract class KeyguardViewBase extends FrameLayout {
	 ...
  @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        ...
        if (interceptMediaKey(event)) {
            return true;
        }
        return super.dispatchKeyEvent(event);
    }

    private boolean interceptMediaKey(KeyEvent event) {
        final int keyCode = event.getKeyCode();
        if (event.getAction() == KeyEvent.ACTION_DOWN) {
            switch (keyCode) {
                ...//more keyevent
                case KeyEvent.KEYCODE_VOLUME_UP:
                case KeyEvent.KEYCODE_VOLUME_DOWN: {
                    ...
                    // Don't execute default volume behavior
                    return true; //直接返回不在向下傳遞處理
                }
            }
        } 
        return false;
    }
 }

九、 KeyguardViewProperties.java 接口

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

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

	    boolean isSecure();
	}

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


十、LockPatternKeyguardViewProperties

源代碼釋義以下:

  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. }
public class LockPatternKeyguardViewProperties implements KeyguardViewProperties {
    ...
    //建立一個LockPatternKeyguardView對象
    public KeyguardViewBase createKeyguardView(Context context,
            KeyguardUpdateMonitor updateMonitor,
            KeyguardWindowController controller) {
        return new LockPatternKeyguardView(context, updateMonitor,
                mLockPatternUtils, controller);
    }
}
//=============================================

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

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


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


十二、KeyguardViewMediator核心類 ,該類是惟一實現了KeyguardViewCallback的類。

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

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

  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. }
public class PhoneWindowManager implements WindowManagerPolicy {
	   ...
	  /** {@inheritDoc} */  //由SystemServer調用
	  public void init(Context context, IWindowManager windowManager,
	          LocalPowerManager powerManager) {
	      ...//初始化該實例
	      mKeyguardMediator = new KeyguardViewMediator(context, this, powerManager);
	  }
  }

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

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

    /**Used to keep the device awake while to ensure the keyguard finishes opening before
     * we sleep.*/ //在須要顯示鎖屏界面時,保持屏幕在某個時間段內爲暗屏狀態  
    private PowerManager.WakeLock mShowKeyguardWakeLock;
    private KeyguardViewManager mKeyguardViewManager; //KeyguardViewManager實例
    /**  * External apps (like the phone app) can tell us to disable the keygaurd.*/
    //是否容許其餘App禁止鎖屏 , 例如來電時 禁止鎖屏
    private boolean mExternallyEnabled = true;
    //處於鎖屏狀態 , 即顯示鎖屏
    private boolean mShowing = false;
    // true if the keyguard is hidden by another window
    private boolean mHidden = false; //被其餘窗口掩蓋 , 例如來電時鎖屏被掩蓋
    private boolean mScreenOn = false; // 是否亮屏

    public KeyguardViewMediator(Context context, PhoneWindowManager callback,
            LocalPowerManager powerManager) {
        ...
        //構造相關實例對象
        mKeyguardViewProperties = new LockPatternKeyguardViewProperties(
                new LockPatternUtils(mContext), mUpdateMonitor);

        mKeyguardViewManager = new KeyguardViewManager(
                context, WindowManagerImpl.getDefault(), this,
                mKeyguardViewProperties, mUpdateMonitor);
        //解鎖成功後發送的Intent
        mUserPresentIntent = new Intent(Intent.ACTION_USER_PRESENT);
        mUserPresentIntent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
    }
    /** Called to let us know the screen was turned off.
     *   @param why either {@link WindowManagerPolicy#OFF_BECAUSE_OF_USER},
     *   {@link WindowManagerPolicy#OFF_BECAUSE_OF_TIMEOUT} or
     *   {@link WindowManagerPolicy#OFF_BECAUSE_OF_PROX_SENSOR}.
     */
    //屏幕變灰暗  , 緣由有以下:以及對應的邏輯處理。
    // 一、OFF_BECAUSE_OF_USER : 用戶按下POWER鍵 , 當前是否處於鎖屏界面,如果(mShowing)則重置顯示界面,不然從新顯示鎖屏界面
    // 二、OFF_BECAUSE_OF_TIMEOUT : 屏幕超時,常見狀況就是一段時間沒有操做屏幕,手機處於灰暗狀態。          處理行爲:   
    //     發送Action值爲DELAYED_KEYGUARD_ACTION的廣播,由於該類註冊了該Intent廣播,接受到時會調用doKeyguard()方法鎖屏
    // 三、OFF_BECAUSE_OF_PROX_SENSOR:接打電話時,距離感應太近致使暗屏,此時因爲PowerManager那兒已經處理了暗屏,不須要作任何事
    // 最後,若是以上邏輯都不成立,調用 doKeyguard()方法顯示屏幕                       
    public void onScreenTurnedOff(int why) {
       ...
    }
    /**
     * Let's us know the screen was turned on.
     */
    public void onScreenTurnedOn() {
        synchronized (this) {
            ...
            notifyScreenOnLocked();  //通知亮屏
        }
    }
    /** Enable the keyguard if the settings are appropriate. */
    private void doKeyguard() {
        synchronized (this) {
            ...
            showLocked();//顯示鎖屏界面
        }
    }
    //該方法的調用時機就是當按下POWER鍵時,系統會回調該方法 keyCode值通常爲 26
    public boolean onWakeKeyWhenKeyguardShowingTq(int keyCode) {
        //操做按鍵是否能喚醒屏幕 
        if (isWakeKeyWhenKeyguardShowing(keyCode)) {
            // give the keyguard view manager a chance to adjust the state of the
            // keyguard based on the key that woke the device before poking
            // the wake lock
            wakeWhenReadyLocked(keyCode);//開始喚醒屏幕咯
            return true;
        } else {
            return false;
        }
    }
    /** {@inheritDoc} */  //在必定時間內保存屏幕爲亮屏狀態
    public void pokeWakelock(int holdMs) {
        ...
    }
    //表示成功得分完成了解鎖操做
    public void keyguardDone(boolean authenticated, boolean wakeup) {
        synchronized (this) {
            //發送給Handler 進行異步處理
            Message msg = mHandler.obtainMessage(KEYGUARD_DONE);
            msg.arg1 = wakeup ? 1 : 0;
            mHandler.sendMessage(msg);
            if (authenticated) {
                mUpdateMonitor.clearFailedAttempts(); //清除錯誤登陸次數
            }
            ...
        }
    }
    //Handler對象 , 異步處理
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
              ...  //異步處理
            }
        }
    };
    //異步處理完成開鎖成功
    private void handleKeyguardDone(boolean wakeup) { 
        handleHide(); //釋放該Keyguard對應的窗口
        mWakeLock.release();
        mContext.sendBroadcast(mUserPresentIntent); //解鎖成功,發送Intent信息
    }
    //顯示鎖屏界面
    private void handleShow() {
        synchronized (KeyguardViewMediator.this) {
            ...
            mKeyguardViewManager.show();
            mShowing = true;
            ...
        }
    }
    private void handleHide() {
        synchronized (KeyguardViewMediator.this) {
            //去除鎖屏界面對應的窗口
            mKeyguardViewManager.hide();
            mShowing = false;
           ...
        }
    }
    //設置狀態欄enable狀態 , 例如:可否被下拉等
    private void adjustStatusBarLocked() {
            ...
            // if the keyguard is shown, allow the status bar to open
            // only if the keyguard is insecure and is covered by another window
            boolean enable = !mShowing || (mHidden && !isSecure());
            mStatusBarManager.disable(enable ?StatusBarManager.DISABLE_NONE : StatusBarManager.DISABLE_EXPAND);
        }
    }
}

該類的不少方法都是由PhoneWindowManager調用訪問的。


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

地方。但願之後可以組件完善起來。 阿門 !


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


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

隨着看的愈來愈深刻,慢慢的也在內心長生了漣漪。通過嘗試、燒雞驗證,發現以下辦法行的通,並且效果還比較好。風險應該

比較小吧。 但願正在在框架修改的同窗,慎重行事。


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

方法去給系統調用,讓系統以爲咱們「鎖屏」了,一樣也不去真正的隱藏「鎖屏」界面,提供一個空殼給系統調用。因爲可能涉及

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


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


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

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

        playSounds(true);
        //Begin : Modifid by qinjuning     
        
        //mKeyguardViewManager.show();  //   
        //mShowing = true;              //
        //adjustUserActivityLocked();   //
        //adjustStatusBarLocked();     //取消對狀態欄的控制
        
        //End
        try {
            ActivityManagerNative.getDefault().closeSystemDialogs("lock");
        } catch (RemoteException e) {
        }
        mShowKeyguardWakeLock.release();
    }
}

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

  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. //mKeyguardViewManager.hide();
  16. //mShowing = false;
  17. //adjustUserActivityLocked();
  18. //adjustStatusBarLocked(); //取消對狀態欄的控制
  19. //End
  20. }
  21. }
//真正的隱藏屏幕實現
private void handleHide() {
    synchronized (KeyguardViewMediator.this) {
        if (DEBUG) Log.d(TAG, "handleHide");
        if (mWakeAndHandOff.isHeld()) {
            Log.w(TAG, "attempt to hide the keyguard while waking, ignored");
            return;
        }
        // only play "unlock" noises if not on a call (since the incall UI
        // disables the keyguard)
        if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)) {
            playSounds(false);
        }
        //Begin : Modifid by qinjuning  
        
        //mKeyguardViewManager.hide();
        //mShowing = false; 
        //adjustUserActivityLocked();       
        //adjustStatusBarLocked();         //取消對狀態欄的控制
        
        //End
    }
}

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

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

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

        // this should result in a call to 'poke wakelock' which will set a timeout
        // on releasing the wakelock
        if (!mKeyguardViewManager.wakeWhenReadyTq(keyCode)) {
            // poke wakelock ourselves if keyguard is no longer active
            Log.w(TAG, "mKeyguardViewManager.wakeWhenReadyTq did not poke wake lock, so poke it ourselves");
            //Begin : Modifid by qinjuning  
            //pokeWakelock();  //按下POWER鍵時, 解除屏幕由亮變暗的Bug   
            //End
        }
        /**
         * Now that the keyguard is ready and has poked the wake lock, we can
         * release the handoff wakelock
         */
        mWakeAndHandOff.release();

        if (!mWakeLock.isHeld()) {
            Log.w(TAG, "mWakeLock not held in mKeyguardViewManager.wakeWhenReadyTq");
        }
    }
}

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

上面Step 一、以及Step 2能夠由以下方法代替:

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

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

            // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes
            // for an occasional ugly flicker in this situation:
            // 1) receive a call with the screen on (no keyguard) or make a call
            // 2) screen times out
            // 3) user hits key to turn screen back on
            // instead, we reenable the keyguard when we know the screen is off and the call
            // ends (see the broadcast receiver below)
            // TODO: clean this up when we have better support at the window manager level
            // for apps that wish to be on top of the keyguard
            return;
        }
        ...
    }
}

該方法的一個缺點就是,假如存在從新調用了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類中

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

        if (!enabled && mShowing) {
            if (mExitSecureCallback != null) {//該判斷爲false
                ... 
                return ;
            }
            mNeedToReshowWhenReenabled = true;  //置爲真,以便下次調用
            hideLocked(); //已經顯示了鎖屏界面,則取消隱藏界面
        } else if (enabled && mNeedToReshowWhenReenabled) { //從新顯示鎖屏界面
            mNeedToReshowWhenReenabled = false;

            if (mExitSecureCallback != null) {//該判斷爲false
              
            } else {
                showLocked(); //顯示隱藏界面
                ...
            }
        }
    }
}


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


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

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


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

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


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

因而在監聽到屏幕變暗/變亮時,經過KeyguardManager 類實現便可。對與用戶而言,就至關於解除了鎖屏界面了。

可能代碼以下:

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


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

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

PS:若是以爲本文對你有幫助,請給頂一下。

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

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

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


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

相關文章
相關標籤/搜索