最近终于成功的摆脱了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执行屏幕锁定 》 。
我也再也不罗嗦了 ,你们认真学习吧 。 后面我会仔细分析下锁屏框架的一些具体处理函数 。