1. 程式人生 > >WMS->視窗管理statusbar和navbar管理

WMS->視窗管理statusbar和navbar管理

要想讀懂窗口布局管理,必須瞭解status bar 和nav bar ,要了解這兩個bar 更要了解相關的flags,下面就會這些flags做一些說明

View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR  //高亮狀態列

View.STATUS_BAR_TRANSLUCENT //半透明狀態列
View.STATUS_BAR_TRANSPARENT //透明狀態列,一般指定WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS系統會設定成全透明

View.SYSTEM_UI_FLAG_FULLSCREEN // 全屏顯示,隱藏狀態列和狀態列
View.STATUS_BAR_UNHIDE     // 顯示狀態列,用於傳遞給systemui處理 View.NAVIGATION_BAR_TRANSPARENT //半透明導航欄 View.NAVIGATION_BAR_TRANSLUCENT //透明導航欄,一般指定WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS系統會設定成全透明 View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // 隱藏導航欄 View.NAVIGATION_BAR_UNHIDE      // 顯示狀態列,傳遞給systemui處理 View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY //身臨其境的感覺,和SYSTEM_UI_FLAG_LIGHT_STATUS_BAR一起使用會隱藏狀態列和導航欄,從上面/下面滑出狀態列和導航欄,過幾秒自動消失
View.SYSTEM_UI_FLAG_IMMERSIVE //身臨其境的感覺,自動隱藏狀態列和導航欄,出上部/下部滑動狀態列出現,不自動隱藏 View.STATUS_BAR_TRANSIENT  //進入瞬態,狀態列出來和隱藏的過程 View.NAVIGATION_BAR_TRANSIENT //進入瞬態,導航欄出來和隱藏的過程 WindowManager.LayoutParams.FLAG_FULLSCREEN //全屏顯示,隱藏狀態列 WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS //導航欄狀態列透明,客戶端渲染狀態列導航欄背景
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND //強制導航欄狀態列透明,客戶端渲染狀態列導航欄背景 WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR  //當此視窗到達頂部的時候保持前一個視窗的透明狀態 WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS //指定半透明status bar WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION //指定半透明nav bar WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND 強制渲染背景色 下面這幾個狀態用於status bar 和nav bar 切換時候的瞬態過程 private static final int TRANSIENT_BAR_NONE = 0; //無任何狀態,當隱藏完成時候設定 private static final int TRANSIENT_BAR_SHOW_REQUESTED = 1; // 請求顯示 private static final int TRANSIENT_BAR_SHOWING = 2; //正在顯示的過程 private static final int TRANSIENT_BAR_HIDING = 3; //正在隱藏的過程,隱藏完成window變成不可見,設定TRANSIENT_BAR_NONE 還要記住,分屏模式不允許隱藏bar

另外管理的還要注意幾個狀態,包括狀態列展開,鎖屏和鎖屏被阻斷,以及正常的全屏activity在前臺的幾種情況。
狀態列管理在wms這端的核心函式是PhoneWindowManager中的updateSystemUiVisibilityLw()函式

private int updateSystemUiVisibilityLw() {
        // If there is no window focused, there will be nobody to handle the events
        // anyway, so just hang on in whatever state we're in until things settle down.
        //1 獲取焦點的window也就是能接收input事件的window
        WindowState winCandidate = mFocusedWindow != null ? mFocusedWindow
                : mTopFullscreenOpaqueWindowState;
        if (winCandidate == null) {
            return 0;
        }
        //2 如果當前焦點的window是ImmersiveModeConfirmation彈出的window不做處理,直接返回
        if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) {
            // The immersive mode confirmation should never affect the system bar visibility,
            // otherwise it will unhide the navigation bar and hide itself.
            winCandidate = isStatusBarKeyguard() ? mStatusBar : mTopFullscreenOpaqueWindowState;
            if (winCandidate == null) {
                return 0;
            }
        }
        final WindowState win = winCandidate;
        //3 焦點window是keygurd,但是keygurd被阻斷,不處理直接返回
        if ((win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 && mHideLockScreen == true) {
            // We are updating at a point where the keyguard has gotten
            // focus, but we were last in a state where the top window is
            // hiding it.  This is probably because the keyguard as been
            // shown while the top window was displayed, so we want to ignore
            // it here because this is just a very transient change and it
            // will quickly lose focus once it correctly gets hidden.
            return 0;
        }
        //4 獲取win的標誌,處理rest flags
        int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null)
                & ~mResettingSystemUiFlags
                & ~mForceClearedSystemUiFlags;
        if (mForcingShowNavBar && win.getSurfaceLayer() < mForcingShowNavBarLayer) {
            tmpVisibility &= ~PolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS);
        }
        //5 根據當前狀態處理SYSTEM_UI_FLAG_LIGHT_STATUS_BAR標誌(也就是status bar高亮狀態)
        final int fullscreenVisibility = updateLightStatusBarLw(0 /* vis */,
                mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
        //6 導航欄高亮狀態處理
        final int dockedVisibility = updateLightStatusBarLw(0 /* vis */,
                mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
        //7 獲取home stack和dock stack的大小
        mWindowManagerFuncs.getStackBounds(HOME_STACK_ID, mNonDockedStackBounds);
        mWindowManagerFuncs.getStackBounds(DOCKED_STACK_ID, mDockedStackBounds);
        //8 更新status bar 的屬性
        final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
        final int diff = visibility ^ mLastSystemUiFlags;
        final int fullscreenDiff = fullscreenVisibility ^ mLastFullscreenStackSysUiFlags;
        final int dockedDiff = dockedVisibility ^ mLastDockedStackSysUiFlags;
        //9 更新各屬性
        final boolean needsMenu = win.getNeedsMenuLw(mTopFullscreenOpaqueWindowState);
        if (diff == 0 && fullscreenDiff == 0 && dockedDiff == 0 && mLastFocusNeedsMenu == needsMenu
                && mFocusedApp == win.getAppToken()
                && mLastNonDockedStackBounds.equals(mNonDockedStackBounds)
                && mLastDockedStackBounds.equals(mDockedStackBounds)) {
            return 0;
        }
        mLastSystemUiFlags = visibility;
        mLastFullscreenStackSysUiFlags = fullscreenVisibility;
        mLastDockedStackSysUiFlags = dockedVisibility;
        mLastFocusNeedsMenu = needsMenu;
        mFocusedApp = win.getAppToken();
        final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds);
        final Rect dockedStackBounds = new Rect(mDockedStackBounds);
        //10請求StatusBarManager處理(最終請求status bar 處理)
        mHandler.post(new Runnable() {
                @Override
                public void run() {
                    StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
                    if (statusbar != null) {
                        statusbar.setSystemUiVisibility(visibility, fullscreenVisibility,
                                dockedVisibility, 0xffffffff, fullscreenStackBounds,
                                dockedStackBounds, win.toString());
                        statusbar.topAppWindowChanged(needsMenu);
                    }
                }
            });
        return diff;
    }

