本篇文章接着上一篇,上一篇主要分析了滑动锁屏加载流程,这篇我们分析密码锁屏也叫Bouncer的加载,看看系统如何加载不同密码锁屏以及如何动态添加的
我们知道密码锁屏有三种,pin/password/pattern,还有两种SIM卡的bouncer,SIM PIN/SIM PUK,要显示密码锁屏,首先需要上滑滑动锁屏,我们就以上滑滑动锁屏为入口分析密码锁屏的出现
通过上一篇文章我们知道滑动锁屏是一个id为notification_panel的自定义ViewGroup,对应类NotificationPanelView,继承PanelView,我们需要找到滑动锁屏的TouchEvent事件分发,当用户在滑动锁屏上滑的速度和距离满足一定条件之后,滑动锁屏就会解锁,这时候如果有密码锁屏就会显示,没有就直接解锁,我们首先来看看PanelView的isFalseTouch方法,这个方法定义了当前是否为误触的条件,isFalseTouch返回true代表是误触
private boolean isFalseTouch(float x, float y) {
if (!mStatusBar.isFalsingThresholdNeeded()) {
return false; } if (mFalsingManager.isClassiferEnabled()) {
return mFalsingManager.isFalseTouch(); } if (!mTouchAboveFalsingThreshold) {
return true; } if (mUpwardsWhenTresholdReached) {
return false; } return !isDirectionUpwards(x, y); }
讯享网
此方法有几个判断条件:
- isFalsingThresholdNeeded方法,此方法定义在StatusBar中,代码很简单"mStatusBarStateController.getState() == StatusBarState.KEYGUARD",返回当前是否是锁屏状态,肯定为true的
- 通过一系列算法来判断的,结合了P-sersor,也就是近距离传感器,
实际应用就是口袋模式防误触,当手机顶部sersor被遮挡了,那不管怎么滑都会被判定为误触 - mTouchAboveFalsingThreshold这个条件判定的就是滑动阙值,后面onTouchEvent方法中再分析
- mUpwardsWhenTresholdReached判定的是滑动方向是否向上且与水平方向成45度角以上,如果是则mUpwardsWhenTresholdReached为true
- !isDirectionUpwards(x, y),其实mUpwardsWhenTresholdReached的值就是调用isDirectionUpwards的返回值,!isDirectionUpwards == !mUpwardsWhenTresholdReached
锁屏误触的条件我们已经清楚了,我们来看PanelView的onTouchEvent方法,主要看Move事件就行了
PanelView.onTouchEvent
讯享网 @Override public boolean onTouchEvent(MotionEvent event) {
... switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN: ... break; case MotionEvent.ACTION_MOVE: //滑动Y轴的距离 float h = y - mInitialTouchY; if (Math.abs(h) > mTouchSlop && (Math.abs(h) > Math.abs(x - mInitialTouchX) || mIgnoreXTouchSlop)) {
mTouchSlopExceeded = true; ... //设置mTracking为true,代表滑动锁屏开始... onTrackingStarted(); } ... //阙值判断,getFalsingThreshold获取 //R.dimen.unlock_falsing_threshold(80dp)的值,然后 //乘上一个系数,1或者1.5 if (-h >= getFalsingThreshold()) {
//阙值满足之后将mTouchAboveFalsingThreshold置为true mTouchAboveFalsingThreshold = true; //并且获取滑动方向和角度 mUpwardsWhenTresholdReached = isDirectionUpwards(x, y); } if (!mJustPeeked && (!mGestureWaitForTouchSlop || mTracking) && !isTrackingBlocked()) {
//这里会传递一个newHeight最终给到NotificationPanelView //根据这个值,锁屏上的时钟,通知等透明度会变化 setExpandedHeightInternal(newHeight); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: addMovement(event); endMotionEvent(event, x, y, false /* forceCancel */); break; } }
当再次满足一个阙值getFalsingThreshold()之后会将mTouchAboveFalsingThreshold置为true,并且得到当前滑动的方向和角度是否满足要求,这两个条件就是我们前面分析isFalseTouch中误触的条件
通过setExpandedHeightInternal,调用NotificationPanelView中的onHeightUpdated方法根据传递的newHeight的值更新锁屏上时钟,通知等的透明度,newHeight这个值开始为手机屏幕高度,随着手指上滑逐渐变小,最终变为0代表滑动锁屏完全退出
Move事件结束后在UP事件中调用了endMotionEvent方法
endMotionEvent
private void endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel) {
...... //expand这个值是解锁的关键,false代表成功,true代表失败 boolean expand = flingExpands(vel, vectorVel, x, y) || event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel; ...... fling(vel, expand, isFalseTouch(x, y)); onTrackingStopped(expand); ... } else if (mPanelClosedOnDown && !mHeadsUpManager.hasPinnedHeadsUp() && !mTracking && !mStatusBar.isBouncerShowing() && !mKeyguardMonitor.isKeyguardFadingAway()) {
... } } else if (!mStatusBar.isBouncerShowing()) {
... } }
先看看给expand赋值方法flingExpands
flingExpands
讯享网protected boolean flingExpands(float vel, float vectorVel, float x, float y) {
//如果解锁被禁用则返回true,这里不太清楚如何禁用 if (mFalsingManager.isUnlockingDisabled()) {
return true; } //是否为误触 if (isFalseTouch(x, y)) {
return true; } //对滑动速度的判断 if (Math.abs(vectorVel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
return getExpandedFraction() > 0.5f; } else {
return vel > 0; } }
flingExpands主要对是否误触和滑动速度的判断,即使通过了误触检测,还会检测滑动的速度,是否误触我们前面已经分析过,这里的速度我自己测试了下,稍微快点的速度都能满足条件,所以flingExpands的返回结果就决定了是否能够滑动解锁,expand等于true代表为误触,为false代表成功滑动解锁
到这里我们可以总结滑动解锁的检测条件了:(1)对P-senor的近距离检测,(2)对滑动的阙值检测,(3)对滑动的方向和角度检测,(4)对滑动速度的检测
fling这个方法主要是对滑动锁屏的一些动画设置和处理,就不具体看了,我们接着分析onTrackingStopped方法,此方法代表手指滑动锁屏结束
因为子类NotificationPanelView覆盖了此方法,所以会调用NotificationPanelView的onTrackingStopped方法
NotificationPanelView.onTrackingStopped
@Override protected void onTrackingStopped(boolean expand) {
... super.onTrackingStopped(expand); ... }
子类NotificationPanelView中做了一些工作之后通过super再调用了父类的此方法
onTrackingStopped
讯享网protected void onTrackingStopped(boolean expand) {
mTracking = false; mBar.onTrackingStopped(expand); notifyBarPanelExpansionChanged(); }
接着调用了PanelBar的onTrackingStopped方法,又会调用到PanelBar的子类PhoneStatusBarView覆盖的此方法
PhoneStatusBarView.onTrackingStopped
@Override public void onTrackingStopped(boolean expand) {
super.onTrackingStopped(expand); mBar.onTrackingStopped(expand); }
这里先通过super调用了父类的onTrackingStopped方法,父类中就一行代码” mTracking = false“,接着调到了StatusBar的onTrackingStopped方法
StatusBar.onTrackingStopped
讯享网public void onTrackingStopped(boolean expand) {
//当前设备状态为KEYGUARD或者SHADE_LOCKED if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
//expand就是flingExpands返回值,一直传递过来,为false //有密码锁屏时canSkipBouncer为false,没有则为true if (!expand && !mUnlockMethodCache.canSkipBouncer()) {
showBouncer(false /* scrimmed */); } } }
接着调用showBouncer
showBouncer
@Override public void showBouncer(boolean scrimmed) {
mStatusBarKeyguardViewManager.showBouncer(scrimmed); }
StatusBarKeyguardViewManager.showBouncer
讯享网 public void showBouncer(boolean scrimmed) {
//Keyguard已经已经show并且Bouncer还没有show if (mShowing && !mBouncer.isShowing()) {
mBouncer.show(false /* resetSecuritySelection */, scrimmed); } //更新当前锁屏的各种状态值 updateStates(); }
KeyguardBouncer.show
public void show(boolean resetSecuritySelection, boolean isScrimmed) {
... //ensureView我们前一篇文章分析过,里面会调用inflateView加载 //id为keyguard_bouncer和keyguard_host_view的bouncer布局文件 //并通过addView添加到SystemUI顶层View中 ensureView(); ... //resetSecuritySelection传递过来为false,代表不更新当前bouncer类型 //什么情况下会更新呢?插入带PIN或者PUK的sim卡时 if (resetSecuritySelection) {
showPrimarySecurityScreen(); } //如果当前bouncer已经可见或者mShowingSoon为true则return //mShowingSoon是个标志位,避免重复调用,代表即将显示bouncer if (mRoot.getVisibility() == View.VISIBLE || mShowingSoon) {
return; } final int activeUserId = KeyguardUpdateMonitor.getCurrentUser(); final boolean isSystemUser = UserManager.isSplitSystemUser() && activeUserId == UserHandle.USER_SYSTEM; final boolean allowDismissKeyguard = !isSystemUser && activeUserId == keyguardUserId; //allowDismissKeyguard一般为true,mKeyguardView.dismiss //决定是否显示bouncer,显示哪种bouncer,返回true代表没有bouncer if (allowDismissKeyguard && mKeyguardView.dismiss(activeUserId)) {
return; } ... //标志位 mShowingSoon = true; DejankUtils.removeCallbacks(mResetRunnable); if (mKeyguardUpdateMonitor.isFaceDetectionRunning()) {
mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY); } else {
DejankUtils.postAfterTraversal(mShowRunnable); } mCallback.onBouncerVisiblityChanged(true /* shown */); mExpansionCallback.onStartingToShow(); }
我们先看看决定是否显示bouncer,显示何种类型bouncer(pin/password/pattern)的核心方法KeyguardHostView.dismiss
KeyguardHostView.dismiss
讯享网 public boolean dismiss(int targetUserId) {
... return dismiss(false, targetUserId); } @Override public boolean dismiss(boolean authenticated, int targetUserId) {
return mSecurityContainer.showNextSecurityScreenOrFinish(authenticated, targetUserId); }
mSecurityContainer是一个id为keyguard_security_container,类型为KeyguardSecurityContainer的自定义FrameLayout,被包含在keyguard_host_view.xml中,它是(pin/password/pattern)主要容器
KeyguardSecurityContainer.showNextSecurityScreenOrFinish
boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId) {
//finish作为此方法的返回值,代表了是否显示bouncer界面 boolean finish = false; boolean strongAuth = false; int eventSubtype = -1; //是否可以信任设备,google有一个smart lock的功能,用户设置可信任 //方式则可以跳过密码锁屏 if (mUpdateMonitor.getUserHasTrust(targetUserId)) {
finish = true; ... //如果使用了生物识别解锁也可以跳过密码锁屏 } else if (mUpdateMonitor.getUserUnlockedWithBiometric(targetUserId)) {
finish = true; ... //如果当前的密码锁屏方式为None } else if (SecurityMode.None == mCurrentSecuritySelection) {
SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId); if (SecurityMode.None == securityMode) {
finish = true; eventSubtype = BOUNCER_DISMISS_NONE_SECURITY; } else {
showSecurityScreen(securityMode); } } else if (authenticated) {
//authenticated传递过来为false switch (mCurrentSecuritySelection) {
case Pattern: case Password: case PIN: strongAuth = true; finish = true; eventSubtype = BOUNCER_DISMISS_PASSWORD; break; case SimPin: case SimPuk: SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId); if (securityMode == SecurityMode.None || mLockPatternUtils.isLockScreenDisabled( KeyguardUpdateMonitor.getCurrentUser())) {
finish = true; eventSubtype = BOUNCER_DISMISS_SIM; } else {
showSecurityScreen(securityMode); } break; default: Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe"); showPrimarySecurityScreen(false); break; } } ... return finish; }
通过手指上滑锁屏显示bouncer不会走此方法里的任何分支,finish为是否显示密码锁屏,true代表不显示,false代表显示,所以当我们设置了(pin/password/pattern)则finish不会进任何分支,直接返回false
那我们的锁屏类型是哪里获取的呢?
我们再回到KeyguardBouncer中的inflateView方法,再来深入分析一下此方法相关的代码
inflateView
讯享网protected void inflateView() {
... mRoot = (ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.keyguard_bouncer, null); mKeyguardView = mRoot.findViewById(R.id.keyguard_host_view); ... mContainer.addView(mRoot, mContainer.getChildCount()); ... }
<com.android.keyguard.KeyguardHostView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/res-auto" android:id="@+id/keyguard_host_view" android:layout_width="match_parent" android:layout_height="match_parent" android:clipChildren="false" android:clipToPadding="false" android:importantForAccessibility="yes"> <!-- Needed because TYPE_WINDOW_STATE_CHANGED is sent from this view when bouncer is shown --> <com.android.keyguard.KeyguardSecurityContainer android:id="@+id/keyguard_security_container" android:layout_width="wrap_content" android:layout_height="wrap_content" androidprv:layout_maxHeight="@dimen/keyguard_security_max_height" android:clipChildren="false" android:clipToPadding="false" android:padding="0dp" android:fitsSystemWindows="true" android:layout_gravity="center"> <com.android.keyguard.KeyguardSecurityViewFlipper android:id="@+id/view_flipper" android:layout_width="match_parent" android:layout_height="match_parent" android:clipChildren="false" android:clipToPadding="false" android:paddingTop="@dimen/keyguard_security_view_top_margin" android:paddingStart="@dimen/keyguard_security_view_lateral_margin" android:paddingEnd="@dimen/keyguard_security_view_lateral_margin" android:gravity="center"> </com.android.keyguard.KeyguardSecurityViewFlipper> </com.android.keyguard.KeyguardSecurityContainer> </com.android.keyguard.KeyguardHostView>
我们可以看到keyguard_host_view是KeyguardHostView的布局文件,所以我们来看看KeyguardHostView加载完成后做的事情
KeyguardHostView.onFinishInflate
讯享网 @Override protected void onFinishInflate() {
mSecurityContainer = findViewById(R.id.keyguard_security_container); ... mSecurityContainer.showPrimarySecurityScreen(false); }
showPrimarySecurityScreen
void showPrimarySecurityScreen(boolean turningOff) {
//获取当前的bouncer类型 SecurityMode securityMode = mSecurityModel.getSecurityMode( KeyguardUpdateMonitor.getCurrentUser()); showSecurityScreen(securityMode); }
mSecurityModel.getSecurityMode
讯享网SecurityMode getSecurityMode(int userId) {
KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext); //如果当前有插入SIM卡,并且SIM卡的状态是PUK_REQUIRED //则bouncer类型为SimPuk if (mIsPukScreenAvailable && SubscriptionManager.isValidSubscriptionId( monitor.getNextSubIdForState(IccCardConstants.State.PUK_REQUIRED))) {
return SecurityMode.SimPuk; } //如果当前有插入SIM卡,并且SIM卡的状态是PIN_REQUIRED //则bouncer类型为SimPin if (SubscriptionManager.isValidSubscriptionId( monitor.getNextSubIdForState(IccCardConstants.State.PIN_REQUIRED))) {
return SecurityMode.SimPin; } //否则就通过mLockPatternUtils获取当前的bouncer, //当Settings中设置好了bouncer类型,就会保存到mLockPatternUtils中 final int security = mLockPatternUtils.getActivePasswordQuality(userId); switch (security) {
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX: return SecurityMode.PIN; case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: case DevicePolicyManager.PASSWORD_QUALITY_MANAGED: return SecurityMode.Password; case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: return SecurityMode.Pattern; case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED: return SecurityMode.None; default: throw new IllegalStateException("Unknown security quality:" + security); } }
上面代码就是获取当前的锁屏类型,包含如下的类型
public enum SecurityMode {
Invalid, // NULL state None, // No security enabled Pattern, // Unlock by drawing a pattern. Password, // Unlock by entering an alphanumeric password PIN, // Strictly numeric password SimPin, // Unlock by entering a sim pin. SimPuk // Unlock by entering a sim puk }
bouncer类型有了之后调用showSecurityScreen方法,此方法就是为了更新当前的锁屏类型,以便需要显示的时候直接显示出来
showSecurityScreen
讯享网private void showSecurityScreen(SecurityMode securityMode) {
//如果bouncer的类型并没有发生变化,就不需要更新 if (securityMode == mCurrentSecuritySelection) return; //getSecurityView方法会根据的bouncer类型,得到具体的bouncer自定义View KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection); KeyguardSecurityView newView = getSecurityView(securityMode); //待getSecurityView分析完之后再贴出后面代码分析 ...... }
getSecurityView
private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
//根据bouncer类型获取具体的bouncer的控件id final int securityViewIdForMode = getSecurityViewIdForMode(securityMode); KeyguardSecurityView view = null; //获取mSecurityViewFlipper中的bouncer个数,如果有的话 final int children = mSecurityViewFlipper.getChildCount(); for (int child = 0; child < children; child++) {
if (mSecurityViewFlipper.getChildAt(child).getId() == securityViewIdForMode) {
//找到bouncer返回 view = ((KeyguardSecurityView)mSecurityViewFlipper.getChildAt(child)); break; } } //根据bouncer类型获取具体的bouncer的布局文件id int layoutId = getLayoutIdFor(securityMode); //如果mSecurityViewFlipper还没有bouncer并且能够获取到bouncer的layout if (view == null && layoutId != 0) {
final LayoutInflater inflater = LayoutInflater.from(mContext); //通过layoutId加载具体的bouncer View View v = mInjectionInflationController.injectable(inflater) .inflate(layoutId, mSecurityViewFlipper, false); //并且将bouncer View添加到mSecurityViewFlipper中 mSecurityViewFlipper.addView(v); //给bouncer设置回调和mLockPatternUtils updateSecurityView(v); view = (KeyguardSecurityView)v; view.reset(); } return view; }
我们先看下上面代码中获取bouncer控件id(getSecurityViewIdForMode)和bouncer布局文件id(getLayoutIdFor),我们发现这五种bouncer类型各自有自己的布局文件
讯享网 private int getSecurityViewIdForMode(SecurityMode securityMode) {
switch (securityMode) {
case Pattern: return R.id.keyguard_pattern_view; case PIN: return R.id.keyguard_pin_view; case Password: return R.id.keyguard_password_view; case SimPin: return R.id.keyguard_sim_pin_view; case SimPuk: return R.id.keyguard_sim_puk_view; } return 0; } @VisibleForTesting public int getLayoutIdFor(SecurityMode securityMode) {
switch (securityMode) {
case Pattern: return R.layout.keyguard_pattern_view; case PIN: return R.layout.keyguard_pin_view; case Password: return R.layout.keyguard_password_view; case SimPin: return R.layout.keyguard_sim_pin_view; case SimPuk: return R.layout.keyguard_sim_puk_view; default: return 0; } }
好了我们总结一下getSecurityView方法:
- 根据当前bouncer类型获取bouncer控件id
- 遍历mSecurityViewFlipper,获取已经添加的bouncer
- 根据当前bouncer类型获取bouncer布局文件id
- 如果没有在mSecurityViewFlipper获取到bouncer,则通过LayoutInflater加载布局得到具体的bouncer View
- 最后将bouncer添加到mSecurityViewFlipper中
我们发现其实最终bouncer的直接父容器是KeyguardSecurityViewFlipper,我们再回头看看keyguard_host_view布局:
KeyguardSecurityViewFlipper是此布局最里面的容器,各种bouncer就是这样动态添加到了keyguard_host_view中
<com.android.keyguard.KeyguardHostView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/res-auto" android:id="@+id/keyguard_host_view" android:layout_width="match_parent" android:layout_height="match_parent" android:clipChildren="false" android:clipToPadding="false" android:importantForAccessibility="yes"> <!-- Needed because TYPE_WINDOW_STATE_CHANGED is sent from this view when bouncer is shown --> <com.android.keyguard.KeyguardSecurityContainer android:id="@+id/keyguard_security_container" android:layout_width="wrap_content" android:layout_height="wrap_content" androidprv:layout_maxHeight="@dimen/keyguard_security_max_height" android:clipChildren="false" android:clipToPadding="false" android:padding="0dp" android:fitsSystemWindows="true" android:layout_gravity="center"> <com.android.keyguard.KeyguardSecurityViewFlipper android:id="@+id/view_flipper" android:layout_width="match_parent" android:layout_height="match_parent" android:clipChildren="false" android:clipToPadding="false" android:paddingTop="@dimen/keyguard_security_view_top_margin" android:paddingStart="@dimen/keyguard_security_view_lateral_margin" android:paddingEnd="@dimen/keyguard_security_view_lateral_margin" android:gravity="center"> </com.android.keyguard.KeyguardSecurityViewFlipper> </com.android.keyguard.KeyguardSecurityContainer> </com.android.keyguard.KeyguardHostView>
另外我们需要知道所有的bouncer都实现了KeyguardSecurityView接口,到此bouncer的获取和创建以及动态添加我们就分析完毕了
接着回到showSecurityScreen后半部分继续分析:
讯享网private void showSecurityScreen(SecurityMode securityMode) {
//如果bouncer的类型并没有发生变化,就不需要更新 if (securityMode == mCurrentSecuritySelection) return; //getSecurityView方法会根据的bouncer类型,得到具体的bouncer自定义View //如果需要更新boucer的情况,先获取老的bouncer类型 KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection); //再获取最新改变的bouncer类型 KeyguardSecurityView newView = getSecurityView(securityMode); //如果之前的bouncer不为空则调用onPause隐藏并设置空回调 if (oldView != null) {
oldView.onPause(); oldView.setKeyguardCallback(mNullCallback); } //如果当前设置的bouncer不为None,则调用其onResume激活,并设置回调接口 if (securityMode != SecurityMode.None) {
newView.onResume(KeyguardSecurityView.VIEW_REVEALED); newView.setKeyguardCallback(mCallback); } //获取bouncer的数量 final int childCount = mSecurityViewFlipper.getChildCount(); final int securityViewIdForMode = getSecurityViewIdForMode(securityMode); //遍历mSecurityViewFlipper的bouncer,并根据当前的bouncer类型 //调用setDisplayedChild切换到此bouncer for (int i = 0; i < childCount; i++) {
if (mSecurityViewFlipper.getChildAt(i).getId() == securityViewIdForMode) {
mSecurityViewFlipper.setDisplayedChild(i); break; } } //将当前的bouncer类型保存到mCurrentSecuritySelection mCurrentSecuritySelection = securityMode; //将当前的bouncer View保存到mCurrentSecurityView mCurrentSecurityView = newView; //通知bouncer类型发生改变 mSecurityCallback.onSecurityModeChanged(securityMode, securityMode != SecurityMode.None && newView.needsInput()); }
到此showSecurityScreen方法我们已经全部分析完毕,此方法主要目的就是更新当前系统的bouncer类型并添加到mSecurityViewFlipper,此时bouncer并不可见
我们可以回到KeyguardBouncer.show方法中去了,我们之前分析到mKeyguardView.dismiss方法,此方法返回false代表需要显示bouncer,返回true代表可以跳过,继续看代码:
KeyguardBouncer.show
public void show(boolean resetSecuritySelection, boolean isScrimmed) {
... //ensureView我们前一篇文章分析过,里面会调用inflateView加载 //id为keyguard_bouncer和keyguard_host_view的bouncer布局文件 //并通过addView添加到SystemUI顶层View中 ensureView(); ... //resetSecuritySelection传递过来为false,代表不更新当前bouncer类型 //什么情况下会更新呢?插入带PIN或者PUK的sim卡时 if (resetSecuritySelection) {
showPrimarySecurityScreen(); } //如果当前bouncer已经可见或者mShowingSoon为true则return //mShowingSoon是个标志位,避免重复调用,代表即将显示bouncer if (mRoot.getVisibility() == View.VISIBLE || mShowingSoon) {
return; } final int activeUserId = KeyguardUpdateMonitor.getCurrentUser(); final boolean isSystemUser = UserManager.isSplitSystemUser() && activeUserId == UserHandle.USER_SYSTEM; final boolean allowDismissKeyguard = !isSystemUser && activeUserId == keyguardUserId; //allowDismissKeyguard一般为true,mKeyguardView.dismiss //决定是否显示bouncer,显示哪种bouncer,返回true代表没有bouncer if (allowDismissKeyguard && mKeyguardView.dismiss(activeUserId)) {
return; } ... //标志位 mShowingSoon = true; if (mKeyguardUpdateMonitor.isFaceDetectionRunning()) {
mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY); } else {
DejankUtils.postAfterTraversal(mShowRunnable); } //通知bouncer的可见性发生了变化 mCallback.onBouncerVisiblityChanged(true /* shown */); //通知bouncer开始显示,里面会更新LockIcon图标的变化 mExpansionCallback.onStartingToShow(); }
最后来看看mShowRunnable,
讯享网private final Runnable mShowRunnable = new Runnable() {
@Override public void run() {
//首先就是让mRoot显示出来 mRoot.setVisibility(View.VISIBLE); ... if (mKeyguardView.getHeight() != 0 && mKeyguardView.getHeight() != mStatusBarHeight) {
mKeyguardView.startAppearAnimation(); } else {
mKeyguardView.getViewTreeObserver().addOnPreDrawListener( new ViewTreeObserver.OnPreDrawListener() {
@Override public boolean onPreDraw() {
mKeyguardView.getViewTreeObserver().removeOnPreDrawListener(this); mKeyguardView.startAppearAnimation(); return true; } }); mKeyguardView.requestLayout(); } //标志位置为false mShowingSoon = false; if (mExpansion == EXPANSION_VISIBLE) {
mKeyguardView.onResume(); mKeyguardView.resetSecurityContainer(); } ... } };
这个Runnable做的事情其实就是将bouncer显示出来,不管哪种类型的bouncer在显示的时候都会调用startAppearAnimation方法,开始显示的动画效果
到此滑动锁屏上滑显示密码锁屏已经全部分析完毕,总结一下:
- 我们分析了滑动锁屏上滑的判定策略,系统提供了几种误触判定的方式,包括P-seneor近距离检测,上滑距离检测,速度检测和方向检测
- 我们分析了bouncer的创建,bouncer类型的获取,bouncer的添加,所有bouncer都是添加到KeyguardSecurityViewFlipper中的,以动态添加的方式
- bouncer的显示,不管何种类型的bouncer显示时都会调用startAppearAnimation方法
上篇和这篇全面总结了锁屏的流程,以后遇到和锁屏相关的问题,我们只需要顺着这个流程查找问题,一定会很容易的

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/72558.html