AndroidQ SystemUI之锁屏加载(下)密码锁屏

本篇文章接着上一篇,上一篇主要分析了滑动锁屏加载流程,这篇我们分析密码锁屏也叫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);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
此方法有几个判断条件:

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;

		}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
我们只看Move事件里做的事情:
h等于touch事件Y轴的距离,因为是向上滑,所以这里h是一个负值,需要用绝对值,mTouchSlop等于R.dimen.hint_move_distance等于75dp,所以首先锁屏上Y轴滑动的距离大于75dp就会调用onTrackingStarted,并设置mTracking为true,代表当前正在滑动锁屏中…

当再次满足一个阙值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()) {
       ...
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
先看看给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;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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);

}
1
2
3
4
5
6
子类NotificationPanelView中做了一些工作之后通过super再调用了父类的此方法

onTrackingStopped
protected void onTrackingStopped(boolean expand) {
mTracking = false;
mBar.onTrackingStopped(expand);
notifyBarPanelExpansionChanged();
}
1
2
3
4
5
接着调用了PanelBar的onTrackingStopped方法,又会调用到PanelBar的子类PhoneStatusBarView覆盖的此方法

PhoneStatusBarView.onTrackingStopped
@Override
public void onTrackingStopped(boolean expand) {
super.onTrackingStopped(expand);
mBar.onTrackingStopped(expand);
}
1
2
3
4
5
这里先通过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 */);
}
}
}
1
2
3
4
5
6
7
8
9
10
接着调用showBouncer

showBouncer
@Override
public void showBouncer(boolean scrimmed) {
mStatusBarKeyguardViewManager.showBouncer(scrimmed);
}
1
2
3
4
StatusBarKeyguardViewManager.showBouncer
public void showBouncer(boolean scrimmed) {
//Keyguard已经已经show并且Bouncer还没有show
if (mShowing && !mBouncer.isShowing()) {
mBouncer.show(false /* resetSecuritySelection */, scrimmed);
}
//更新当前锁屏的各种状态值
updateStates();
}
1
2
3
4
5
6
7
8
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();
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
我们先看看决定是否显示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);
}
1
2
3
4
5
6
7
8
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;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
通过手指上滑锁屏显示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());
    ...
}

1
2
3
4
5
6
7
8
9
我们来看下,首先加载keyguard_bouncer的布局,然后加载keyguard_host_view的布局:
keyguard_host_view.xml

<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”>

<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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
我们可以看到keyguard_host_view是KeyguardHostView的布局文件,所以我们来看看KeyguardHostView加载完成后做的事情

KeyguardHostView.onFinishInflate
@Override
protected void onFinishInflate() {
mSecurityContainer =
findViewById(R.id.keyguard_security_container);

mSecurityContainer.showPrimarySecurityScreen(false);
}
1
2
3
4
5
6
7
keyguard_security_container我们前面讲过,是一个被包含在keyguard_host_view
中的KeyguardSecurityContainer,我们主要看调用它的showPrimarySecurityScreen方法

showPrimarySecurityScreen
void showPrimarySecurityScreen(boolean turningOff) {
//获取当前的bouncer类型
SecurityMode securityMode = mSecurityModel.getSecurityMode(
KeyguardUpdateMonitor.getCurrentUser());

    showSecurityScreen(securityMode);
}

1
2
3
4
5
6
7
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);
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
上面代码就是获取当前的锁屏类型,包含如下的类型

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
}
1
2
3
4
5
6
7
8
9
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分析完之后再贴出后面代码分析

}
1
2
3
4
5
6
7
8
9
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;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
我们先看下上面代码中获取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;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
好了我们总结一下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”>

<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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
另外我们需要知道所有的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());
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
到此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();
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
最后来看看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();
}

}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
这个Runnable做的事情其实就是将bouncer显示出来,不管哪种类型的bouncer在显示的时候都会调用startAppearAnimation方法,开始显示的动画效果

到此滑动锁屏上滑显示密码锁屏已经全部分析完毕,总结一下:

我们分析了滑动锁屏上滑的判定策略,系统提供了几种误触判定的方式,包括P-seneor近距离检测,上滑距离检测,速度检测和方向检测
我们分析了bouncer的创建,bouncer类型的获取,bouncer的添加,所有bouncer都是添加到KeyguardSecurityViewFlipper中的,以动态添加的方式
bouncer的显示,不管何种类型的bouncer显示时都会调用startAppearAnimation方法
上篇和这篇全面总结了锁屏的流程,以后遇到和锁屏相关的问题,我们只需要顺着这个流程查找问题,一定会很容易的

原文链接:https://blog.csdn.net/qq_34211365/article/details/104575238

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值