我們這裡對以上10個步驟進行解釋
1獲取候選的window,也就是接收事件的window,這裡可能有集中情況
(1)status bar 被展開或者keygurd的情況statusbar window是焦點的window
(2) 在分屏狀態下,接收事件的那個window
(3) 在沒有分屏狀態下全屏的activity就是焦點
對於這裡選擇的候選window,預設以焦點為主,沒有焦點時則使用mTopFullscreenOpaqueWindowState作為候選window,候選window用於使用它的flags處理系統bar的狀態。 如果沒有找到候選window 就直接返回。這裡解釋下mTopFullscreenOpaqueWindowState代表在full workspace stack上的activity
2 這裡處理的情況是當前的焦點window 是如下這個window
這裡寫圖片描述
當處於身臨其境的模式,也就是設定了View.SYSTEM_UI_FLAG_IMMERSIVE |View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY|View.SYSTEM_UI_FLAG_HIDE_NAVIGATION標誌的時候,防止使用者恐慌,提示如何劃出狀態列導航欄,這種狀態不需要處理狀態列屬性,直接等到使用者退出幫助頁面後再去操作
3 當有一個window設定了WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED標誌的時候這個介面會在鎖屏上面顯示,這時候mHideLockScreen為真,注意這個標誌在8.0裡面改了這變數名,其實是一樣的。這種情況馬上焦點window就不是keygurd了,這只是焦點視窗切換的中間狀態,這種情況直接不處理,等狀態變了後再去處理
4 PolicyControl.getSystemUiVisibility(win, null)函式我們暫時怎為是直接獲取到win的systemUiVisibility變數所描述的關於該window設定的systemui的一些flags。
另外對於mResettingSystemUiFlags的解釋,當設定了View.SYSTEM_UI_FLAG_HIDE_NAVIGATION|View.SYSTEM_UI_FLAG_FULLSCREEN這連個標誌而沒有設定IMMERSIVE相關標誌的時候,會隱藏掉狀態列和導航欄,這時候要求點選螢幕任意地方,狀態列和導航欄都出出來,這個功能的實現是建立一個全屏的事件消費者去接收螢幕點選事件,當用戶點選後這個事件消費者會處理事件,事件消費者註冊的地方是在PhoneWindowManager的beginLayoutLw()函式中,事件的處理是在PhoneWindowManager的內部類HideNavInputEventReceiver的onInputEvent()方法中,讀者不妨自己去讀一下,主要是通過給mResettingSystemUiFlags設定View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_FULLSCREEN 這三個標誌,使status bar 出現來實現的。這個過程我們最後用一個情景棧去分析.
5 第五步是最終要的一步,前面的步驟都,用於更新systemui的一些標誌,由於函式比較長,我們稍後分析.
6 在一些場景下要對高亮標誌進行修正,我們這裡只拿statusbar 作為例子說明

  private int updateLightStatusBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming) {
        WindowState statusColorWin = isStatusBarKeyguard() && !mHideLockScreen
                ? mStatusBar
                : opaqueOrDimming;

        if (statusColorWin != null) {
            if (statusColorWin == opaque) {
                // If the top fullscreen-or-dimming window is also the top fullscreen, respect
                // its light flag.
                vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
                vis |= PolicyControl.getSystemUiVisibility(statusColorWin, null)
                        & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
            } else if (statusColorWin != null && statusColorWin.isDimming()) {
                // Otherwise if it's dimming, clear the light flag.
                vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
            }
        }
        return vis;
    }

