Android 10.0 滑动解锁流程

server/2024/12/22 9:04:27/

前言

滑动解锁相对于来说逻辑还是简单的,说白了就是对事件的处理,然后做一些事。
这里主要从锁屏的界面Layout结构、touchEvent事件分发、解锁动作逻辑几个方面进行源码的分析。

锁屏的界面Layout结构分析

StatusbarWindowView
整个锁屏界面的顶级 View 就是 StatusbarWindowView;
StatusBar#createAndAddWindows()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.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

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

  1. 硬件发出指令:按下,移动,抬起
  2. input接收
  3. 代码执行相应操作:ACTION_DOWN,ACTION_MOVE,ACTION_UP

PanelView#onInterceptTouchEvent()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java

    @Overridepublic boolean onInterceptTouchEvent(MotionEvent event) {return mTouchHandler.onInterceptTouchEvent(event);}

PanelViewController
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.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 trackfinal 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;}@Overridepublic 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 trackfinal 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()) {// 用户滑动过程会调用setExpandedHeightInternalsetExpandedHeightInternal(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

protected void onTrackingStarted() {endClosing();mTracking = true;mBar.onTrackingStarted();notifyExpandingStarted();notifyBarPanelExpansionChanged();}

frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.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

    @Overrideprotected 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 + minStackHeightfloatpanelHeightQsCollapsed =mNotificationStackScroller.getIntrinsicPadding()+ mNotificationStackScroller.getLayoutMinHeight();float panelHeightQsExpanded = calculatePanelHeightQsExpanded();t =(expandedHeight - panelHeightQsCollapsed) / (panelHeightQsExpanded- panelHeightQsCollapsed);}floattargetHeight =mQsMinExpansionHeight + t * (mQsMaxExpansionHeight - mQsMinExpansionHeight);setQsExpansion(targetHeight);}updateExpandedHeight(expandedHeight);updateHeader();// 更新通知半透明updateNotificationTranslucency();updatePanelExpanded();updateGestureExclusionRect();if (DEBUG) {mView.invalidate();}}

到这里了就一起看个滑动解锁的堆栈:

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)

再看一个滑动后到密码安全锁(即密码解锁)的堆栈

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

    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

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  = falseif (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()

@Overridepublic 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() {@Overridepublic void run() {if (mPanelFraction == 0.0f) {mBar.makeExpandedInvisible();}}};

接下来执行 StatusBar 中的 makeExpandedInvisible() 方法,更新通知栏和状态栏窗口的可见性。
StatusBar#makeExpandedInvisible()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.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 visiblemGutsManager.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

    private void showBouncerIfKeyguard() {if ((mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)&& !mKeyguardViewMediator.isHiding()) {// !mKeyguardViewMediator.isHiding() 不管是滑动解锁还是PIN码解锁等方式都是 truemStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */);}}

StatusBarKeyguardViewManager#showBouncer()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.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

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

    // 显示安全锁public boolean dismiss(int targetUserId) {return dismiss(false, targetUserId, false);}@Overridepublic 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

    // 显示下一个安全屏幕(如果有)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-833BouncerUiEvent 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,没有返回 trueif (finish) {// 重点关注mSecurityCallback.finish(strongAuth, targetUserId);// 省略部分代码......}return finish;}

KeyguardHostView#finish()
frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java

    @Overridepublic 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 上面设置成了 falsemViewMediatorCallback.keyguardDonePending(strongAuth, targetUserId);} else {// 重点关注mViewMediatorCallback.keyguardDone(strongAuth, targetUserId);}}}

KeyguardViewMediator#keyguardDone()
frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java

        @Overridepublic 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);    // 启动预隐藏动画}}

http://www.ppmy.cn/server/131667.html

相关文章

【spring ai】java 实现RAG检索增强,超快速入门

rag 需求产生的背景介绍&#xff1a; 在使用大模型时&#xff0c;一个常见的问题是模型会产生幻觉&#xff08;即生成的内容与事实不符&#xff09;&#xff0c;同时由于缺乏企业内部数据的支持&#xff0c;导致其回答往往不够精准和具体&#xff0c;偏向于泛泛而谈。这些问题…

单体到微服务架构服务演化过程

单体到微服务架构服务演化过程 架构服务化 聊聊从单体到微服务架构服务演化过程 单体分层架构 在 Web 应用程序发展的早期&#xff0c;大部分工程是将所有的服务端功能模块打包到单个巨石型&#xff08;Monolith&#xff09;应用中&#xff0c;譬如很多企业的 Java 应用程序…

现代数字信号处理I-P3 MVUE学习笔记

目录 1. 参数估计问题的提出与本质 2. 估计的性质 2.1 Ancillary&#xff08;多余估计&#xff09; 例1&#xff0c;Ancillary估计量 2. Uniformly Optimal 3. Sufficiency充分性 3.1 统计量充分性定义 例2&#xff1a;利用充分统计量定义获取伯努利分布的充分统计量 …

File IO

Unix标准为程序员提供了一系列的通用IO(无缓冲IO)接口以实现文件读写操作&#xff0c;原书第三章主要所涉及到的系统接口主要针对普通文件为操作目标。通用IO相比于标准IO的最大的区别在于没有缓冲机制&#xff0c;只要调用一次标准IO&#xff0c;就进行IO&#xff0c;而不是像…

finebi面试题精选

‌‌FineBI‌是由‌帆软软件有限公司开发的一款商业智能&#xff08;Business Intelligence&#xff09;产品&#xff0c;旨在通过最终业务用户自主分析企业已有的信息化数据&#xff0c;帮助企业发现并解决存在的问题&#xff0c;及时调整策略以做出更好的决策&#xff0c;从而…

Adobe Photoshop Lightroom(图像处理软件) v8.0 中文版

Adobe Photoshop Lightroom 是由 Adobe 公司推出的一个专业的图像处理软件&#xff0c;主要用于数字摄影后期处理。Lightroom 与 Adobe Photoshop 不同&#xff0c;它是专门为数字摄影而设计的软件&#xff0c;注重于整个摄影工作流程的管理和处理&#xff0c;能够帮助摄影师更…

Go语言学习第一章

1、搭建Go开发环境-安装和配置SDK 基本介绍&#xff1a; SDK的全称&#xff08;Software Development Kit软件开发工具包&#xff09; SDK是提供给开发人员使用的&#xff0c;其中包含了对应开发语言的工具包 2、SDK下载 Go语言的官网为&#xff1a;golang.org&#xff0c;无法…

关于京东商品数据采集||附电商API接口采集文档

京东商品数据采集可以通过多种方法实现&#xff0c;包括使用自动化工具、编写爬虫程序、利用API接口等。以下是一些常见的采集方法和注意事项&#xff1a; 1. **使用自动化工具**&#xff1a;可以使用自动化工具进行数据采集。这些工具通常提供图形化界面&#xff0c;用户可以…