美文网首页Android开发systemui
Android8.1 SystemUI Keyguard之滑动解

Android8.1 SystemUI Keyguard之滑动解

作者: 汪和呆喵 | 来源:发表于2018-09-12 15:42 被阅读0次

    我们理解Keyguard的解锁流程主要从锁屏的界面Layout结构、touchEvent事件分发、解锁动作逻辑几个方面进行源码的分析

    锁屏的界面Layout结构分析

    StatusbarWindowView

    整个锁屏界面的顶级View就是mStatusBarWindow
    src/com/android/systemui/statusbar/phone/StatusBar.java

        public void createAndAddWindows() {
            addStatusBarWindow();
        }
    
        private void addStatusBarWindow() {
            makeStatusBarView();
            mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
            mRemoteInputController = new RemoteInputController(mHeadsUpManager);
            mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
            // add by hai.qin for story lock
            if (mLockScreenManager != null) {
                mLockScreenManager.setStatusBarWindowManager(mStatusBarWindowManager);
            }
            //
        }
    

    src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java

        /**
         * Adds the status bar view to the window manager.
         *
         * @param statusBarView The view to add.
         * @param barHeight The height of the status bar in collapsed state.
         */
        public void add(View statusBarView, int barHeight) {
    
            // 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,
                    barHeight,
                    WindowManager.LayoutParams.TYPE_STATUS_BAR,
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                            | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                            | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                            | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                            | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
                    PixelFormat.TRANSLUCENT);
            mLp.token = new Binder();
            mLp.gravity = Gravity.TOP;
            mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
            mLp.setTitle("StatusBar");
            mLp.packageName = mContext.getPackageName();
            mStatusBarView = statusBarView;
            mBarHeight = barHeight;
            mWindowManager.addView(mStatusBarView, mLp);
            mLpChanged = new WindowManager.LayoutParams();
            mLpChanged.copyFrom(mLp);
        }
    

    mStatusBarWindow是在StatusBar的create流程中调用WindowManager.addView()添加到窗口上的, 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(安全锁界面)
    

    上划后显示的安全锁界面是KeyguardBouncer,但keyguardbouncer并没有写在super_status_bar的layout文件里面,那么他是在什么时候添加的呢?

    KeyguarBouncer何时创建

    src/com/android/systemui/statusbar/phone/StatusBar.java
    -->start()-->startKeyguard()

     protected void startKeyguard() {
     ...
            mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
                    getBouncerContainer(), mScrimController,
                    mFingerprintUnlockController);
     ...
     }
    

    src/com/android/systemui/keyguard/KeyguardViewMediator.java
    -->registerStatusBar()
    src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
    -->registerStatusBar()

        public void registerStatusBar(StatusBar statusBar,
                ViewGroup container,
                ScrimController scrimController,
                FingerprintUnlockController fingerprintUnlockController,
                DismissCallbackRegistry dismissCallbackRegistry) {
            mStatusBar = statusBar;
            mContainer = container;
            mScrimController = scrimController;
            mFingerprintUnlockController = fingerprintUnlockController;
            mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
                    mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry);
        }
    

    那么这里的container是什么?

        protected ViewGroup getBouncerContainer() {
            return mStatusBarWindow;
        }
    

    是什么时候把keyguard_host_view加入到mStatuBarWindow的?

    src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
    -->show()-->ensureView()-->inflateView()

        protected void inflateView() {
            removeView();
            mHandler.removeCallbacks(mRemoveViewRunnable);
            mRoot = (ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.keyguard_bouncer, null);
            mKeyguardView = mRoot.findViewById(R.id.keyguard_host_view);
            mKeyguardView.setLockPatternUtils(mLockPatternUtils);
            mKeyguardView.setViewMediatorCallback(mCallback);
            mContainer.addView(mRoot, mContainer.getChildCount());
            mRoot.setVisibility(View.INVISIBLE);
    
            final WindowInsets rootInsets = mRoot.getRootWindowInsets();
            if (rootInsets != null) {
                mRoot.dispatchApplyWindowInsets(rootInsets);
            }
        }
    

    不同类型的安全锁是怎么放入keyguard_host_view的?

    src/com/android/keyguard/KeyguardSecurityContainer.java

        private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
            final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
            KeyguardSecurityView view = null;
            final int children = mSecurityViewFlipper.getChildCount();
            for (int child = 0; child < children; child++) {
                if (mSecurityViewFlipper.getChildAt(child).getId() == securityViewIdForMode) {
                    view = ((KeyguardSecurityView)mSecurityViewFlipper.getChildAt(child));
                    break;
                }
            }
            int layoutId = getLayoutIdFor(securityMode);
            if (view == null && layoutId != 0) {
                final LayoutInflater inflater = LayoutInflater.from(mContext);
                if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
                View v = inflater.inflate(layoutId, mSecurityViewFlipper, false);
                mSecurityViewFlipper.addView(v);
                updateSecurityView(v);
                view = (KeyguardSecurityView)v;
            }
    
            return view;
        }
    

    每次获取securityview的时候 先判断是否在viewflippter里存在该id
    没有的话 inflate一个新的同时addview到viewflippter里面

    在显示的时候调用showSecurityScreen(SecurityMode securityMode)

        private void showSecurityScreen(SecurityMode securityMode) {
        ...
    
            // Find and show this child.
            final int childCount = mSecurityViewFlipper.getChildCount();
    
            final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
            for (int i = 0; i < childCount; i++) {
                if (mSecurityViewFlipper.getChildAt(i).getId() == securityViewIdForMode) {
                    mSecurityViewFlipper.setDisplayedChild(i);
                    break;
                }
            }
        ...
        }
    

    会根据securitymode选择viewflipper中对应的child进行显示

    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

    在上滑过程中,不断调用PanelView.java的setExpandedHeightInternal()->notifyBarPanelExpansionChanged()-->PanelBar.java的notifyBarPanelExpansionChanged()
    src/com/android/systemui/statusbar/phone/PanelBar.java

        public void panelExpansionChanged(float frac, boolean expanded) {
            Log.d("WANG", "panelExpansionChanged frac=" + frac + " expaned=" + expanded );
            boolean fullyClosed = true;
            boolean fullyOpened = false;
            if (SPEW) LOG("panelExpansionChanged: start state=%d", mState);
            PanelView pv = mPanel;
            pv.setVisibility(expanded ? VISIBLE : INVISIBLE);
            // adjust any other panels that may be partially visible
            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;
            }
            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":"");
        }
    

    达到阈值时,expanded == false fullyClosed == true
    调用onPanelCollapsed()->调用子类PhoneStatubarView.java的onPanelCollapsed()
    src/com/android/systemui/statusbar/phone/PhoneStatusBarView.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();
                }
            }
        };
    

    解锁动作逻辑

    整个解锁过程分为两个部分:1. 隐藏notification_panel 2. 展示keyguard_bouncer或直接解锁

    src/com/android/systemui/statusbar/phone/StatusBar.java

        void makeExpandedInvisible() {
            if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible
                    + " mExpandedVisible=" + mExpandedVisible);
    
            if (!mExpandedVisible || mStatusBarWindow == null) {
                return;
            }
    
            // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
            mStatusBarView.collapsePanel(/*animate=*/ false, false /* delayed*/,
                    1.0f /* speedUpFactor */);
    
            mNotificationPanel.closeQs();
    
            mExpandedVisible = false;
            visibilityChanged(false);
    
            // Shrink the window to the size of the status bar only
            mStatusBarWindowManager.setPanelVisible(false);
            mStatusBarWindowManager.setForceStatusBarVisible(false);
    
            // Close any guts that might be visible
            closeAndSaveGuts(true /* removeLeavebehind */, true /* force */, true /* removeControls */,
                    -1 /* x */, -1 /* y */, true /* resetMenu */);
    
            runPostCollapseRunnables();
            setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
            showBouncerIfKeyguard();
            recomputeDisableFlags(mNotificationPanel.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);
            }
        }
    

    mStatusBarWindowManager.setPanelVisible(false);
    调用WindowManager更改为StatusBarWindow的高度, 只保留状态栏高度
    mStatusBarWindowManager.setForceStatusBarVisible(false);
    调用WindowManager使状态栏不可见
    showBouncerIfKeyguard()->showBouncer()最终调用到StatusBarKeyguardViewManager的dismiss()->showBouncer()方法

    src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java

        private void showBouncer() {
            if (mShowing) {
                mBouncer.show(false /* resetSecuritySelection */);
            }
            updateStates();
        }
    

    src/com/android/systemui/statusbar/phone/KeyguardBouncer.java

        public void show(boolean resetSecuritySelection) {
            ...
            // If allowed, try to dismiss the Keyguard. If no security auth (password/pin/pattern) is
            // set, this will dismiss the whole Keyguard. Otherwise, show the bouncer.
            if (allowDismissKeyguard && mKeyguardView.dismiss(activeUserId)) {
                return;
            }
            ...
        }
    

    在没有安全锁的情况下,会回调KeyguardHostView的finish方法

        @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) {
                    mViewMediatorCallback.keyguardDonePending(strongAuth, targetUserId);
                } else {
                    mViewMediatorCallback.keyguardDone(strongAuth, targetUserId);
                }
            }
        }
    

    mViewMediatorCallback定义在KeyguardViewMediator中

      @Override
            public void keyguardDone(boolean strongAuth, int targetUserId) {
                if (targetUserId != ActivityManager.getCurrentUser()) {
                    return;
                }
    
                tryKeyguardDone();
            }
    

    一系列调用来到解锁的核心代码
    mKeyguardGoingAwayRunnable.run();

        private final Runnable mKeyguardGoingAwayRunnable = new Runnable() {
            @Override
            public void run() {
                Trace.beginSection("KeyguardViewMediator.mKeyGuardGoingAwayRunnable");
                if (DEBUG) Log.d(TAG, "keyguardGoingAway");
                //Modified for MYOS begin
    
                try {
                    mStatusBarKeyguardViewManager.keyguardGoingAway();
    
                    int flags = 0;
                    if (mStatusBarKeyguardViewManager.shouldDisableWindowAnimationsForUnlock()
                            || mWakeAndUnlocking || mAniSpeedup) {
                        flags |= WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
                    }
                    if (mStatusBarKeyguardViewManager.isGoingToNotificationShade()) {
                        flags |= WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
                    }
                    if (mStatusBarKeyguardViewManager.isUnlockWithWallpaper()) {
                        flags |= WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
                    }
    
                    mUpdateMonitor.setKeyguardGoingAway(true /* goingAway */);
                    // Don't actually hide the Keyguard at the moment, wait for window
                    // manager until it tells us it's safe to do so with
                    // startKeyguardExitAnimation.
                    ActivityManager.getService().keyguardGoingAway(flags);
                } catch (RemoteException e) {
                    Log.e(TAG, "Error while calling WindowManager", e);
                }
    
                //Modified for MYOS end
                Trace.endSection();
            }
        };
    

    解锁过程的核心实质上是锁屏启动了一个runnable,
    通知AMS和WMS显示锁屏下方的activity组件窗口以及调用该activity组件的生命周期

        void keyguardGoingAway(int flags) {
            if (!mKeyguardShowing) {
                return;
            }
            Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway");
            mWindowManager.deferSurfaceLayout();
            try {
                setKeyguardGoingAway(true);
                mWindowManager.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY,
                        false /* alwaysKeepCurrent */, convertTransitFlags(flags),
                        false /* forceOverride */);
                updateKeyguardSleepToken();
    
                // Some stack visibility might change (e.g. docked stack)
                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
                mStackSupervisor.addStartingWindowsForVisibleActivities(true /* taskSwitch */);
                mWindowManager.executeAppTransition();
            } finally {
                Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway: surfaceLayout");
                mWindowManager.continueSurfaceLayout();
                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
            }
        }
    

    在系统准备解锁完成后,PhoneWindowManager回调KeyguardService的startKeyguardExitAnimation

      private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration) {
          Trace.beginSection("KeyguardViewMediator#handleStartKeyguardExitAnimation");
          if (DEBUG) Log.d(TAG, "handleStartKeyguardExitAnimation startTime=" + startTime
                  + " fadeoutDuration=" + fadeoutDuration);
          synchronized (KeyguardViewMediator.this) {
    
              if (!mHiding) {
                  return;
              }
              mHiding = false;
    
              if (mWakeAndUnlocking && mDrawnCallback != null) {
    
                  // Hack level over 9000: To speed up wake-and-unlock sequence, force it to report
                  // the next draw from here so we don't have to wait for window manager to signal
                  // this to our ViewRootImpl.
                  mStatusBarKeyguardViewManager.getViewRootImpl().setReportNextDraw();
                  notifyDrawn(mDrawnCallback);
                  mDrawnCallback = null;
              }
    
              // only play "unlock" noises if not on a call (since the incall UI
              // disables the keyguard)
              if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)) {
                  playSounds(false);
              }
    
              mWakeAndUnlocking = false;
              setShowingLocked(false);
              mDismissCallbackRegistry.notifyDismissSucceeded();
              mStatusBarKeyguardViewManager.hide(startTime, fadeoutDuration);
              resetKeyguardDonePendingLocked();
              mHideAnimationRun = false;
              adjustStatusBarLocked();
              sendUserPresentBroadcast();
              mUpdateMonitor.setKeyguardGoingAway(false /* goingAway */);
    
              // ADD FOR FINGERPRINT SHOT BEGIN
              mFingerPrintManager.notifyFpService(1, null);
              // ADD FOR FINGERPRINT SHOT END
          }
          Trace.endSection();
      }
    

    播放解锁声音、设置StatusBar的flag、发出ACTION_USER_PRESENT广播、隐藏KeyguardView,解锁流程结束

    推荐文章

    Android View的事件分发机制探索

    解锁的framework流程及动画

    相关文章

      网友评论

        本文标题:Android8.1 SystemUI Keyguard之滑动解

        本文链接:https://www.haomeiwen.com/subject/hlpawftx.html