程式碼很簡單,首先選擇statusColorWin,規則是如果是keygurd且不被其他window覆蓋則選擇statusbar 作為statusColorWin,否則就是使用引數opaqueOrDimming(放在full stack中的), 當statusColorWin為opaque的window的時候(也就是full stack中不透明的window),這時候狀態列的高亮狀態由opaque window決定. 如果不是full stack中的opaque window 則由window自身的Dimming狀態決定
7 獲取Home stack 和docker stack的大小儲存在mNonDockedStackBounds,mDockedStackBounds變數中
8,9 更新我們計算好的systemui相關的屬性
10請求StatusBarManager處理(最終請求Systemui 處理)

這裡第五條和第十條我們分別展開說明
首先第五步驟更新systemuiFlags的過程,這個過程主要參考焦點window,mTopFullscreenOpaqueWindowState和mTopDockedOpaqueWindowState去計算bar的屬性

private int updateSystemBarsLw(WindowState win, int oldVis, int vis) {
        final boolean dockedStackVisible = mWindowManagerInternal.isStackVisible(DOCKED_STACK_ID);
        final boolean freeformStackVisible =
                mWindowManagerInternal.isStackVisible(FREEFORM_WORKSPACE_STACK_ID);
        final boolean resizing = mWindowManagerInternal.isDockedDividerResizing();

        // We need to force system bars when the docked stack is visible, when the freeform stack
        // is visible but also when we are resizing for the transitions when docked stack
        // visibility changes.
        //1 判斷是否強制顯示system bar
        mForceShowSystemBars = dockedStackVisible || freeformStackVisible || resizing;
        final boolean forceOpaqueStatusBar = mForceShowSystemBars && !mForceStatusBarFromKeyguard;

        //2 計算fullscreenTransWin用於計算bar 的透明情況
        // apply translucent bar vis flags
        WindowState fullscreenTransWin = isStatusBarKeyguard() && !mHideLockScreen
                ? mStatusBar
                : mTopFullscreenOpaqueWindowState;
        //3 計算狀態列透明flags
        vis = mStatusBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);
        //4 計算導航欄透明情況
        vis = mNavigationBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);
        //5 計算docker導航欄透明度
        final int dockedVis = mStatusBarController.applyTranslucentFlagLw(
                mTopDockedOpaqueWindowState, 0, 0);
    //6 根據mTopFullscreenOpaqueWindowState計算fullscreen stack的FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS標誌
        final boolean fullscreenDrawsStatusBarBackground =
                (drawsSystemBarBackground(mTopFullscreenOpaqueWindowState)
                        && (vis & View.STATUS_BAR_TRANSLUCENT) == 0)
                || forcesDrawStatusBarBackground(mTopFullscreenOpaqueWindowState);
       //7 根據dockedDrawsStatusBarBackground計算docker stack的FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS標誌
        final boolean dockedDrawsStatusBarBackground =
                (drawsSystemBarBackground(mTopDockedOpaqueWindowState)
                        && (dockedVis & View.STATUS_BAR_TRANSLUCENT) == 0)
                || forcesDrawStatusBarBackground(mTopDockedOpaqueWindowState);

        // prevent status bar interaction from clearing certain flags
        int type = win.getAttrs().type;
        boolean statusBarHasFocus = type == TYPE_STATUS_BAR;
        //8 根據status 是否展開清除一些屬性
        if (statusBarHasFocus && !isStatusBarKeyguard()) {
            int flags = View.SYSTEM_UI_FLAG_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_IMMERSIVE
                    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                    | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
            if (mHideLockScreen) {
                flags |= View.STATUS_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSLUCENT;
            }
            vis = (vis & ~flags) | (oldVis & flags);
        }
        //9 根據FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS設定一些屬性
        if (fullscreenDrawsStatusBarBackground && dockedDrawsStatusBarBackground) {
            vis |= View.STATUS_BAR_TRANSPARENT;
            vis &= ~View.STATUS_BAR_TRANSLUCENT;
        } else if ((!areTranslucentBarsAllowed() && fullscreenTransWin != mStatusBar)
                || forceOpaqueStatusBar) {
            vis &= ~(View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT);
        }
        //10 設定導航欄的透明度                                           
        vis = configureNavBarOpacity(vis, dockedStackVisible, freeformStackVisible, resizing);

        // update status bar
        boolean immersiveSticky =
                (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
        final boolean hideStatusBarWM =
                mTopFullscreenOpaqueWindowState != null
                && (PolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null)
                        & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
        final boolean hideStatusBarSysui =
                (vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
        final boolean hideNavBarSysui =
                (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;

        final boolean transientStatusBarAllowed = mStatusBar != null
                && (statusBarHasFocus || (!mForceShowSystemBars
                        && (hideStatusBarWM || (hideStatusBarSysui && immersiveSticky))));

        final boolean transientNavBarAllowed = mNavigationBar != null
                && !mForceShowSystemBars && hideNavBarSysui && immersiveSticky;
        //11 根據使用者是否進入恐慌狀態顯示bar 
        final long now = SystemClock.uptimeMillis();
        final boolean pendingPanic = mPendingPanicGestureUptime != 0
                && now - mPendingPanicGestureUptime <= PANIC_GESTURE_EXPIRATION;
        if (pendingPanic && hideNavBarSysui && !isStatusBarKeyguard() && mKeyguardDrawComplete) {
            // The user performed the panic gesture recently, we're about to hide the bars,
            // we're no longer on the Keyguard and the screen is ready. We can now request the bars.
            mPendingPanicGestureUptime = 0;
            mStatusBarController.showTransient();
            if (!isNavBarEmpty(vis)) {
                mNavigationBarController.showTransient();
            }
        }

        final boolean denyTransientStatus = mStatusBarController.isTransientShowRequested()
                && !transientStatusBarAllowed && hideStatusBarSysui;
        final boolean denyTransientNav = mNavigationBarController.isTransientShowRequested()
                && !transientNavBarAllowed;
        //12 不能隱藏bar 設定flags
        if (denyTransientStatus || denyTransientNav || mForceShowSystemBars) {
            // clear the clearable flags instead
            clearClearableFlagsLw();
            vis &= ~View.SYSTEM_UI_CLEARABLE_FLAGS;
        }

        final boolean immersive = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0;
        immersiveSticky = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
        final boolean navAllowedHidden = immersive || immersiveSticky;
        //13 根據window型別顯示導航欄
        if (hideNavBarSysui && !navAllowedHidden && windowTypeToLayerLw(win.getBaseType())
                > windowTypeToLayerLw(TYPE_INPUT_CONSUMER)) {
            // We can't hide the navbar from this window otherwise the input consumer would not get
            // the input events.
            vis = (vis & ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
        }
        //14 更新status bar 可見性
        vis = mStatusBarController.updateVisibilityLw(transientStatusBarAllowed, oldVis, vis);

        // update navigation bar
        boolean oldImmersiveMode = isImmersiveMode(oldVis);
        boolean newImmersiveMode = isImmersiveMode(vis);
        //15 身臨其境狀態發生變化,處理
        if (win != null && oldImmersiveMode != newImmersiveMode) {
            final String pkg = win.getOwningPackage();
            mImmersiveModeConfirmation.immersiveModeChangedLw(pkg, newImmersiveMode,
                    isUserSetupComplete(), isNavBarEmpty(win.getSystemUiVisibility()));
        }
        //16 更系導航欄可見性
        vis = mNavigationBarController.updateVisibilityLw(transientNavBarAllowed, oldVis, vis);

        return vis;
    }

1 判斷是否強制顯示system bar ,當freedom stack和docker stack存在或者正在拖拽改變大小的情況,強制顯示system bar
2 計算fullscreenTransWin用於計算bar 的透明情況,這裡可以看出來總是以statusbar window 和mTopFullscreenOpaqueWindowState計算全域性的bar 狀態
3 使用fullscreenTransWin計算狀態列透明標誌

  public int applyTranslucentFlagLw(WindowState win, int vis, int oldVis) {
        if (mWin != null) {
            if (win != null && (win.getAttrs().privateFlags
                    & WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) == 0) {
                int fl = PolicyControl.getWindowFlags(win, null);
                if ((fl & mTranslucentWmFlag) != 0) {
                    vis |= mTranslucentFlag;
                } else {
                    vis &= ~mTranslucentFlag;
                }
                if ((fl & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) {
                    vis |= mTransparentFlag;
                } else {
                    vis &= ~mTransparentFlag;
                }
            } else {
                vis = (vis & ~mTranslucentFlag) | (oldVis & mTranslucentFlag);
                vis = (vis & ~mTransparentFlag) | (oldVis & mTransparentFlag);
            }
        }
        return vis;
    }

這裡如果PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR標誌被設定則繼承之前的標誌,對於status bar 這裡主要根據WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS設定View.STATUS_BAR_TRANSLUCENT(看來是為了相容老版本) ,根據WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS設定View.STATUS_BAR_TRANSPARENT狀態. 一個代表半透明,一個代表透明.

4 計算導航欄透明情況,這裡和3是很相似的就不去介紹了
5 計算docker導航欄透明度
6 根據mTopFullscreenOpaqueWindowState 和FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS標誌計算fullscreen stack是否允許客戶端自己繪製狀態列的顏色,這裡可以看出如果設定了狀態列半透明,是不允許客戶端繪製狀態列顏色的,注意客戶端如果設定了WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS就會設定View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN,這時候狀態列就應該隱藏,所以不需要使用客戶端渲染狀態列背景色. 除非客戶端強制渲染背景色,也就是PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND標誌成立
7 和6類似,只不過是計算docker stack的狀態
8 如果當前status bar 獲取焦點,要去掉一些標誌

 int type = win.getAttrs().type;
        boolean statusBarHasFocus = type == TYPE_STATUS_BAR;
        if (statusBarHasFocus && !isStatusBarKeyguard()) {
            int flags = View.SYSTEM_UI_FLAG_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_IMMERSIVE
                    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                    | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
            if (mHideLockScreen) {
                flags |= View.STATUS_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSLUCENT;
            }
            vis = (vis & ~flags) | (oldVis & flags);
        }

這裡可以看出,對於這些標誌不做更改,還是使用之前的標誌.所以status bar 設定這些標誌是不生效的.
9 在步驟6中計算出來docker stack和full stack是否需要使用客戶端渲染status bar 背景色,如果二者都需要客戶端渲染背景色,設定狀態列背景為透明的,去掉辦透明的設定,如果status bar 只能設定為不透明的話,其掉View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT標誌. 所以記住這裡View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT是有可能並存的
10 設定導航欄的透明度

    private int configureNavBarOpacity(int visibility, boolean dockedStackVisible,
            boolean freeformStackVisible, boolean isDockedDividerResizing) {
        if (mNavBarOpacityMode == NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED) {
            if (dockedStackVisible || freeformStackVisible || isDockedDividerResizing) {
                visibility = setNavBarOpaqueFlag(visibility);
            }
        } else if (mNavBarOpacityMode == NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE) {
            if (isDockedDividerResizing) {
                visibility = setNavBarOpaqueFlag(visibility);
            } else if (freeformStackVisible) {
                visibility = setNavBarTranslucentFlag(visibility);
            } else {
                visibility = setNavBarOpaqueFlag(visibility);
            }
        }

        if (!areTranslucentBarsAllowed()) {
            visibility &= ~View.NAVIGATION_BAR_TRANSLUCENT;
        }
        return visibility;
    }

這裡主要根據一些配置的屬性設定導航蘭透明情況
11 根據使用者是否進入恐慌狀態顯示bar

      boolean immersiveSticky =
                (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
        final boolean hideStatusBarWM =
                mTopFullscreenOpaqueWindowState != null
                && (PolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null)
                        & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
        final boolean hideStatusBarSysui =
                (vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
        final boolean hideNavBarSysui =
                (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;

        final boolean transientStatusBarAllowed = mStatusBar != null
                && (statusBarHasFocus || (!mForceShowSystemBars
                        && (hideStatusBarWM || (hideStatusBarSysui && immersiveSticky))));

        final boolean transientNavBarAllowed = mNavigationBar != null
                && !mForceShowSystemBars && hideNavBarSysui && immersiveSticky;

介紹11 之前先看看這些變數的含義. SYSTEM_UI_FLAG_IMMERSIVE_STICKY標誌的含義我們最開始已經看到了,hideStatusBarWM代表full stack中不透明的window是否設定了FLAG_FULLSCREEN標誌,用於隱藏狀態列
hideStatusBarSysui代表full stack中不透明的window是否設定了SYSTEM_UI_FLAG_FULLSCREEN,用於隱藏狀態列
hideNavBarSysui代表是否要隱藏導航欄目,通過View.SYSTEM_UI_FLAG_HIDE_NAVIGATION標誌確定
transientStatusBarAllowed代表status bar 是否允許進入到順態,也就是從隱藏到顯示的一個切換狀態. 這裡有一下幾種情況可以進入瞬態

  • 1 status bar 為焦點window
  • 2不強制顯示status bar 並且設定了WindowManager.LayoutParams.FLAG_FULLSCREEN標誌
  • 3不強制顯示status bar 並且設定了View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_FULLSCREEN

看來2和3標誌所產生的效果是一樣的
在來看11,當用戶5s內連續按power鍵,先熄滅螢幕,再點亮螢幕,有可能是由於狀態看和導航來都關閉了,引起使用者的恐慌,這裡如果發現是這種情況,使狀態列和導航欄進入舜態,從螢幕兩端移出來,showTransient()函式我們一會分析,這裡注意引起恐慌的還是應該為導航欄不可見,如果導航蘭可見也不會使狀態列進入舜態,另外11這種情況不會管transientStatusBarAllowed和transientNavBarAllowed條件是否成立

12 不能隱藏bar 設定flags

 final boolean denyTransientStatus = mStatusBarController.isTransientShowRequested()
                && !transientStatusBarAllowed && hideStatusBarSysui;
        final boolean denyTransientNav = mNavigationBarController.isTransientShowRequested()
                && !transientNavBarAllowed;

這裡前面計算了兩個變數,如果已經請求了進入瞬態,或者允許進入瞬態,這兩種情況都不為真,就不允許進入瞬態,就會設定denyTransientStatus,denyTransientNav,不能進入瞬態,要設定mResettingSystemUiFlags|=SYSTEM_UI_CLEARABLE_FLAGS,通知客戶端清除這些標誌,然後也清除我們要通知給systemui的這些標誌

13 不允許隱藏狀態列,清除View.SYSTEM_UI_FLAG_HIDE_NAVIGATION標誌
14 更新狀態列顯示狀態mStatusBarController.updateVisibilityLw(transientStatusBarAllowed, oldVis, vis);我們一會分析
15 View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY 標誌發生變化,使用 mImmersiveModeConfirmation.immersiveModeChangedLw(pkg, newImmersiveMode,
isUserSetupComplete(), isNavBarEmpty(win.getSystemUiVisibility()))更新狀態,mImmersiveModeConfirmation只是用於控制下面window的顯示
這裡寫圖片描述

16 mNavigationBarController.updateVisibilityLw(transientNavBarAllowed, oldVis, vis)更新狀態列顯示狀態

到這裡看完了全域性visible的更新,我們就來看下StatusBarController的使用,目前位置我們基本上遇到的就兩個函式
1mStatusBarController.showTransient(); 使請求status bar 進入瞬態
2 mStatusBarController.updateVisibilityLw(transientStatusBarAllowed, oldVis, vis) 更新顯示狀態

 public void showTransient() {
        if (mWin != null) {
            setTransientBarState(TRANSIENT_BAR_SHOW_REQUESTED);
        }
    }
       private void setTransientBarState(int state) {
        if (mWin != null && state != mTransientBarState) {
            if (mTransientBarState == TRANSIENT_BAR_SHOWING || state == TRANSIENT_BAR_SHOWING) {
                mLastTranslucent = SystemClock.uptimeMillis();
            }
            mTransientBarState = state;
            if (DEBUG) Slog.d(mTag, "mTransientBarState: " + transientBarStateToString(state));
        }
    }

函式很簡單,只是把mTransientBarState設定為TRANSIENT_BAR_SHOW_REQUESTED狀態,表示請求瞬態

再來看下updateVisibilityLw函式

 public int updateVisibilityLw(boolean transientAllowed, int oldVis, int vis) {
        //1 status bar 對應的window不存在,直接返回
        if (mWin == null) return vis;
        //2 需要進入舜態的處理
        if (isTransientShowing() || isTransientShowRequested()) { // transient bar requested
            if (transientAllowed) { //3 允許進入瞬態
                vis |= mTransientFlag; //4 設定View.STATUS_BAR_TRANSIENT狀態表示進入瞬態
                if ((oldVis & mTransientFlag) == 0) { 
                //5首次設定View.STATUS_BAR_TRANSIENT告訴systemui 
                //準備好了顯示,設定View.STATUS_BAR_UNHIDE標誌
                    vis |= mUnhideFlag;  // tell sysui we're ready to unhide
                }
                //6 更新為TRANSIENT_BAR_SHOWING狀態
                setTransientBarState(TRANSIENT_BAR_SHOWING);  // request accepted
            } else {
                //7 不允許進入瞬態,設定為TRANSIENT_BAR_NONE狀態
                setTransientBarState(TRANSIENT_BAR_NONE);  // request denied
            }
        }
        if (mShowTransparent) { //8 設定status bar 可見,注意這裡不一定是瞬態的
            vis |= mTransparentFlag;
            if (mSetUnHideFlagWhenNextTransparent) {
                vis |= mUnhideFlag;
                mSetUnHideFlagWhenNextTransparent = false;
            }
        }
        if (mTransientBarState != TRANSIENT_BAR_NONE) { //9 如果狀態不是NONE不允許LOW_PROFILE(淡化)模式顯示
            vis |= mTransientFlag;  // ignore clear requests until transition completes
            vis &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE;  // never show transient bars in low profile
        }
        //10 更新最後不是半透明的時間
        if ((vis & mTranslucentFlag) != 0 || (oldVis & mTranslucentFlag) != 0 ||
                ((vis | oldVis) & mTransparentFlag) != 0) {
            mLastTranslucent = SystemClock.uptimeMillis();
        }
        return vis;
    }

準備了這麼多標誌,最終都是要作用在客戶端和System ui上,我們這裡就看看如何通知給system ui

statusbar.setSystemUiVisibility(visibility, fullscreenVisibility,
dockedVisibility, 0xffffffff, fullscreenStackBounds,
dockedStackBounds, win.toString());
statusbar.topAppWindowChanged(needsMenu);
該方法在StatusBarManagerService中實現

    private void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
            Rect fullscreenBounds, Rect dockedBounds, String cause) {
        // also allows calls from window manager which is in this process.
        //1 檢查許可權
        enforceStatusBarService();

        if (SPEW) Slog.d(TAG, "setSystemUiVisibility(0x" + Integer.toHexString(vis) + ")");

        synchronized (mLock) {
            //2 更新SystemUI 
            updateUiVisibilityLocked(vis, fullscreenStackVis, dockedStackVis, mask,
                    fullscreenBounds, dockedBounds);
            //3 更新system ui disable 狀態(DISABLE_EXPAND | DISABLE_NOTIFICATION_ICONS
            //| DISABLE_NOTIFICATION_ALERTS | DISABLE_NOTIFICATION_TICKER
           // | DISABLE_SYSTEM_INFO | DISABLE_RECENT | DISABLE_HOME | DISABLE_BACK | DISABLE_CLOCK
           // | DISABLE_SEARCH;) 我們不關心這些不作分析
            disableLocked(
                    mCurrentUserId,
                    vis & StatusBarManager.DISABLE_MASK,
                    mSysUiVisToken,
                    cause, 1);
        }
    }
     private void updateUiVisibilityLocked(final int vis,
            final int fullscreenStackVis, final int dockedStackVis, final int mask,
            final Rect fullscreenBounds, final Rect dockedBounds) {
        //1 判斷狀態是否發生變化
        if (mSystemUiVisibility != vis
                || mFullscreenStackSysUiVisibility != fullscreenStackVis
                || mDockedStackSysUiVisibility != dockedStackVis
                || !mFullscreenStackBounds.equals(fullscreenBounds)
                || !mDockedStackBounds.equals(dockedBounds)) {
            //2 記錄最終狀態,方便下一次比較狀態是否發生變化
            mSystemUiVisibility = vis;
            mFullscreenStackSysUiVisibility = fullscreenStackVis;
            mDockedStackSysUiVisibility = dockedStackVis;
            mFullscreenStackBounds.set(fullscreenBounds);
            mDockedStackBounds.set(dockedBounds);
            mHandler.post(new Runnable() {
                    public void run() {
                        if (mBar != null) {
                            try {
                                //3 通知systemui 狀態變化
                                mBar.setSystemUiVisibility(vis, fullscreenStackVis, dockedStackVis,
                                        mask, fullscreenBounds, dockedBounds);
                            } catch (RemoteException ex) {
                            }
                        }
                    }
                });
        }
    }

函式有七個引數,其中
vis表示計算出來的全域性的bar的flags,fullscreenStackVis表示full stactk決定的SYSTEM_UI_FLAG_LIGHT_STATUS_BAR狀態
dockedStackVis則表示dock stack的SYSTEM_UI_FLAG_LIGHT_STATUS_BAR狀態
Rect fullscreenBounds, Rect dockedBounds分別表示full stack大小和docker stack大小
函式在上面程式碼的註釋中已經分析清楚,我們重點來看Systemui 中的處理

mBar.setSystemUiVisibility(vis, fullscreenStackVis, dockedStackVis,
                                        mask, fullscreenBounds, dockedBounds);

函式在SystemUI的PhoneStatusBar中,8.0這個類已經去掉,在哪我沒有看,這裡引數和StatusBarManagerService類中的同名函式幾乎是一致的,這裡不再詳細說明

 @Override // CommandQueue
    public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
            int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
        //1 只計算mask影響的位的變化
        final int oldVal = mSystemUiVisibility;
        final int newVal = (oldVal&~mask) | (vis&mask);
        final int diff = newVal ^ oldVal;
        if (DEBUG) Log.d(TAG, String.format(
                "setSystemUiVisibility vis=%s mask=%s oldVal=%s newVal=%s diff=%s",
                Integer.toHexString(vis), Integer.toHexString(mask),
                Integer.toHexString(oldVal), Integer.toHexString(newVal),
                Integer.toHexString(diff)));
        boolean sbModeChanged = false;
        if (diff != 0) {//2 發生變化做處理
            //3 更新標誌
            mSystemUiVisibility = newVal;

            //3 更新low profile的狀態,其實就是更新id為notification_lights_out的view的alpha
            // update low profile
            if ((diff & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
                setAreThereNotifications();
            }

            // ready to unhide
            //4 設定STATUS_BAR_UNHIDE標誌
            if ((vis & View.STATUS_BAR_UNHIDE) != 0) {
                mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE;
                mNoAnimationOnNextBarModeChange = true;
            }
            //5計算bar顯示的屬性
            // update status bar mode
            final int sbMode = computeBarMode(oldVal, newVal, mStatusBarView.getBarTransitions(),
                    View.STATUS_BAR_TRANSIENT, View.STATUS_BAR_TRANSLUCENT,
                    View.STATUS_BAR_TRANSPARENT);

            //6 計算導航欄模式
            // update navigation bar mode
            final int nbMode = mNavigationBarView == null ? -1 : computeBarMode(
                    oldVal, newVal, mNavigationBarView.getBarTransitions(),
                    View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT,
                    View.NAVIGATION_BAR_TRANSPARENT);
            sbModeChanged = sbMode != -1;
            final boolean nbModeChanged = nbMode != -1;
            boolean checkBarModes = false;
            //7 更新status bar 模式
            if (sbModeChanged && sbMode != mStatusBarMode) {
                mStatusBarMode = sbMode;
                checkBarModes = true;
            }
            //8 跟新nav bar 模式
            if (nbModeChanged && nbMode != mNavigationBarMode) {
                mNavigationBarMode = nbMode;
                checkBarModes = true;
            }
            //9 導航欄或者狀態列模式發生變化,切換模式
            if (checkBarModes) {
                checkBarModes();
            }
            //10 模式發生變化
            if (sbModeChanged || nbModeChanged) {
                // update transient bar autohide
                if (mStatusBarMode == MODE_SEMI_TRANSPARENT || mNavigationBarMode == MODE_SEMI_TRANSPARENT) {
                    //11 三秒後移動隱藏
                    scheduleAutohide();
                } else {
                    //12 取消自動隱藏
                    cancelAutohide();
                }
            }

            if ((vis & View.NAVIGATION_BAR_UNHIDE) != 0) {
                mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
            }
            //13 通知狀態列狀態發生變化
            // send updated sysui visibility to window manager
            notifyUiVisibilityChanged(mSystemUiVisibility);
        }

        mLightStatusBarController.onSystemUiVisibilityChanged(fullscreenStackVis, dockedStackVis,
                mask, fullscreenStackBounds, dockedStackBounds, sbModeChanged, mStatusBarMode);
    }

5 計算bar 顯示的屬性

 private int barMode(int vis, int transientFlag, int translucentFlag, int transparentFlag) {
        int lightsOutTransparent = View.SYSTEM_UI_FLAG_LOW_PROFILE | transparentFlag;
        return (vis & transientFlag) != 0 ? MODE_SEMI_TRANSPARENT
                : (vis & translucentFlag) != 0 ? MODE_TRANSLUCENT
                : (vis & lightsOutTransparent) == lightsOutTransparent ? MODE_LIGHTS_OUT_TRANSPARENT
                : (vis & transparentFlag) != 0 ? MODE_TRANSPARENT
                : (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0 ? MODE_LIGHTS_OUT
                : MODE_OPAQUE;
    }

這裡主要有五個屬性
1 MODE_SEMI_TRANSPARENT由不顯示到顯示的過程中,這種是完全透明的status bar ,不影響應用的content顯示
2 MODE_TRANSLUCENT 半透明的,這種應該隱藏狀態列
3 View.SYSTEM_UI_FLAG_LOW_PROFILE | View.STATUS_BAR_TRANSPARENT 低亮度但是狀態列透明,客戶端渲染顏色,這種情況並不覆蓋在應用的content上
4 MODE_TRANSPARENT 透明,客戶端渲染背景
5 MODE_LIGHTS_OUT 低亮
6 不透明

知道這些後systemui 這邊的設計也非常簡單的理解了. 那麼我們再來看看 notifyUiVisibilityChanged(mSystemUiVisibility);通知給WMS後會做什麼
在WindowManagerService中的statusBarVisibilityChanged函式中處理

 @Override
    public void statusBarVisibilityChanged(int visibility) {
        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
                != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException("Caller does not hold permission "
                    + android.Manifest.permission.STATUS_BAR);
        }

        synchronized (mWindowMap) {
            //1 更新最終狀態
            mLastStatusBarVisibility = visibility;
            //2 計算PhoneWindowMananger中的狀態
            visibility = mPolicy.adjustSystemUiVisibilityLw(visibility);
            //3 更新客戶端狀態
            updateStatusBarVisibilityLocked(visibility);
        }
    }

我們就來按照上面三步去分析
步驟1很簡單就不說了
2 PhoneWindowMananger->adjustSystemUiVisibilityLw

 @Override
    public int adjustSystemUiVisibilityLw(int visibility) {
        //1 更新status bar 屬性
        mStatusBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);
        mNavigationBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);

        // Reset any bits in mForceClearingStatusBarVisibility that
        // are now clear.
        mResettingSystemUiFlags &= visibility;
        // Clear any bits in the new visibility that are currently being
        // force cleared, before reporting it.
        //3 清除visibility中包含的mResettingSystemUiFlags|mForceClearedSystemUiFlags屬性
        return visibility & ~mResettingSystemUiFlags
                & ~mForceClearedSystemUiFlags;
    }
  • 1 adjustSystemUiVisibilityLw 這個函式只是向status bar的另一個狀態切換,請參考最前面說明的瞬態狀態
  • 2 清除已經設定好的mResettingSystemUiFlags中的標誌
  • 3 清除那些無用要清除的標誌返回

    3 WindowManagerService->updateStatusBarVisibilityLocked(visibility)函式

 boolean updateStatusBarVisibilityLocked(int visibility) {
        //1 沒有變化直接返回
        if (mLastDispatchedSystemUiVisibility == visibility) {
            return false;
        }
        //2 變化的標誌
        final int globalDiff = (visibility ^ mLastDispatchedSystemUiVisibility)
                // We are only interested in differences of one of the
                // clearable flags...
                & View.SYSTEM_UI_CLEARABLE_FLAGS
                // ...if it has actually been cleared.
                & ~visibility;

        mLastDispatchedSystemUiVisibility = visibility;
        mInputManager.setSystemUiVisibility(visibility);
        //3 獲取主螢幕上的window 
        final WindowList windows = getDefaultWindowListLocked();
        final int N = windows.size();
        for (int i = 0; i < N; i++) {
            WindowState ws = windows.get(i);
            try {
                int curValue = ws.mSystemUiVisibility;
                int diff = (curValue ^ visibility) & globalDiff;
                int newValue = (curValue&~diff) | (visibility&diff);
                if (newValue != curValue) {
                    ws.mSeq++;
                    ws.mSystemUiVisibility = newValue;
                }
                if (newValue != curValue || ws.mAttrs.hasSystemUiListeners) {
                    //4 標誌發生變化,通知客戶端變化
                    ws.mClient.dispatchSystemUiVisibilityChanged(ws.mSeq,
                            visibility, newValue, diff);
                }
            } catch (RemoteException e) {
                // so sorry
            }
        }
        return 
            
           

相關推薦

no