最近終於成功的擺脫了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
其源代碼釋義以下:
/** * 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
其源代碼釋義以下:
/** 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(); }
三、KeyguardViewCallback類 接口
功能: 提供了一些接口用來接受用戶操做Screen的結果。
路徑:frameworks\base\policy\src\com\android\internal\policy\impl\KeyguardViewCallback.java
其源代碼釋義以下:
/** * 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類 接口
功能:提供通用 接口,判斷該界面是否須要顯示輸入法窗口。
其源代碼釋義以下:
/** * 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功能了,提供了添加、刪除鎖屏界面的功能。
其源代碼釋義以下:
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卡狀態、電池電量;使用廣播監聽),根據這種狀態
值的改變回調監聽了該狀態信息的對象實例。
其源代碼釋義以下:
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); //註冊一個監聽器 } ... } ... }
功能:做爲LockScreen和UnLockScreen界面的載體,控制顯示哪一個界面。
其源代碼釋義以下:
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等。
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 接口
功能:提供了建立界面的通用方法。
public interface KeyguardViewProperties { //建立一個KeyguardViewBase實例 , 實際是指LockPatternKeyguardView實例 KeyguardViewBase createKeyguardView(Context context, KeyguardUpdateMonitor updateMonitor, KeyguardWindowController controller); boolean isSecure(); }
其惟一實現類是是LockPatternKeyguardViewProperties類(稍後講到)。
十、LockPatternKeyguardViewProperties類
源代碼釋義以下:
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的構造函數中建立的。以下:
public class PhoneWindowManager implements WindowManagerPolicy { ... /** {@inheritDoc} */ //由SystemServer調用 public void init(Context context, IWindowManager windowManager, LocalPowerManager powerManager) { ...//初始化該實例 mKeyguardMediator = new KeyguardViewMediator(context, this, powerManager); } }
參照源代碼,把一些重要的屬性和方法的大意給分析下:
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 一、 取消 真正的去鎖屏實現
//該方法會顯示鎖屏界面,咱們使其成爲一個空殼子 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 二、 取消 真正的去隱藏鎖屏實現
//真正的隱藏屏幕實現 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
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, 接下來須要顯示界面時都不會繼續走下去,以下函數:
/** * 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.KeyguardLock對象,進而調用相應方法去取消鎖屏界面
和顯示鎖屏界面。
KeyguardManager.KeyguardLock的兩個方法說明以下:
public void disableKeyguard ()
功能:取消鎖屏界面顯示,同時禁止顯示鎖屏界面。除非你顯示調用了reenableKeyguard()方法使能顯示鎖屏界面。
功能: 使能顯示鎖屏界面,若是你以前調用了disableKeyguard()方法取消鎖屏界面,那麼會立刻顯示鎖屏界面。
這兩個方法最終都會調用到KeyguardViewMediator類的setKeyguardEnabled(boolean enable)方法。
參數說明: enable = false 對應於disableKeyguard()方法,
enable = true 對應於reenableKeyguard()方法。
該方法原型爲: 位於KeyguardViewMediator類中
/** * 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 類實現便可。對與用戶而言,就至關於解除了鎖屏界面了。
可能代碼以下:
//屏幕變暗/變亮的廣播 , 咱們要調用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執行屏幕鎖定 》 。
我也再也不羅嗦了 ,你們認真學習吧 。 後面我會仔細分析下鎖屏框架的一些具體處理函數 。