Android 10.0 滑动解锁流程

前言

滑动解锁相对于来说逻辑还是简单的,说白了就是对事件的处理,然后做一些事。

这里主要从锁屏的界面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。

  1. 硬件发出指令:按下,移动,抬起
  2. input接收
  3. 代码执行相应操作: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);    // 启动预隐藏动画
        }
    }
相关推荐
Asin²+cos²=18 小时前
关于Android Studio Koala Feature Drop | 2024.1.2下载不了插件的解决办法
android·ide·android studio
大耳猫9 小时前
Android gradle和maven国内镜像地址
android·gradle·maven
-seventy-11 小时前
Android 玩机知识储备
android
CYRUS STUDIO11 小时前
frida脚本,自动化寻址JNI方法
android·运维·自动化·逆向·移动安全·jni·frida
暮志未晚Webgl12 小时前
102. UE5 GAS RPG 实现范围技能奥术伤害
android·java·ue5
Patience to do12 小时前
Android Studio项目(算法计算器)
android·算法·android studio
我又来搬代码了16 小时前
【Android】使用TextView实现按钮开关代替Switch开关
android
江-月*夜18 小时前
uniapp vuex 搭建
android·javascript·uni-app
大风起兮云飞扬丶19 小时前
Android——显式/隐式Intent
android
大风起兮云飞扬丶19 小时前
Android——metaData
android