前言
滑动解锁相对于来说逻辑还是简单的,说白了就是对事件的处理,然后做一些事。
这里主要从锁屏的界面Layout结构、touchEvent事件分发、解锁动作逻辑几个方面进行源码的分析。
锁屏的界面Layout结构分析
StatusbarWindowView
整个锁屏界面的顶级 View 就是 StatusbarWindowView;
StatusBar#createAndAddWindows()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
java
public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
makeStatusBarView(result);
mNotificationShadeWindowController.attach();
// 添加视图
mStatusBarWindowController.attach();
}
StatusBarWindowController#attach()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
java
public void attach() {
// Now that the status bar window encompasses the sliding panel and its
// translucent backdrop, the entire thing is made TRANSLUCENT and is
// hardware-accelerated.
mLp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
mBarHeight,
WindowManager.LayoutParams.TYPE_STATUS_BAR,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
| WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
PixelFormat.TRANSLUCENT);
mLp.privateFlags |= PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
mLp.token = new Binder();
mLp.gravity = Gravity.TOP;
mLp.setFitInsetsTypes(0 /* types */);
mLp.setTitle("StatusBar");
mLp.packageName = mContext.getPackageName();
mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mWindowManager.addView(mStatusBarView, mLp);
mLpChanged.copyFrom(mLp);
}
StatusBarWindow 是在 StatusBar 的 createAndAddWindows() 流程中调用StatusBarWindowController.attach() 添加到窗口上的, type为WindowManager.LayoutParams.TYPE_STATUS_BAR
Layout结构
锁屏界面的Layout结构可以简单概括为以下结构:
- mStatusBarWindow--> R.layout.super_status_bar
- notification_panel--> R.layout.status_bar_expanded
- keyguardBouncer-->R.layout.keyguard_bouncer
java
mStatusBarWindow-->notification_panel-->notification_container_parent-->keyguard_header(锁屏状态栏)
| |
| -->keyguard_bottom_area (lock_icon和充电状态等)
| |
| -->keyguard_status_view (锁屏时钟日期)
| |
| -->keyguard_up_slide (箭头提示动画)
|
-->keyguardBouncer(安全锁界面)
touchEvent事件分发
我们这里分析上滑解锁过程中的touchEvent事件分发
android中的事件分发概念:事件序列。
事件序列
在Android系统中,一个单独的事件基本上是没什么作用的,只有一个事件序列,才有意义。一个事件序列正常情况下,定义为 DOWN、MOVE(0或者多个)、UP/CANCEL。事件序列以DOWN事件开始,中间会有0或者多个MOVE事件,最后以UP事件或者CANCEL事件结束。
DOWN事件作为序列的开始,有一个很重要的职责,就是寻找事件序列的接受者,怎么理解呢?framework 在DOWN事件的传递过程中,需要根据View事件处理方法(onTouchEvent)的返回值来确定事件序列的接受者。如果一个View的onTouchEvent事件,在处理DOWN事件的时候返回true,说明它愿意接受并处理该事件序列。
上滑解锁
当用户移动手指时,产生touch down事件,最外层view StatusBarWindowView会执行onInterceptTouchEvent,看是否需要拦截touch事件。再一级级往子View传递,都没有被拦截,之后执行OnTouchEvent从子View开始一级级往父View传递,到PanelView这里当手指移动的距离达到一定的阈值会调用onTrackingStarted从而设置mTracking的值为true,onTouchEvent返回true,接收此touch move事件,之后的touch事件直接传到此View。
在用户滑动过程会调用setExpandedHeightInternal,进而调用NotificationPanelView的onHeightUpdated进行锁屏上的时间和通知View根据手指的移动距离进行缩小、变透明处理。
当用户抬起手指时,产生touch up事件,PanelView接收到这个事件后会调用endMotionEvent,如果手指从down到up之间移动的距离达到一定阈值会调用onTrackingStopped。
- 硬件发出指令:按下,移动,抬起
- input接收
- 代码执行相应操作:ACTION_DOWN,ACTION_MOVE,ACTION_UP
PanelView#onInterceptTouchEvent()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
java
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return mTouchHandler.onInterceptTouchEvent(event);
}
PanelViewController
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
java
public class TouchHandler implements View.OnTouchListener {
public boolean onInterceptTouchEvent(MotionEvent event) {
if (mInstantExpanding || !mNotificationsDragEnabled || mTouchDisabled || (mMotionAborted
&& event.getActionMasked() != MotionEvent.ACTION_DOWN)) {
return false;
}
/*
* If the user drags anywhere inside the panel we intercept it if the movement is
* upwards. This allows closing the shade from anywhere inside the panel.
*
* We only do this if the current content is scrolled to the bottom,
* i.e canCollapsePanelOnTouch() is true and therefore there is no conflicting scrolling
* gesture
* possible.
*/
int pointerIndex = event.findPointerIndex(mTrackingPointer);
if (pointerIndex < 0) {
pointerIndex = 0;
mTrackingPointer = event.getPointerId(pointerIndex);
}
final float x = event.getX(pointerIndex);
final float y = event.getY(pointerIndex);
boolean canCollapsePanel = canCollapsePanelOnTouch();
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mStatusBar.userActivity();
mAnimatingOnDown = mHeightAnimator != null;
mMinExpandHeight = 0.0f;
mDownTime = SystemClock.uptimeMillis();
if (mAnimatingOnDown && mClosing && !mHintAnimationRunning
|| mPeekAnimator != null) {
cancelHeightAnimator();
cancelPeek();
mTouchSlopExceeded = true;
return true;
}
mInitialTouchY = y;
mInitialTouchX = x;
mTouchStartedInEmptyArea = !isInContentBounds(x, y);
mTouchSlopExceeded = mTouchSlopExceededBeforeDown;
mJustPeeked = false;
mMotionAborted = false;
mPanelClosedOnDown = isFullyCollapsed();
mCollapsedAndHeadsUpOnDown = false;
mHasLayoutedSinceDown = false;
mUpdateFlingOnLayout = false;
mTouchAboveFalsingThreshold = false;
addMovement(event);
break;
case MotionEvent.ACTION_POINTER_UP:
final int upPointer = event.getPointerId(event.getActionIndex());
if (mTrackingPointer == upPointer) {
// gesture is ongoing, find a new pointer to track
final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
mTrackingPointer = event.getPointerId(newIndex);
mInitialTouchX = event.getX(newIndex);
mInitialTouchY = event.getY(newIndex);
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
mMotionAborted = true;
mVelocityTracker.clear();
}
break;
case MotionEvent.ACTION_MOVE:
final float h = y - mInitialTouchY;
addMovement(event);
if (canCollapsePanel || mTouchStartedInEmptyArea || mAnimatingOnDown) {
float hAbs = Math.abs(h);
float touchSlop = getTouchSlop(event);
if ((h < -touchSlop || (mAnimatingOnDown && hAbs > touchSlop))
&& hAbs > Math.abs(x - mInitialTouchX)) {
cancelHeightAnimator();
startExpandMotion(x, y, true /* startTracking */, mExpandedHeight);
return true;
}
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mVelocityTracker.clear();
break;
}
return false;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if (mInstantExpanding || (mTouchDisabled
&& event.getActionMasked() != MotionEvent.ACTION_CANCEL) || (mMotionAborted
&& event.getActionMasked() != MotionEvent.ACTION_DOWN)) {
return false;
}
// If dragging should not expand the notifications shade, then return false.
if (!mNotificationsDragEnabled) {
if (mTracking) {
// Turn off tracking if it's on or the shade can get stuck in the down position.
onTrackingStopped(true /* expand */);
}
return false;
}
// On expanding, single mouse click expands the panel instead of dragging.
if (isFullyCollapsed() && event.isFromSource(InputDevice.SOURCE_MOUSE)) {
if (event.getAction() == MotionEvent.ACTION_UP) {
expand(true);
}
return true;
}
/*
* We capture touch events here and update the expand height here in case according to
* the users fingers. This also handles multi-touch.
*
* If the user just clicks shortly, we show a quick peek of the shade.
*
* Flinging is also enabled in order to open or close the shade.
*/
int pointerIndex = event.findPointerIndex(mTrackingPointer);
if (pointerIndex < 0) {
pointerIndex = 0;
mTrackingPointer = event.getPointerId(pointerIndex);
}
final float x = event.getX(pointerIndex);
final float y = event.getY(pointerIndex);
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
mGestureWaitForTouchSlop = shouldGestureWaitForTouchSlop();
mIgnoreXTouchSlop = isFullyCollapsed() || shouldGestureIgnoreXTouchSlop(x, y);
}
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
mJustPeeked = false;
mMinExpandHeight = 0.0f;
mPanelClosedOnDown = isFullyCollapsed();
mHasLayoutedSinceDown = false;
mUpdateFlingOnLayout = false;
mMotionAborted = false;
mPeekTouching = mPanelClosedOnDown;
mDownTime = SystemClock.uptimeMillis();
mTouchAboveFalsingThreshold = false;
mCollapsedAndHeadsUpOnDown =
isFullyCollapsed() && mHeadsUpManager.hasPinnedHeadsUp();
addMovement(event);
if (!mGestureWaitForTouchSlop || (mHeightAnimator != null
&& !mHintAnimationRunning) || mPeekAnimator != null) {
mTouchSlopExceeded =
(mHeightAnimator != null && !mHintAnimationRunning)
|| mPeekAnimator != null || mTouchSlopExceededBeforeDown;
cancelHeightAnimator();
cancelPeek();
onTrackingStarted();
}
if (isFullyCollapsed() && !mHeadsUpManager.hasPinnedHeadsUp()
&& !mStatusBar.isBouncerShowing()) {
startOpening(event);
}
break;
case MotionEvent.ACTION_POINTER_UP:
final int upPointer = event.getPointerId(event.getActionIndex());
if (mTrackingPointer == upPointer) {
// gesture is ongoing, find a new pointer to track
final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
final float newY = event.getY(newIndex);
final float newX = event.getX(newIndex);
mTrackingPointer = event.getPointerId(newIndex);
startExpandMotion(newX, newY, true /* startTracking */, mExpandedHeight);
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
mMotionAborted = true;
endMotionEvent(event, x, y, true /* forceCancel */);
return false;
}
break;
case MotionEvent.ACTION_MOVE:
addMovement(event);
float h = y - mInitialTouchY;
// If the panel was collapsed when touching, we only need to check for the
// y-component of the gesture, as we have no conflicting horizontal gesture.
if (Math.abs(h) > getTouchSlop(event)
&& (Math.abs(h) > Math.abs(x - mInitialTouchX)
|| mIgnoreXTouchSlop)) {
mTouchSlopExceeded = true;
if (mGestureWaitForTouchSlop && !mTracking && !mCollapsedAndHeadsUpOnDown) {
if (!mJustPeeked && mInitialOffsetOnTouch != 0f) {
startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
h = 0;
}
cancelHeightAnimator();
// 向上滑动时,手指移动的距离达到一定的阈值会调用onTrackingStarted,
// 设置mTracking值为true,从而接收touch事件
onTrackingStarted();
}
}
float newHeight = Math.max(0, h + mInitialOffsetOnTouch);
if (newHeight > mPeekHeight) {
if (mPeekAnimator != null) {
mPeekAnimator.cancel();
}
mJustPeeked = false;
} else if (mPeekAnimator == null && mJustPeeked) {
// The initial peek has finished, but we haven't dragged as far yet, lets
// speed it up by starting at the peek height.
mInitialOffsetOnTouch = mExpandedHeight;
mInitialTouchY = y;
mMinExpandHeight = mExpandedHeight;
mJustPeeked = false;
}
newHeight = Math.max(newHeight, mMinExpandHeight);
if (-h >= getFalsingThreshold()) {
mTouchAboveFalsingThreshold = true;
mUpwardsWhenThresholdReached = isDirectionUpwards(x, y);
}
if (!mJustPeeked && (!mGestureWaitForTouchSlop || mTracking)
&& !isTrackingBlocked()) {
// 用户滑动过程会调用setExpandedHeightInternal
setExpandedHeightInternal(newHeight);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
addMovement(event);
endMotionEvent(event, x, y, false /* forceCancel */);
break;
}
return !mGestureWaitForTouchSlop || mTracking;
}
}
移动过程中:主要在调用了两个方法。
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
java
protected void onTrackingStarted() {
endClosing();
mTracking = true;
mBar.onTrackingStarted();
notifyExpandingStarted();
notifyBarPanelExpansionChanged();
}
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
java
public void setExpandedHeightInternal(float h) {
if (isNaN(h)) {
Log.wtf(TAG, "ExpandedHeight set to NaN");
}
if (mExpandLatencyTracking && h != 0f) {
DejankUtils.postAfterTraversal(
() -> mLatencyTracker.onActionEnd(LatencyTracker.ACTION_EXPAND_PANEL));
mExpandLatencyTracking = false;
}
float fhWithoutOverExpansion = getMaxPanelHeight() - getOverExpansionAmount();
if (mHeightAnimator == null) {
float overExpansionPixels = Math.max(0, h - fhWithoutOverExpansion);
if (getOverExpansionPixels() != overExpansionPixels && mTracking) {
setOverExpansion(overExpansionPixels, true /* isPixels */);
}
mExpandedHeight = Math.min(h, fhWithoutOverExpansion) + getOverExpansionAmount();
} else {
mExpandedHeight = h;
if (mOverExpandedBeforeFling) {
setOverExpansion(Math.max(0, h - fhWithoutOverExpansion), false /* isPixels */);
}
}
// If we are closing the panel and we are almost there due to a slow decelerating
// interpolator, abort the animation.
if (mExpandedHeight < 1f && mExpandedHeight != 0f && mClosing) {
mExpandedHeight = 0f;
if (mHeightAnimator != null) {
mHeightAnimator.end();
}
}
mExpandedFraction = Math.min(1f,
fhWithoutOverExpansion == 0 ? 0 : mExpandedHeight / fhWithoutOverExpansion);
// 进行锁屏上的时间和通知View根据手指的移动距离进行缩小、变透明处理
onHeightUpdated(mExpandedHeight);
notifyBarPanelExpansionChanged();
}
下面主要从:onHeightUpdated、notifyBarPanelExpansionChanged 两方法作为入口。
先看 NotificationPanelViewController#onHeightUpdated()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
java
@Override
protected void onHeightUpdated(float expandedHeight) {
if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) {
// 更新时钟位置将设置顶部填充,这可能会触发新的面板高度并重新定位时钟。
// 这是一个循环依赖项,应该避免,否则会出现堆栈溢出。
if (mStackScrollerMeasuringPass > 2) {
if (DEBUG) Log.d(TAG, "Unstable notification panel height. Aborting.");
} else {
//锁屏上的时间和通知View根据手指的移动距离进行缩小、变透明处理
positionClockAndNotifications();
}
}
if (mQsExpandImmediate || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null
&& !mQsExpansionFromOverscroll) {
float t;
if (mKeyguardShowing) {
// 在Keyguard上,将QS扩展线性插值到面板扩展
t = expandedHeight / (getMaxPanelHeight());
} else {
// In Shade, interpolate linearly such that QS is closed whenever panel height is
// minimum QS expansion + minStackHeight
float
panelHeightQsCollapsed =
mNotificationStackScroller.getIntrinsicPadding()
+ mNotificationStackScroller.getLayoutMinHeight();
float panelHeightQsExpanded = calculatePanelHeightQsExpanded();
t =
(expandedHeight - panelHeightQsCollapsed) / (panelHeightQsExpanded
- panelHeightQsCollapsed);
}
float
targetHeight =
mQsMinExpansionHeight + t * (mQsMaxExpansionHeight - mQsMinExpansionHeight);
setQsExpansion(targetHeight);
}
updateExpandedHeight(expandedHeight);
updateHeader();
// 更新通知半透明
updateNotificationTranslucency();
updatePanelExpanded();
updateGestureExclusionRect();
if (DEBUG) {
mView.invalidate();
}
}
到这里了就一起看个滑动解锁的堆栈:
shell
12-30 08:32:48.658 1479 1479 D longzhiye : java.lang.Throwable:
12-30 08:32:48.658 1479 1479 D longzhiye : at com.android.keyguard.KeyguardSecurityContainer.showNextSecurityScreenOrFinish(KeyguardSecurityContainer.java:710)
12-30 08:32:48.658 1479 1479 D longzhiye : at com.android.keyguard.KeyguardHostView.dismiss(KeyguardHostView.java:214)
12-30 08:32:48.658 1479 1479 D longzhiye : at com.android.keyguard.KeyguardHostView.dismiss(KeyguardHostView.java:196)
12-30 08:32:48.658 1479 1479 D longzhiye : at com.android.systemui.statusbar.phone.KeyguardBouncer.show(KeyguardBouncer.java:167)
12-30 08:32:48.658 1479 1479 D longzhiye : at com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.showBouncer(StatusBarKeyguardViewManager.java:434)
12-30 08:32:48.658 1479 1479 D longzhiye : at com.android.systemui.statusbar.phone.StatusBar.showBouncerIfKeyguard(StatusBar.java:3959)
12-30 08:32:48.658 1479 1479 D longzhiye : at com.android.systemui.statusbar.phone.StatusBar.makeExpandedInvisible(StatusBar.java:2506)
12-30 08:32:48.658 1479 1479 D longzhiye : at com.android.systemui.statusbar.phone.PhoneStatusBarView$1.run(PhoneStatusBarView.java:65)
12-30 08:32:48.658 1479 1479 D longzhiye : at android.os.Handler.handleCallback(Handler.java:938)
12-30 08:32:48.658 1479 1479 D longzhiye : at android.os.Handler.dispatchMessage(Handler.java:99)
12-30 08:32:48.658 1479 1479 D longzhiye : at android.os.Looper.loop(Looper.java:223)
12-30 08:32:48.658 1479 1479 D longzhiye : at android.app.ActivityThread.main(ActivityThread.java:7945)
12-30 08:32:48.658 1479 1479 D longzhiye : at java.lang.reflect.Method.invoke(Native Method)
12-30 08:32:48.658 1479 1479 D longzhiye : at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:603)
12-30 08:32:48.658 1479 1479 D longzhiye : at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
再看一个滑动后到密码安全锁(即密码解锁)的堆栈
shell
12-30 09:28:31.8189 1470 1470 D longzhiye: : java.lang.Throwable
12-30 09:28:31.8189 1470 1470 D longzhiye: : at com.android.keyguard.KeyguardSecurityContainer.showNextSecurityScreenOrFinish(KeyguardSecurityContainer.java:710)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at com.android.keyguard.KeyguardHostView.dismiss(KeyguardHostView.java:214)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at com.android.keyguard.KeyguardHostView.dismiss(KeyguardHostView.java:196)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at com.android.systemui.statusbar.phone.KeyguardBouncer.show(KeyguardBouncer.java:167)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.onPanelExpansionChanged(StatusBarKeyguardViewManager.java:297)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at com.android.systemui.statusbar.phone.PanelViewController.notifyBarPanelExpansionChanged(PanelViewController.java:1011)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at com.android.systemui.statusbar.phone.PanelViewController.setExpandedHeightInternal(PanelViewController.java:727)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at com.android.systemui.statusbar.phone.PanelViewController$TouchHandler.onTouch(PanelViewController.java:1338)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at com.android.systemui.statusbar.phone.NotificationPanelViewController$18.onTouch(NotificationPanelViewController.java:3229)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at android.view.View.dispatchTouchEvent(View.java:14385)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2792)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3126)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2806)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at com.android.systemui.statusbar.phone.NotificationShadeWindowView.dispatchTouchEvent(NotificationShadeWindowView.java:173)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at android.view.View.dispatchPointerEvent(View.java:14656)
// 省略部分Log....
接着看 PanelViewController#notifyBarPanelExpansionChanged()
这里注意:都是到 KeyguardSecurityContainer#KeyguardSecurityContainer(),滑动解锁与带有密码安全界面,即密码解锁,两个流程的代码执行顺序有差异。
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
java
protected void notifyBarPanelExpansionChanged() {
if (mBar != null) {
mBar.panelExpansionChanged(
mExpandedFraction,
mExpandedFraction > 0f || mPeekAnimator != null || mInstantExpanding
|| isPanelVisibleBecauseOfHeadsUp() || mTracking
|| mHeightAnimator != null);
}
for (int i = 0; i < mExpansionListeners.size(); i++) {
mExpansionListeners.get(i).onPanelExpansionChanged(mExpandedFraction, mTracking);
}
}
再 notifyBarPanelExpansionChanged() 方法中继而调用 PanelBar 中的panelExpansionChanged方法:页面的透明度。
PanelBar#panelExpansionChanged()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
java
public void panelExpansionChanged(float frac, boolean expanded) {
if (isNaN(frac)) {
throw new IllegalArgumentException("frac cannot be NaN");
}
boolean fullyClosed = true;
boolean fullyOpened = false;
if (SPEW) LOG("panelExpansionChanged: start state=%d", mState);
PanelViewController pv = mPanel;
mExpanded = expanded;
mPanelFraction = frac;
updateVisibility();
// 调整可能部分可见的任何其他面板
if (expanded) {
if (mState == STATE_CLOSED) {
go(STATE_OPENING);
onPanelPeeked();
}
fullyClosed = false;
final float thisFrac = pv.getExpandedFraction();
if (SPEW) LOG("panelExpansionChanged: -> %s: f=%.1f", pv.getName(), thisFrac);
fullyOpened = thisFrac >= 1f;
}
// fullyOpened 完全打开;就是:锁屏界面是否完全展开的;(手指不滑动时,fullyOpened = true,fullyClosed = false;)
// fullyClosed 完全关闭,就是:锁屏界面是否完全折叠,即消失了;(锁屏界面上滑消失时,fullyOpened = false,fullyClosed = true)
// 手指在滑动过程中时:fullyOpened = false,fullyClosed = false
if (fullyOpened && !mTracking) {
go(STATE_OPEN);
onPanelFullyOpened();
} else if (fullyClosed && !mTracking && mState != STATE_CLOSED) {
go(STATE_CLOSED);
// 面板折叠时
onPanelCollapsed();
}
if (SPEW) LOG("panelExpansionChanged: end state=%d [%s%s ]", mState,
fullyOpened?" fullyOpened":"", fullyClosed?" fullyClosed":"");
}
当锁屏页面完全消失时,调用 onPanelCollapsed() 方法,执行 post() 方法。
PhoneStatusBarView#onPanelCollapsed()
java
@Override
public void onPanelCollapsed() {
super.onPanelCollapsed();
// Close the status bar in the next frame so we can show the end of the animation.
post(mHideExpandedRunnable);
mIsFullyOpenedPanel = false;
}
private Runnable mHideExpandedRunnable = new Runnable() {
@Override
public void run() {
if (mPanelFraction == 0.0f) {
mBar.makeExpandedInvisible();
}
}
};
接下来执行 StatusBar 中的 makeExpandedInvisible() 方法,更新通知栏和状态栏窗口的可见性。
StatusBar#makeExpandedInvisible()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
java
void makeExpandedInvisible() {
if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible
+ " mExpandedVisible=" + mExpandedVisible);
if (!mExpandedVisible || mNotificationShadeWindowView == null) {
return;
}
// 确保面板完全折叠
mStatusBarView.collapsePanel(/*animate=*/ false, false /* delayed*/,
1.0f /* speedUpFactor */);
mNotificationPanelViewController.closeQs();
mExpandedVisible = false;
visibilityChanged(false);
// 更新通知阴影和状态栏窗口的可见性
mNotificationShadeWindowController.setPanelVisible(false);
mStatusBarWindowController.setForceStatusBarVisible(false);
// Close any guts that might be visible
mGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
mShadeController.runPostCollapseRunnables();
setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
if (!mNotificationActivityStarter.isCollapsingToShowActivityOverLockscreen()) {
showBouncerIfKeyguard();
} else if (DEBUG) {
Log.d(TAG, "Not showing bouncer due to activity showing over lockscreen");
}
mCommandQueue.recomputeDisableFlags(
mDisplayId,
mNotificationPanelViewController.hideStatusBarIconsWhenExpanded() /* animate */);
// Trimming will happen later if Keyguard is showing - doing it here might cause a jank in
// the bouncer appear animation.
if (!mStatusBarKeyguardViewManager.isShowing()) {
WindowManagerGlobal.getInstance().trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
}
}
StatusBar#showBouncerIfKeyguard()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
java
private void showBouncerIfKeyguard() {
if ((mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)
&& !mKeyguardViewMediator.isHiding()) {
// !mKeyguardViewMediator.isHiding() 不管是滑动解锁还是PIN码解锁等方式都是 true
mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */);
}
}
StatusBarKeyguardViewManager#showBouncer()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
java
public void showBouncer(boolean scrimmed) {
// 如果是滑动解锁,这里 if 条件是 true,如果是密码解锁,这里是 false。
if (mShowing && !mBouncer.isShowing()) {
mBouncer.show(false /* resetSecuritySelection */, scrimmed);
}
updateStates();
}
KeyguardBouncer#show() 如果有设置密码,则显示安全锁界面
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
java
public void show(boolean resetSecuritySelection, boolean isScrimmed) {
final int keyguardUserId = KeyguardUpdateMonitor.getCurrentUser();
if (keyguardUserId == UserHandle.USER_SYSTEM && UserManager.isSplitSystemUser()) {
// In split system user mode, we never unlock system user.
return;
}
ensureView();
mIsScrimmed = isScrimmed;
if (isScrimmed) {
setExpansion(EXPANSION_VISIBLE);
}
if (resetSecuritySelection) {
showPrimarySecurityScreen();
}
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;
// 重点关注 dismiss()
if (allowDismissKeyguard && mKeyguardView.dismiss(activeUserId)) {
return;
}
if (!allowDismissKeyguard) {
Log.w(TAG, "User can't dismiss keyguard: " + activeUserId + " != " + keyguardUserId);
}
mShowingSoon = true;
// Split up the work over multiple frames.
DejankUtils.removeCallbacks(mResetRunnable);
if (mKeyguardStateController.isFaceAuthEnabled() && !needsFullscreenBouncer()
&& !mKeyguardUpdateMonitor.userNeedsStrongAuth()
&& !mKeyguardBypassController.getBypassEnabled()) {
mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY);
} else {
DejankUtils.postAfterTraversal(mShowRunnable);
}
// 安全锁设置可见性
mCallback.onBouncerVisiblityChanged(true /* shown */);
// 开始显示
mExpansionCallback.onStartingToShow();
}
这里重点关注 mKeyguardView.dismiss(activeUserId) ;
KeyguardHostView#dismiss()
frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
java
// 显示安全锁
public boolean dismiss(int targetUserId) {
return dismiss(false, targetUserId, false);
}
@Override
public boolean dismiss(boolean authenticated, int targetUserId,
boolean bypassSecondaryLockScreen) {
// 重点关注
return mSecurityContainer.showNextSecurityScreenOrFinish(authenticated, targetUserId,
bypassSecondaryLockScreen);
}
KeyguardSecurityContainer#showNextSecurityScreenOrFinish()
frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
java
// 显示下一个安全屏幕(如果有)
boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId,
boolean bypassSecondaryLockScreen) {
if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
boolean finish = false;
boolean strongAuth = false;
int eventSubtype = -1;
int unLockMode = 0;// add for KFCAANWIKFRA-833
BouncerUiEvent uiEvent = BouncerUiEvent.UNKNOWN;
if (mUpdateMonitor.getUserHasTrust(targetUserId)) {
// 省略部分代码......
} else if (mUpdateMonitor.getUserUnlockedWithBiometric(targetUserId)) {
// 省略部分代码......
} else if (SecurityMode.None == mCurrentSecuritySelection) { // mCurrentSecuritySelection当前安全选择的模式
SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
if (SecurityMode.None == securityMode) {
unLockMode = 0;
finish = true; // 没有安全锁
eventSubtype = BOUNCER_DISMISS_NONE_SECURITY;
uiEvent = BouncerUiEvent.BOUNCER_DISMISS_NONE_SECURITY;
} else {
showSecurityScreen(securityMode); // switch to the alternate security view
}
} else if (authenticated) {
// mCurrentSecuritySelection 当前锁的模式
switch (mCurrentSecuritySelection) {
case Pattern:
case Password:
case PIN:
unLockMode = mCurrentSecuritySelection.ordinal() - 1;
strongAuth = true;
finish = true;
eventSubtype = BOUNCER_DISMISS_PASSWORD;
uiEvent = BouncerUiEvent.BOUNCER_DISMISS_PASSWORD;
break;
// 省略部分代码......
default:
Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe");
showPrimarySecurityScreen(false);
break;
}
}
// 检查设备管理员指定的其他安全措施。
/* UNISOC: Modify for bug1394148 @{ */
Intent secondaryLockscreenIntent =
mUpdateMonitor.getSecondaryLockscreenRequirement(targetUserId);
if (finish && !bypassSecondaryLockScreen && secondaryLockscreenIntent != null) {
mSecondaryLockScreenController.show(secondaryLockscreenIntent);
return false;
}
/* @} */
if (eventSubtype != -1) {
mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER)
.setType(MetricsEvent.TYPE_DISMISS).setSubtype(eventSubtype));
}
if (uiEvent != BouncerUiEvent.UNKNOWN) {
sUiEventLogger.log(uiEvent);
}
// finish 是否还有一个安全屏幕,即是否解锁完成,有则返回 false,没有返回 true
if (finish) {
// 重点关注
mSecurityCallback.finish(strongAuth, targetUserId);
// 省略部分代码......
}
return finish;
}
KeyguardHostView#finish()
frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
java
@Override
public void finish(boolean strongAuth, int targetUserId) {
// If there's a pending runnable because the user interacted with a widget
// and we're leaving keyguard, then run it.
boolean deferKeyguardDone = false;
if (mDismissAction != null) {
deferKeyguardDone = mDismissAction.onDismiss();
mDismissAction = null;
mCancelAction = null;
}
if (mViewMediatorCallback != null) {
if (deferKeyguardDone) { // deferKeyguardDone 上面设置成了 false
mViewMediatorCallback.keyguardDonePending(strongAuth, targetUserId);
} else {
// 重点关注
mViewMediatorCallback.keyguardDone(strongAuth, targetUserId);
}
}
}
KeyguardViewMediator#keyguardDone()
frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
java
@Override
public void keyguardDone(boolean strongAuth, int targetUserId) {
if (targetUserId != ActivityManager.getCurrentUser()) {
return;
}
if (DEBUG) Log.d(TAG, "keyguardDone");
tryKeyguardDone();
}
private void tryKeyguardDone() {
if (DEBUG) {
Log.d(TAG, "tryKeyguardDone: pending - " + mKeyguardDonePending + ", animRan - "
+ mHideAnimationRun + " animRunning - " + mHideAnimationRunning);
}
if (!mKeyguardDonePending && mHideAnimationRun && !mHideAnimationRunning) {
handleKeyguardDone();
} else if (!mHideAnimationRun) {
if (DEBUG) Log.d(TAG, "tryKeyguardDone: starting pre-hide animation");
mHideAnimationRun = true;
mHideAnimationRunning = true;
mKeyguardViewControllerLazy.get()
.startPreHideAnimation(mHideAnimationFinishedRunnable); // 启动预隐藏动画
}
}