要想读懂窗口布局管理,必须了解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 是否允许进入到顺态,也就是从隐藏到显示的一个切换状态. 这里有一下几种情况可以进入瞬态
看来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;
}
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 true;
}
函数也很简单,我们直接看客户端如何处理,客户端的处理在
ViewRootImpl的 handleDispatchSystemUiVisibilityChanged((SystemUiVisibilityInfo) msg.obj)函数中完成
public void handleDispatchSystemUiVisibilityChanged(SystemUiVisibilityInfo args) {
if (mSeq != args.seq) {
// The sequence has changed, so we need to update our value and make
// sure to do a traversal afterward so the window manager is given our
// most recent data.
//1 更新seq,注意当systemflags变化的时候seq值会加1
mSeq = args.seq;
//2 设置mForceReportNewAttributes就会触发reLayoutWindow函数请求WMS重新layout
mAttachInfo.mForceReportNewAttributes = true;
//3 请求遍历
scheduleTraversals();
}
//4 decor view 不存在直接返回
if (mView == null) return;
if (args.localChanges != 0) {
//5 localChanges发生了变化,更新systemuiVisibility属性
mView.updateLocalSystemUiVisibility(args.localValue, args.localChanges);
}
//6 SYSTEM_UI_CLEARABLE_FLAGS包含的三个属性变化,通知注册的监听者
int visibility = args.globalVisibility&View.SYSTEM_UI_CLEARABLE_FLAGS;
if (visibility != mAttachInfo.mGlobalSystemUiVisibility) {
mAttachInfo.mGlobalSystemUiVisibility = visibility;
mView.dispatchSystemUiVisibilityChanged(visibility);
}
}
我们这里只看第五步,这里是针对DecorView设置的
boolean updateLocalSystemUiVisibility(int localValue, int localChanges) {
int val = (mSystemUiVisibility&~localChanges) | (localValue&localChanges);
if (val != mSystemUiVisibility) {
setSystemUiVisibility(val);
return true;
}
return false;
}
调用函数setSystemUiVisibility设置了新的标志,这和我们设置systemui 属性是一样的
if (isDefaultDisplay) {
mService.mPolicy.beginPostLayoutPolicyLw(dw, dh);
for (int i = windows.size() - 1; i >= 0; i--) {
WindowState w = windows.get(i);
if (w.mHasSurface) {
mService.mPolicy.applyPostLayoutPolicyLw(w, w.mAttrs,
w.mAttachedWindow);
}
}
displayContent.pendingLayoutChanges |=
mService.mPolicy.finishPostLayoutPolicyLw();
if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after finishPostLayoutPolicyLw",
displayContent.pendingLayoutChanges);
}
在每次WMS layout完成之后都会调用PhoneWindowManager的beginPostLayoutPolicyLw,applyPostLayoutPolicyLw和finishPostLayoutPolicyLw三个函数,也是设置mTopFullscreenOpaqueWindowState等变量的位置,我们稍微也分析一下
@Override
public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight) {
mTopFullscreenOpaqueWindowState = null; //full stack 不透明window
mTopFullscreenOpaqueOrDimmingWindowState = null; //full stack不透明或者dimming状态的window
mTopDockedOpaqueWindowState = null; // docker stack不透明window
mTopDockedOpaqueOrDimmingWindowState = null; //docker stack不透明或者Dimming 的window
mAppsToBeHidden.clear(); //即将被隐藏数AppWindowToken集合
mAppsThatDismissKeyguard.clear();
mForceStatusBar = false;// FLAG_FORCE_NOT_FULLSCREEN时候强制显示status bar
mForceStatusBarFromKeyguard = false; //强制在keygurd上显示status bar
mForceStatusBarTransparent = false;//强制status bar 透明
mForcingShowNavBar = false; //强制显示导航栏
mForcingShowNavBarLayer = -1; // 强制显示导航栏
mHideLockScreen = false;// 当window显示在keygurd上,该值为真
mAllowLockscreenWhenOn = false;//允许亮屏时候锁屏
mDismissKeyguard = DISMISS_KEYGUARD_NONE; //消除keygurd
mShowingLockscreen = false; //keygurd 显示
mShowingDream = false; //显示在dream模式
mWinShowWhenLocked = null; //在锁屏上显示的window
mKeyguardSecure = isKeyguardSecure(mCurrentUserId); //keygurd 需要密码解锁
mKeyguardSecureIncludingHidden = mKeyguardSecure //需要密码解锁,但是keygurd没显示
&& (mKeyguardDelegate != null && mKeyguardDelegate.isShowing());
}
beginPostLayoutPolicyLw比较简单,只是初始化上面的一些变量
/** {@inheritDoc} */
@Override
public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs,
WindowState attached) {
if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": isVisibleOrBehindKeyguardLw="
+ win.isVisibleOrBehindKeyguardLw());
final int fl = PolicyControl.getWindowFlags(win, attrs);
if (mTopFullscreenOpaqueWindowState == null
&& win.isVisibleLw() && attrs.type == TYPE_INPUT_METHOD) {
//1 输入法上上面,强制显示nav bar
mForcingShowNavBar = true;
mForcingShowNavBarLayer = win.getSurfaceLayer();
}
if (attrs.type == TYPE_STATUS_BAR) {
if ((attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
//2key guran显示.设置强制显示status bar
mForceStatusBarFromKeyguard = true;
mShowingLockscreen = true;
}
if ((attrs.privateFlags & PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT) != 0) {
//3 强制status bar 透明
mForceStatusBarTransparent = true;
}
}
boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW
&& attrs.type < FIRST_SYSTEM_WINDOW;
final boolean showWhenLocked = (fl & FLAG_SHOW_WHEN_LOCKED) != 0;
final boolean dismissKeyguard = (fl & FLAG_DISMISS_KEYGUARD) != 0;
final int stackId = win.getStackId();
//4 可见的或者keygurd后面的窗口
if (mTopFullscreenOpaqueWindowState == null &&
win.isVisibleOrBehindKeyguardLw() && !win.isGoneForLayoutLw()) {
//5 强制非全屏,设置mForceStatusBarFromKeyguard或者mForceStatusBar,其实就是对
//FLAG_FORCE_NOT_FULLSCREEN标志的处理
if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) {
if ((attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
mForceStatusBarFromKeyguard = true;
} else {
mForceStatusBar = true;
}
}
if (attrs.type == TYPE_DREAM) {
//6 window的类型为TYPE_DREAM如果不是在锁屏发生dream或者已经绘制出,设置mShowingDream
// If the lockscreen was showing when the dream started then wait
// for the dream to draw before hiding the lockscreen.
if (!mDreamingLockscreen
|| (win.isVisibleLw() && win.hasDrawnLw())) {
mShowingDream = true;
appWindow = true;
}
}
final IApplicationToken appToken = win.getAppToken();
// For app windows that are not attached, we decide if all windows in the app they
// represent should be hidden or if we should hide the lockscreen. For attached app
// windows we defer the decision to the window it is attached to.
//7 是一个appwindow 并且不是子window
if (appWindow && attached == null) {
if (showWhenLocked) {//8 设置了FLAG_SHOW_WHEN_LOCKED标志,可以显示在keygurd上
// Remove any previous windows with the same appToken.
mAppsToBeHidden.remove(appToken);
mAppsThatDismissKeyguard.remove(appToken);
if (mAppsToBeHidden.isEmpty()) {
if (dismissKeyguard && !mKeyguardSecure) {
//9 可以解除锁屏,放在mAppsThatDismissKeyguard处理
mAppsThatDismissKeyguard.add(appToken);
} else if (win.isDrawnLw() || win.hasAppShownWindows()) {
//10 已经绘制出来,设置下面三个变量
mWinShowWhenLocked = win;
mHideLockScreen = true;
mForceStatusBarFromKeyguard = false;
}
}
} else if (dismissKeyguard) {//11 设置FLAG_DISMISS_KEYGUARD标志,但是不能显示在keygurd上
if (mKeyguardSecure) {
//12需要解锁解除keygurd,放到 mAppsToBeHidden中
mAppsToBeHidden.add(appToken);
} else {
//12不需要解锁,移除
mAppsToBeHidden.remove(appToken);
}
//13 添加到mAppsThatDismissKeyguard中稍后处理
mAppsThatDismissKeyguard.add(appToken);
} else {
//14 不能显示,放到mAppsToBeHidden中稍后处理
mAppsToBeHidden.add(appToken);
}
//15 全屏的window并且放在full stack中,(这里是指full work space stack和home stack)
if (isFullscreen(attrs) && StackId.normallyFullscreenWindows(stackId)) {
if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win);
//16 设置mTopFullscreenOpaqueWindowState,设置mTopFullscreenOpaqueOrDimmingWindowState
mTopFullscreenOpaqueWindowState = win;
if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
mTopFullscreenOpaqueOrDimmingWindowState = win;
}
//17 如果设置FLAG_DISMISS_KEYGUARD标志的window存在,并且现在是锁屏状态
if (!mAppsThatDismissKeyguard.isEmpty() &&
mDismissKeyguard == DISMISS_KEYGUARD_NONE) {
if (DEBUG_LAYOUT) Slog.v(TAG,
"Setting mDismissKeyguard true by win " + win);
//解除keygurd的状态,需要密码为DISMISS_KEYGUARD_CONTINUE,否则为
//DISMISS_KEYGUARD_START
mDismissKeyguard = (mWinDismissingKeyguard == win
&& mSecureDismissingKeyguard == mKeyguardSecure)
? DISMISS_KEYGUARD_CONTINUE : DISMISS_KEYGUARD_START;
//1 设置要解除锁屏的activity为win
mWinDismissingKeyguard = win;
//2 要消除的锁屏状态是需要密码解锁的
mSecureDismissingKeyguard = mKeyguardSecure;
mForceStatusBarFromKeyguard = mShowingLockscreen && mKeyguardSecure;
} else if (mAppsToBeHidden.isEmpty() && showWhenLocked
&& (win.isDrawnLw() || win.hasAppShownWindows())) {
//18 需要显示在keygurd上面的window
if (DEBUG_LAYOUT) Slog.v(TAG,
"Setting mHideLockScreen to true by win " + win);
mHideLockScreen = true;
mForceStatusBarFromKeyguard = false;
}
if ((fl & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) {
//19 允许亮屏时锁屏,60s没操作锁屏,也就是最上面的全屏activity控制
mAllowLockscreenWhenOn = true;
}
}
//20 不允许在锁屏显示hide window
if (!mKeyguardHidden && mWinShowWhenLocked != null &&
mWinShowWhenLocked.getAppToken() != win.getAppToken() &&
(attrs.flags & FLAG_SHOW_WHEN_LOCKED) == 0) {
win.hideLw(false);
}
}
} else if (mTopFullscreenOpaqueWindowState == null && mWinShowWhenLocked == null) {
// No TopFullscreenOpaqueWindow is showing, but we found a SHOW_WHEN_LOCKED window
// that is being hidden in an animation - keep the
// keyguard hidden until the new window shows up and
// we know whether to show the keyguard or not.
//21 没有TopFullscreenOpaqueWindow,但是一个可以在锁屏上显示的window
//(设置了FLAG_SHOW_WHEN_LOCKED标志),并且之歌window正在执行动画,记录下锁屏状态和
//mWinShowWhenLocked
if (win.isAnimatingLw() && appWindow && showWhenLocked && mKeyguardHidden) {
mHideLockScreen = true;
mWinShowWhenLocked = win;
}
}
//22 window真的可见
final boolean reallyVisible = win.isVisibleOrBehindKeyguardLw() && !win.isGoneForLayoutLw();
//23 语音助手在所有window之上
// Voice interaction overrides both top fullscreen and top docked.
if (reallyVisible && win.getAttrs().type == TYPE_VOICE_INTERACTION) {
if (mTopFullscreenOpaqueWindowState == null) {
mTopFullscreenOpaqueWindowState = win;
if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
mTopFullscreenOpaqueOrDimmingWindowState = win;
}
}
if (mTopDockedOpaqueWindowState == null) {
mTopDockedOpaqueWindowState = win;
if (mTopDockedOpaqueOrDimmingWindowState == null) {
mTopDockedOpaqueOrDimmingWindowState = win;
}
}
}
//24 如果不存在mTopFullscreenOpaqueOrDimmingWindowState,非全屏的Dimming也可以设置
// Keep track of the window if it's dimming but not necessarily fullscreen.
if (mTopFullscreenOpaqueOrDimmingWindowState == null && reallyVisible
&& win.isDimming() && StackId.normallyFullscreenWindows(stackId)) {
mTopFullscreenOpaqueOrDimmingWindowState = win;
}
// We need to keep track of the top "fullscreen" opaque window for the docked stack
// separately, because both the "real fullscreen" opaque window and the one for the docked
// stack can control View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
//25 设置docker stack的全屏window
if (mTopDockedOpaqueWindowState == null && reallyVisible && appWindow && attached == null
&& isFullscreen(attrs) && stackId == DOCKED_STACK_ID) {
mTopDockedOpaqueWindowState = win;
if (mTopDockedOpaqueOrDimmingWindowState == null) {
mTopDockedOpaqueOrDimmingWindowState = win;
}
}
// Also keep track of any windows that are dimming but not necessarily fullscreen in the
// docked stack.
//26 设置docker stack中的mTopDockedOpaqueOrDimmingWindowState
if (mTopDockedOpaqueOrDimmingWindowState == null && reallyVisible && win.isDimming()
&& stackId == DOCKED_STACK_ID) {
mTopDockedOpaqueOrDimmingWindowState = win;
}
}
函数不太复杂下面分析finishPostLayoutPolicyLw
/** {@inheritDoc} */
@Override
public int finishPostLayoutPolicyLw() {
if (mWinShowWhenLocked != null && mTopFullscreenOpaqueWindowState != null &&
mWinShowWhenLocked.getAppToken() != mTopFullscreenOpaqueWindowState.getAppToken()
&& isKeyguardLocked()) {
//1 锁屏上有window,并且是不是一个全屏app window的情况,这种情况有可能是是一个透明的dialog,所以
//添加一个FLAG_SHOW_WALLPAPER标志,强制显示wallPaper,另外使
//mTopFullscreenOpaqueWindowState隐藏,设置mTopFullscreenOpaqueWindowState为这个window
//为mTopFullscreenOpaqueWindowState
// A dialog is dismissing the keyguard. Put the wallpaper behind it and hide the
// fullscreen window.
// TODO: Make sure FLAG_SHOW_WALLPAPER is restored when dialog is dismissed. Or not.
mWinShowWhenLocked.getAttrs().flags |= FLAG_SHOW_WALLPAPER;
mTopFullscreenOpaqueWindowState.hideLw(false);
mTopFullscreenOpaqueWindowState = mWinShowWhenLocked;
}
int changes = 0;
boolean topIsFullscreen = false;
//2 获取mTopFullscreenOpaqueWindowState的LayoutParams
final WindowManager.LayoutParams lp = (mTopFullscreenOpaqueWindowState != null)
? mTopFullscreenOpaqueWindowState.getAttrs()
: null;
// If we are not currently showing a dream then remember the current
// lockscreen state. We will use this to determine whether the dream
// started while the lockscreen was showing and remember this state
// while the dream is showing.
//3 dream 模式这里先不管
if (!mShowingDream) {
mDreamingLockscreen = mShowingLockscreen;
if (mDreamingSleepTokenNeeded) {
mDreamingSleepTokenNeeded = false;
mHandler.obtainMessage(MSG_UPDATE_DREAMING_SLEEP_TOKEN, 0, 1).sendToTarget();
}
} else {
if (!mDreamingSleepTokenNeeded) {
mDreamingSleepTokenNeeded = true;
mHandler.obtainMessage(MSG_UPDATE_DREAMING_SLEEP_TOKEN, 1, 1).sendToTarget();
}
}
if (mStatusBar != null) {
if (DEBUG_LAYOUT) Slog.i(TAG, "force=" + mForceStatusBar
+ " forcefkg=" + mForceStatusBarFromKeyguard
+ " top=" + mTopFullscreenOpaqueWindowState);
boolean shouldBeTransparent = mForceStatusBarTransparent
&& !mForceStatusBar
&& !mForceStatusBarFromKeyguard;
if (!shouldBeTransparent) {
//3 status bar 不需要透明
mStatusBarController.setShowTransparent(false /* transparent */);
} else if (!mStatusBar.isVisibleLw()) {
//4 强制status bar 透明
mStatusBarController.setShowTransparent(true /* transparent */);
}
WindowManager.LayoutParams statusBarAttrs = mStatusBar.getAttrs();
//5 status bar 是展开的状态
boolean statusBarExpanded = statusBarAttrs.height == MATCH_PARENT
&& statusBarAttrs.width == MATCH_PARENT;
//6 以下为显示status bar 的状态
if (mForceStatusBar /*FLAG_FORCE_NOT_FULLSCREEN*/
|| mForceStatusBarFromKeyguard/*keygurd 显示*/
|| mForceStatusBarTransparent /**强制透明/
|| statusBarExpanded/*status bar 展开*/) {
if (DEBUG_LAYOUT) Slog.v(TAG, "Showing status bar: forced");
if (mStatusBarController.setBarShowingLw(true)) {
//7 设置显示status bar ,通知WMS需要重新layout
changes |= FINISH_LAYOUT_REDO_LAYOUT;
}
// Maintain fullscreen layout until incoming animation is complete.
//8 保持全屏布局知道动画完成
topIsFullscreen = mTopIsFullscreen && mStatusBar.isAnimatingLw();
// Transient status bar on the lockscreen is not allowed
if (mForceStatusBarFromKeyguard && mStatusBarController.isTransientShowing()) {
//9 在keygurd上 设置不允许进入瞬态
mStatusBarController.updateVisibilityLw(false /*transientAllowed*/,
mLastSystemUiFlags, mLastSystemUiFlags);
}
//10 状态栏展开设置导航栏可见
if (statusBarExpanded && mNavigationBar != null) {
if (mNavigationBarController.setBarShowingLw(true)) {
changes |= FINISH_LAYOUT_REDO_LAYOUT;
}
}
} else if (mTopFullscreenOpaqueWindowState != null) {
//11 mTopFullscreenOpaqueWindowState存在
final int fl = PolicyControl.getWindowFlags(null, lp);
if (localLOGV) {
Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw()
+ " shown position: "
+ mTopFullscreenOpaqueWindowState.getShownPositionLw());
Slog.d(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs()
+ " lp.flags=0x" + Integer.toHexString(fl));
}
//12 全屏的mTopFullscreenOpaqueWindowState
topIsFullscreen = (fl & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0
|| (mLastSystemUiFlags & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
// The subtle difference between the window for mTopFullscreenOpaqueWindowState
// and mTopIsFullscreen is that mTopIsFullscreen is set only if the window
// has the FLAG_FULLSCREEN set. Not sure if there is another way that to be the
// case though.
//13 正在瞬态可见过程中,设置可见
if (mStatusBarController.isTransientShowing()) {
if (mStatusBarController.setBarShowingLw(true)) {
changes |= FINISH_LAYOUT_REDO_LAYOUT;
}
} else if (topIsFullscreen
&& !mWindowManagerInternal.isStackVisible(FREEFORM_WORKSPACE_STACK_ID)
&& !mWindowManagerInternal.isStackVisible(DOCKED_STACK_ID)) {
if (DEBUG_LAYOUT) Slog.v(TAG, "** HIDING status bar");
//14 全屏不在瞬态过程中,设置为不可见
if (mStatusBarController.setBarShowingLw(false)) {
changes |= FINISH_LAYOUT_REDO_LAYOUT;
} else {
if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar already hiding");
}
} else {
//15 非全屏包含分屏窗口,设置为可见
if (DEBUG_LAYOUT) Slog.v(TAG, "** SHOWING status bar: top is not fullscreen");
if (mStatusBarController.setBarShowingLw(true)) {
changes |= FINISH_LAYOUT_REDO_LAYOUT;
}
}
}
}
//16 TopIsFullscreen发生变化更新
if (mTopIsFullscreen != topIsFullscreen) {
if (!topIsFullscreen) {
// Force another layout when status bar becomes fully shown.
changes |= FINISH_LAYOUT_REDO_LAYOUT;
}
mTopIsFullscreen = topIsFullscreen;
}
// Hide the key guard if a visible window explicitly specifies that it wants to be
// displayed when the screen is locked.
if (mKeyguardDelegate != null && mStatusBar != null) {
if (localLOGV) Slog.v(TAG, "finishPostLayoutPolicyLw: mHideKeyguard="
+ mHideLockScreen);
if (mDismissKeyguard != DISMISS_KEYGUARD_NONE && !mKeyguardSecure) {
mKeyguardHidden = true;
//17 需要关闭keygurd
if (setKeyguardOccludedLw(true)) {
changes |= FINISH_LAYOUT_REDO_LAYOUT
| FINISH_LAYOUT_REDO_CONFIG
| FINISH_LAYOUT_REDO_WALLPAPER;
}
if (mKeyguardDelegate.isShowing()) {
mHandler.post(new Runnable() {
@Override
public void run() {
mKeyguardDelegate.keyguardDone(false, false);
}
});
}
} else if (mHideLockScreen) { //18 window显示在keygurd上
mKeyguardHidden = true;
mWinDismissingKeyguard = null;
if (setKeyguardOccludedLw(true)) {
changes |= FINISH_LAYOUT_REDO_LAYOUT
| FINISH_LAYOUT_REDO_CONFIG
| FINISH_LAYOUT_REDO_WALLPAPER;
}
} else if (mDismissKeyguard != DISMISS_KEYGUARD_NONE) {
mKeyguardHidden = false;
if (setKeyguardOccludedLw(false)) {
changes |= FINISH_LAYOUT_REDO_LAYOUT
| FINISH_LAYOUT_REDO_CONFIG
| FINISH_LAYOUT_REDO_WALLPAPER;
}
if (mDismissKeyguard == DISMISS_KEYGUARD_START) {
// Only launch the next keyguard unlock window once per window.
//19 结束keygurd
mHandler.post(new Runnable() {
@Override
public void run() {
mKeyguardDelegate.dismiss();
}
});
}
} else {
//20
mWinDismissingKeyguard = null;
mSecureDismissingKeyguard = false;
mKeyguardHidden = false;
if (setKeyguardOccludedLw(false)) {
changes |= FINISH_LAYOUT_REDO_LAYOUT
| FINISH_LAYOUT_REDO_CONFIG
| FINISH_LAYOUT_REDO_WALLPAPER;
}
}
}
//21 更新system ui 属性
if ((updateSystemUiVisibilityLw()&SYSTEM_UI_CHANGING_LAYOUT) != 0) {
// If the navigation bar has been hidden or shown, we need to do another
// layout pass to update that window.
changes |= FINISH_LAYOUT_REDO_LAYOUT;
}
//22更新锁屏超时时间
// update since mAllowLockscreenWhenOn might have changed
updateLockScreenTimeout();
return changes;
}
1app客户端请求relayoutWindow
2 WMS处理SystemUI相关的LayoutParams变化,WMS计算好全局SystemUI相关flags,通知SystemUI更新
3 SystemUI更新完成后通知WMS新的systemui flags
4WMS接收到新的标志后通知app客户端systemui布局发生变化,app端更新LayoutParams
5app客户端请求relayoutWindow
最后周而复始知道SystemUI标志不再改变