Android 10.0 滑动解锁流程

前言

滑动解锁相对于来说逻辑还是简单的,说白了就是对事件的处理,然后做一些事。
这里主要从锁屏的界面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.mzph.cn/news/882262.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[MySQL课后作业]人事管理系统的SQL实践

1.假设某商业集团中有若干公司&#xff0c;人事数据库中有3个基本表 职工表:EMP(E#.ENAME,AGE, SEX, ECITY)。 其属性分别表示职工工号、姓名、年龄、性别和居住城市。 工作表:WORKS(E#,C#,SALARY)。其属性分别表示职工工号、所在公司的编号和工资。 公司表:COMP(C#,CANME,CITY…

【优选算法篇】踏入算法的深邃乐章:滑动窗口的极致探秘

文章目录 C 滑动窗口详解&#xff1a;进阶题解与思维分析前言第二章&#xff1a;进阶挑战2.1 水果成篮解法一&#xff1a;滑动窗口解法二&#xff1a;滑动窗口 数组模拟哈希表复杂度分析&#xff1a;图解分析&#xff1a;示例&#xff1a;滑动窗口执行过程图解&#xff1a; 详…

C for Graphic:径向模糊

最近要做一系列特效需求&#xff0c;顺便记录一下。 径向模糊&#xff08;也叫辐射模糊&#xff09;&#xff1a;一种由内向外发散的模糊的效果 原理&#xff1a;获取中心点&#xff08;centeruv&#xff09;到当前像素&#xff08;pixeluv&#xff09;的朝向法向…

RFC2616 超文本传输协议 HTTP/1.1

一、URL-俗称“网址” HTTP 使用 URL(Uniform Resource Locator&#xff0c;统一资源定位符)来定位资源&#xff0c;它是 URI(Uniform Resource Identifier&#xff0c;统一资源标识符)的子集&#xff0c;URL 在 URI 的基础上增加了定位能力 URI 除了包含 URL&#xff0c;还包…

车载实操:一对一实操学习、CANoe实操学习、推荐就业机会、就业技术支持、协助面试辅导

FOTA模块中OTA的知识点&#xff1a;1.测试过程中发现哪几类问题&#xff1f; 可能就是一个单键的ecu&#xff0c;比如升了一个门的ecu&#xff0c;他的升了之后就关不上&#xff0c;还有就是升级组合ecu的时候&#xff0c;c屏上不显示进度条。 2.在做ota测试的过程中&#xff…

5G NR:UE初始接入信令流程浅介

UE初始接入信令流程 流程说明 用户设备&#xff08;UE&#xff09;向gNB-DU发送RRCSetupRequest消息。gNB-DU 包含 RRC 消息&#xff0c;如果 UE 被接纳&#xff0c;则在 INITIAL UL RRC MESSAGE TRANSFER 消息中包括为 UE 分配的低层配置&#xff0c;并将其传输到 gNB-CU。IN…

PFC和LLC的本质和为什么要用PFC和LLC电路原因

我们可以用电感和电容的特性,以及电压和电流之间的不同步原理来解释PFC(功率因数校正)和LLC(谐振变换器)。 电感和电容的基本概念 电感(Inductor): 电感是一种储存电能的组件。它的电流变化比较慢,电流在电感中延迟,而电压变化得比较快。可以把电感想象成一个“滞后…

『Mysql集群』Mysql高可用集群之主从复制 (一)

Mysql主从复制模式 主从复制有一主一从、主主复制、一主多从、多主一从等多种模式. 我们可以根据它们的优缺点选择适合自身企业情况的主从复制模式进行搭建 . 一主一从 主主复制 (互为主从模式): 实现Mysql多活部署 一主多从: 提高整个集群的读能力 多主一从: 提高整个集群的…

transformers 推理 Qwen2.5 等大模型技术细节详解(一)transformers 初始化和对象加载(文末免费送书)

上周收到一位网友的私信&#xff0c;希望老牛同学写一篇有关使用 transformers 框架推理大模型的技术细节的文章。 老牛同学刚开始以为这类的文章网上应该会有很多&#xff0c;于是想着百度几篇质量稍高一点的回复这位网友。结果&#xff0c;老牛同学搜索后发现&#xff0c;类…

信息与计算科学:“数学 + 计算机”,奏响未来科技新乐章

在当今科技飞速发展的时代&#xff0c;有一个专业如同一颗闪耀的新星&#xff0c;散发着独特的魅力&#xff0c;那就是信息与计算科学专业。 一、专业全貌&#xff1a;追根溯源&#xff0c;领略交叉之美 &#xff08;一&#xff09;专业的诞生与发展 1998 年&#xff0c;教育…

一图解千言,了解常见的流程图类型及其作用

在企业管理、软件研发过程中&#xff0c;经常会需要进行各种业务流程梳理&#xff0c;而流程图就是梳理业务时必要的手段&#xff0c;同时也是梳理的产出。但在不同的情况下适用的流程图又不尽相同。 本文我们就一起来总结一下8 种最常见的流程图类型 数据流程图 数据流程图&…

RHCE——例行性工作

准备工作 [rootlocalhost ~]# cat /etc/yum.repos.d/aliyun.repo [ali-app] nameali-app baseurlhttps://mirrors.aliyun.com/centos-stream/9-stream/AppStream/x86_64/os/ gpgcheck0[ali-base] nameali-base baseurlhttps://mirrors.aliyun.com/centos-stream/9-stream/Base…

JS | JS中类的 prototype 属性和__proto__属性

大多数浏览器的 ES5 实现之中&#xff0c;每一个对象都有__proto__属性&#xff0c;指向对应的构造函数的prototype属性。Class 作为构造函数的语法糖&#xff0c;同时有prototype属性和__proto__属性&#xff0c;因此同时存在两条继承链。 构造函数的子类有prototype属性。‌ …

倍福中控显示屏维修控制面板CP7732-1207-0030

使用的环境条件不当可能会损坏设备。 保护设备&#xff0c;防止灰尘、湿气和热量进入。 使用注意事项&#xff1a; 空气流通不畅 设备安装不正确会阻碍设备内的空气流通&#xff0c;从而导致过热和功能受损。 只能按所示方向将设备安装在相应的壁上。 该设备设计用于安装在…

05 P1157 组合的输出

题目&#xff1a; 代码&#xff1a; #include<iostream> using namespace std; # define M 500 #include<algorithm>int sa[100005],k,n,count1;bool func(int n) {int mark0;if(n1){return 1;}else{for(int i2;i<n-1;i){if(n%i0){mark1;return 0;}}if(mark0)r…

强化学习案例:美团是如何在推荐系统中落地强化学习

目录 美团的强化学习应用场景和分析 场景举例 使用原因 强化学习的六大要素 智能体 环境 行动 奖励 目标 状态 美团强化学习模型设计 美团强化学习工程落地 总体的数据结构关系图 实现步骤 1. 日志收集与实时处理&#xff08;Log Collector, Online Joiner&…

PyTorch 2.5 发布带来一些新特性和改进

官网&#xff1a;https://github.com/pytorch/pytorchGitHub&#xff1a;https://github.com/pytorch/pytorch原文&#xff1a;https://github.com/pytorch/pytorch/releases/tag/v2.5.0 主要亮点 (Highlights)] SDPA CuDNN 后端&#xff1a;为 torch.nn.functional.scaled_d…

C++标准模板库--vector

vector 介绍 vector&#xff08;向量&#xff09;是一种序列容器&#xff0c;表示为可以改变大小的数组。vector中的元素使用连续的存储位置&#xff0c;这意味着也可以使用指向其元素的常规指针偏移量来访问任意元素&#xff0c;且与数组一样高效。但与数组不同的是&#xff…

React Componet类组件详解(老项目)

React类组件是通过创建class继承React.Component来创建的&#xff0c;是React中用于构建用户界面的重要部分。以下是对React类组件的详细解释&#xff1a; 一、定义与基本结构 类组件使用ES6的class语法定义&#xff0c;并继承自React.Component。它们具有更复杂的功能&#…

流量PID控制(开度前馈量计算+辅助PID)

和流体流速(瞬时流量)相关的计算请参考下面文章链接: 1、PLC通过伯努利方程近似计算水箱流量 PLC通过伯努利方程近似计算水箱流量(FC)-CSDN博客文章浏览阅读1.6k次。本文介绍了如何使用PLC通过伯努利方程近似计算水箱中的液体流量,主要涉及流量计算、模型验证、梯形图编程及…