前言
上一篇我们具体分析了在Android系统中SystemUIVisibility属性是如何控制状态栏和导航栏样式的,除了通过SystemUIVisibility属性控制状态栏和导航栏属性以外,我们还可以通过WindowInsetsController相关的类来控制状态栏和导航栏。在Android10以后,谷歌不再推荐使用SystemUIVisibility属性来控制状态栏和导航栏,而是推荐通过和WindowInsetsController相关的类来控制状态栏和导航栏。本篇文章我们就来具体分析一下WindowInsetsController是如何控制状态栏和导航栏的。
一、对状态栏导航栏进行隐藏和显示
1、在Android10以上的系统中,我们可以通过调用以下代码来隐藏状态栏和导航栏。
//获取WindowInsetsController对象实例
WindowInsetsController windowInsetsController = getWindow().getDecorView().getWindowInsetsController();
//调用hide隐藏状态栏
windowInsetsController.hide(WindowInsets.Type.statusBars())
//调用hide隐藏导航栏
windowInsetsController.hide(WindowInsets.Type.navigationBar())
//调用show显示状态栏
windowInsetsController.show(WindowInsets.Type.statusBars())
//调用show显示导航栏
windowInsetsController.show(WindowInsets.Type.navigationBar())
2、WindowInsets.Type是一个很关键的类,系统就是用它来表示不同的窗口装饰区域类型的,该类使用二进制整数的每一位来标识特定的窗口装饰区域类型,相关的代码如下所示。
frameworks/base/core/java/android/view/WindowInsets.java
public final class WindowInsets {
public static final class Type {
static final int FIRST = 1 << 0;
static final int STATUS_BARS = FIRST; //状态栏类型
static final int NAVIGATION_BARS = 1 << 1; //导航类类型
static final int CAPTION_BAR = 1 << 2;
static final int IME = 1 << 3;//输入法
static final int SYSTEM_GESTURES = 1 << 4;
static final int MANDATORY_SYSTEM_GESTURES = 1 << 5;
static final int TAPPABLE_ELEMENT = 1 << 6;
static final int DISPLAY_CUTOUT = 1 << 7;
static final int LAST = 1 << 8;
static final int SIZE = 9;
static final int WINDOW_DECOR = LAST;
/**
* 状态栏
*/
public static @InsetsType int statusBars() {
return STATUS_BARS;
}
/**
* 导航类
*/
public static @InsetsType int navigationBars() {
return NAVIGATION_BARS;
}
public static @InsetsType int captionBar() {
return CAPTION_BAR;
}
public static @InsetsType int systemBars() {
return STATUS_BARS | NAVIGATION_BARS | CAPTION_BAR;
}
}
}
二、WindowInsetsController对象实例的获取。
1、调用DecorView的getWindowInsetsController方法获取实例对象
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
private PendingInsetsController mPendingInsetsController = new PendingInsetsController();
/**
* 当前视图是否已经被添加到窗口上
*/
public boolean isAttachedToWindow() {
return mAttachInfo != null;
}
@Override
public WindowInsetsController getWindowInsetsController() {
//判断视图是否已经被添加到Window上
if (isAttachedToWindow()) {
return super.getWindowInsetsController();//主要分析是这种情况
} else {
return mPendingInsetsController;
}
}
}
如果视图已经被添加到窗口上则继续调用父类的方法,如果视图还未被添加到窗口上,则返回PendingInsetsController对象实例,由于PendingInsetsController对象只是做了个缓存,其内部的实现方式基本和第一种情况一样。这里我们主要分析视图已经被添加到窗口上的这种情况,这个时候会调用父类的getWindowInsetsController方法。
2、getWindowInsetsController方法最初是在View类中实现的。
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
AttachInfo mAttachInfo;
public @Nullable WindowInsetsController getWindowInsetsController() {
if (mAttachInfo != null) {
//判断视图是否已经被添加到Window上
return mAttachInfo.mViewRootImpl.getInsetsController();//调用ViewRootImpl的getInsetsController方法
}
ViewParent parent = getParent();
if (parent instanceof View) {
return ((View) parent).getWindowInsetsController();
} else if (parent instanceof ViewRootImpl) {
// Between WindowManager.addView() and the first traversal AttachInfo isn't set yet.
return ((ViewRootImpl) parent).getInsetsController();
}
return null;
}
}
如果视图已经被添加到窗口上,则最终调用的其实是ViewRootImpl的getInsetsController方法。
3、ViewRootImpl和getInsetsController方法相关的代码如下所示。
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
AttachedSurfaceControl {
private final InsetsController mInsetsController;
public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,
boolean useSfChoreographer) {
...代码省略...
mInsetsController = new InsetsController(new ViewRootInsetsControllerHost(this));
...代码省略...
}
public InsetsController getInsetsController() {
return mInsetsController;
}
}
通过getWindow().getDecorView().getWindowInsetsController()方法所获取的对象实例其实是ViewRootImpl对象中类型为的InsetsController的属性对象mInsetsController,而该对象最初是在ViewRootImpl的构造方法中被赋值的。
三、调用hide隐藏状态栏。
1、由于隐藏和显示状态栏导航栏的流程大同小异,这里我们将会结合源码来梳理InsetsController隐藏状态栏的相关流程。
//调用hide隐藏状态栏
windowInsetsController.hide(WindowInsetsCompat.Type.statusBars())
结合前面的代码我们知道,这里调用的其实是InsetsController的hide方法,WindowInsetsCompat.Type.statusBars方法返回的是STATUS_BARS类型。
2、下面我们具体来分析InsetsController类的hide方法。
frameworks/base/core/java/android/view/InsetsController.java
public class InsetsController implements WindowInsetsController, InsetsAnimationControlCallbacks {
public void hide(int types) {
this.hide(types, false);
}
public void hide(@InsetsType int types, boolean fromIme) {
int typesReady = 0;
//将一个外部类型转化为内部类型,其实就是将WindowInsets.Type类型对应的整数转化为InsetsState类型对应的整数,
//返回InsetsState类型对应的ArraySet<Integer>类型的集合。
final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
//遍历internalTypes
for (int i = internalTypes.size() - 1; i >= 0; i--) {
@InternalInsetsType int internalType = internalTypes.valueAt(i);
//获取当前internalType对应的动画
@AnimationType int animationType = getAnimationType(internalType);
//获取当前internalType对应的窗口装饰区域事件消费者
InsetsSourceConsumer consumer = getSourceConsumer(internalType);
if (!consumer.isRequestedVisible() && animationType == ANIMATION_TYPE_NONE
|| animationType == ANIMATION_TYPE_HIDE) {
//如果动画类型为隐藏类型、或者视图属于隐藏类型且没有隐藏动画的,直接跳过
continue;
}
//否则会将这个内部类型(InsetsState类型)对应的整数转化为外部类型(WindowInsets.Type类型)对应的整数
typesReady |= InsetsState.toPublicType(consumer.getType());
}
//继续调用applyAnimation方法
applyAnimation(typesReady, false /* show */, fromIme /* fromIme */);
}
}
以上代码主要做了以下几点。
1)首先会调用InsetsState类的toInternalType方法,将WindowInsets.Type类型对应的整数转化为InsetsState类型对应的整数,并封装成ArraySet类型的集合返回。InsetsState的toInternalType方法如下所示。
frameworks/base/core/java/android/view/InsetsState.java
public class InsetsState implements Parcelable {
public static final int ITYPE_INVALID = -1;
static final int FIRST_TYPE = 0;
public static final int ITYPE_STATUS_BAR = FIRST_TYPE;//状态栏
public static final int ITYPE_NAVIGATION_BAR = 1;//导航栏
public static final int ITYPE_CAPTION_BAR = 2;
public static final int ITYPE_TOP_GESTURES = 3;
public static final int ITYPE_BOTTOM_GESTURES = 4;
public static final int ITYPE_LEFT_GESTURES = 5;
public static final int ITYPE_RIGHT_GESTURES = 6;
public static final int ITYPE_TOP_MANDATORY_GESTURES = 7;
public static final int ITYPE_BOTTOM_MANDATORY_GESTURES = 8;
public static final int ITYPE_LEFT_MANDATORY_GESTURES = 9;
public static final int ITYPE_RIGHT_MANDATORY_GESTURES = 10;
public static final int ITYPE_LEFT_DISPLAY_CUTOUT = 11;
public static final int ITYPE_TOP_DISPLAY_CUTOUT = 12;
public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 13;
public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 14;
public static final int ITYPE_LEFT_TAPPABLE_ELEMENT = 15;
public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 16;
public static final int ITYPE_RIGHT_TAPPABLE_ELEMENT = 17;
public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 18;
public static final int ITYPE_IME = 19;//输入法
public static final int ITYPE_CLIMATE_BAR = 20;
public static final int ITYPE_EXTRA_NAVIGATION_BAR = 21;
static final int LAST_TYPE = ITYPE_EXTRA_NAVIGATION_BAR;
public static final int SIZE = LAST_TYPE + 1;
public static @InternalInsetsType ArraySet<Integer> toInternalType(@InsetsType int types) {
final ArraySet<Integer> result = new ArraySet<>();
if ((types & Type.STATUS_BARS) != 0) {
result.add(ITYPE_STATUS_BAR);
result.add(ITYPE_CLIMATE_BAR);
}
if ((types & Type.NAVIGATION_BARS) != 0) {
result.add(ITYPE_NAVIGATION_BAR);
result.add(ITYPE_EXTRA_NAVIGATION_BAR);
}
if ((types & Type.CAPTION_BAR) != 0) {
result.add(ITYPE_CAPTION_BAR);
}
if ((types & Type.SYSTEM_GESTURES) != 0) {
result.add(ITYPE_LEFT_GESTURES);
result.add(ITYPE_TOP_GESTURES);
result.add(ITYPE_RIGHT_GESTURES);
result.add(ITYPE_BOTTOM_GESTURES);
}
if ((types & Type.MANDATORY_SYSTEM_GESTURES) != 0) {
result.add(ITYPE_LEFT_MANDATORY_GESTURES);
result.add(ITYPE_TOP_MANDATORY_GESTURES);
result.add(ITYPE_RIGHT_MANDATORY_GESTURES);
result.add(ITYPE_BOTTOM_MANDATORY_GESTURES);
}
if ((types & Type.DISPLAY_CUTOUT) != 0) {
result.add(ITYPE_LEFT_DISPLAY_CUTOUT);
result.add(ITYPE_TOP_DISPLAY_CUTOUT);
result.add(ITYPE_RIGHT_DISPLAY_CUTOUT);
result.add(ITYPE_BOTTOM_DISPLAY_CUTOUT);
}
if ((types & Type.IME) != 0) {
result.add(ITYPE_IME);
}
return result;
}
}
由于WindowInsets.Type是用二进制的每一位来标识的类型的,而InsetsState是整数,这样同一个WindowInsets.Type可能会对应多个InsetsState类型的整数,因此这里将它们封装成了ArraySet类型的集合。
2)遍历通过InsetsState.toInternalType方法所得到的类型为InsetsState类型的整数集合internalTypes 。
3)调用getAnimationType方法获取当前InsetsState类型的整数(即窗口装饰区域类型)对应的动画类型。
public class InsetsController implements WindowInsetsController, InsetsAnimationControlCallbacks {
/** Not running an animation. */
public static final int ANIMATION_TYPE_NONE = -1;
/** Running animation will show insets */
public static final int ANIMATION_TYPE_SHOW = 0;
/** Running animation will hide insets */
public static final int ANIMATION_TYPE_HIDE = 1;
/** Running animation is controlled by user via {@link #controlWindowInsetsAnimation} */
public static final int ANIMATION_TYPE_USER = 2;
/** Running animation will resize insets */
public static final int ANIMATION_TYPE_RESIZE = 3;
private final ArrayList<RunningAnimation> mRunningAnimations = new ArrayList<>();
public @AnimationType int getAnimationType(@InternalInsetsType int type) {
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner;
if (control.controlsInternalType(type)) {
//如果存在缓存直接使用缓存
return mRunningAnimations.get(i).type;
}
}
//否则返回ANIMATION_TYPE_NONE,表示没有动画
return ANIMATION_TYPE_NONE;
}
}
4)调用getSourceConsumer方法获取当前InsetsState类型的整数(即窗口装饰区域类型)对应的消费者。
public class InsetsController implements WindowInsetsController, InsetsAnimationControlCallbacks {
private final BiFunction<InsetsController, Integer, InsetsSourceConsumer> mConsumerCreator;
private final SparseArray<InsetsSourceConsumer> mSourceConsumers = new SparseArray<>();
public InsetsController(Host host) {
this(host, (controller, type) -> {
if (type == ITYPE_IME) {
//是输入法,此类继承自InsetsSourceConsumer
return new ImeInsetsSourceConsumer(controller.mState, Transaction::new, controller);
} else {
//不是输入法
return new InsetsSourceConsumer(type, controller.mState, Transaction::new,
controller);
}
}, host.getHandler());
}
public InsetsController(Host host,
BiFunction<InsetsController, Integer, InsetsSourceConsumer> consumerCreator,
Handler handler) {
mHost = host;
mConsumerCreator = consumerCreator;
...代码省略...
}
public @NonNull InsetsSourceConsumer getSourceConsumer(@InternalInsetsType int type) {
InsetsSourceConsumer controller = mSourceConsumers.get(type);
if (controller != null) {
//如果存在缓存直接使用缓存
return controller;
}
//接受类型为InsetsController和Integer类型的参数,创建InsetsSourceConsumer对象实例
controller = mConsumerCreator.apply(this, type);
//缓存
mSourceConsumers.put(type, controller);
return controller;
}
}
public interface BiFunction<T, U, R> {
/**
* 此方法将t、u作为参数,创建R类型的对象实例。
*/
R apply(T t, U u);
}
getSourceConsumer方法会调用BiFunction类型的mConsumerCreator对象的apply方法,该方法会创建InsetsSourceConsumer对象实例。
mConsumerCreator对象最早是在InsetsController的构造方法中被赋值的,该方法会根据传入的不同type类型,决定返回的是ImeInsetsSourceConsumer对象还是InsetsSourceConsumer对象,然后将该对象缓存到mSourceConsumers中,然后返回。
5)如果动画类型为隐藏类型、或者视图属于隐藏类型且没有隐藏动画的,直接跳过,否则会调用InsetsState的toPublicType方法将这个内部类型(InsetsState类型)对应的整数重新转化为外部类型(WindowInsets.Type类型)对应的整数。
public class InsetsState implements Parcelable {
public static final int ITYPE_INVALID = -1;
static final int FIRST_TYPE = 0;
public static final int ITYPE_STATUS_BAR = FIRST_TYPE;//状态栏
public static final int ITYPE_NAVIGATION_BAR = 1;//导航栏
public static final int ITYPE_CAPTION_BAR = 2;
public static final int ITYPE_TOP_GESTURES = 3;
public static final int ITYPE_BOTTOM_GESTURES = 4;
public static final int ITYPE_LEFT_GESTURES = 5;
public static final int ITYPE_RIGHT_GESTURES = 6;
public static final int ITYPE_TOP_MANDATORY_GESTURES = 7;
public static final int ITYPE_BOTTOM_MANDATORY_GESTURES = 8;
public static final int ITYPE_LEFT_MANDATORY_GESTURES = 9;
public static final int ITYPE_RIGHT_MANDATORY_GESTURES = 10;
public static final int ITYPE_LEFT_DISPLAY_CUTOUT = 11;
public static final int ITYPE_TOP_DISPLAY_CUTOUT = 12;
public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 13;
public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 14;
public static final int ITYPE_LEFT_TAPPABLE_ELEMENT = 15;
public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 16;
public static final int ITYPE_RIGHT_TAPPABLE_ELEMENT = 17;
public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 18;
public static final int ITYPE_IME = 19;//输入法
public static final int ITYPE_CLIMATE_BAR = 20;
public static final int ITYPE_EXTRA_NAVIGATION_BAR = 21;
static final int LAST_TYPE = ITYPE_EXTRA_NAVIGATION_BAR;
public static final int SIZE = LAST_TYPE + 1;
/**
* 将InsetsState类型对应的整数转化为WindowInsets.Type类型对应的整数
*/
public static @Type.InsetsType int toPublicType(@InternalInsetsType int type) {
switch (type) {
case ITYPE_STATUS_BAR:
case ITYPE_CLIMATE_BAR:
return Type.STATUS_BARS;//WindowInsets.Type类型的状态栏
case ITYPE_NAVIGATION_BAR:
case ITYPE_EXTRA_NAVIGATION_BAR:
return Type.NAVIGATION_BARS;//WindowInsets.Type类型导航栏
case ITYPE_CAPTION_BAR:
return Type.CAPTION_BAR;//WindowInsets.Type类型
case ITYPE_IME:
return Type.IME;//WindowInsets.Type类型输入法
case ITYPE_TOP_MANDATORY_GESTURES:
case ITYPE_BOTTOM_MANDATORY_GESTURES:
case ITYPE_LEFT_MANDATORY_GESTURES:
case ITYPE_RIGHT_MANDATORY_GESTURES:
return Type.MANDATORY_SYSTEM_GESTURES;
case ITYPE_TOP_GESTURES:
case ITYPE_BOTTOM_GESTURES:
case ITYPE_LEFT_GESTURES:
case ITYPE_RIGHT_GESTURES:
return Type.SYSTEM_GESTURES;
case ITYPE_LEFT_TAPPABLE_ELEMENT:
case ITYPE_TOP_TAPPABLE_ELEMENT:
case ITYPE_RIGHT_TAPPABLE_ELEMENT:
case ITYPE_BOTTOM_TAPPABLE_ELEMENT:
return Type.TAPPABLE_ELEMENT;
case ITYPE_LEFT_DISPLAY_CUTOUT:
case ITYPE_TOP_DISPLAY_CUTOUT:
case ITYPE_RIGHT_DISPLAY_CUTOUT:
case ITYPE_BOTTOM_DISPLAY_CUTOUT:
return Type.DISPLAY_CUTOUT;
default:
throw new IllegalArgumentException("Unknown type: " + type);
}
}
}
这样其实就过滤掉了动画类型为隐藏类型,或者视图属于隐藏类型且没有隐藏动画的。
6)继续调用applyAnimation方法
3、InsetsController的applyAnimation方法如下所示。
public class InsetsController implements WindowInsetsController, InsetsAnimationControlCallbacks {
public void applyAnimation(@InsetsType final int types, boolean show, boolean fromIme) {
boolean skipAnim = false;//默认跳过动画
if ((types & ime()) != 0) {
//如果是输入法则可能不跳过动画
final InsetsSourceConsumer consumer = mSourceConsumers.get(ITYPE_IME);
final InsetsSourceControl imeControl = consumer != null ? consumer.getControl() : null;
// Skip showing animation once that made by system for some reason.
// (e.g. starting window with IME snapshot)
if (imeControl != null) {
skipAnim = imeControl.getAndClearSkipAnimationOnce() && show
&& consumer.hasViewFocusWhenWindowFocusGain();
}
}
//继续调用applyAnimation
applyAnimation(types, show, fromIme, skipAnim);
}
public void applyAnimation(int types, boolean show, boolean fromIme, boolean skipAnim) {
if (types != 0) {
//是否有动画回调
boolean hasAnimationCallbacks = this.mHost.hasAnimationCallbacks();
//内部动画控制器监听者
InsetsController.InternalAnimationControlListener listener = new InsetsController.InternalAnimationControlListener(show, hasAnimationCallbacks, types, this.mHost.getSystemBarsBehavior(), skipAnim || this.mAnimationsDisabled, this.mHost.dipToPx(-80));
//继续调用controlAnimationUnchecked方法
this.controlAnimationUnchecked(types, (CancellationSignal)null, listener, (Rect)null, fromIme, listener.getDurationMs(), listener.getInsetsInterpolator(), show ? 0 : 1, show ? 0 : 1, !hasAnimationCallbacks);
}
}
private void controlAnimationUnchecked(int types, CancellationSignal cancellationSignal, WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme, long durationMs, Interpolator interpolator, int animationType, int layoutInsetsDuringAnimation, boolean useInsetsAnimationThread) {
...代码省略...
//继续调用updateRequestedVisibilities方法
this.updateRequestedVisibilities();
...代码省略...
}
}
经过层层调用,最终会调用InsetsController的updateRequestedVisibilities方法。
4、updateRequestedVisibilities方法如下所示。
public class InsetsController implements WindowInsetsController, InsetsAnimationControlCallbacks {
private final InsetsController.Host mHost;
public InsetsController(InsetsController.Host host) {
this(host, (controller, type) -> {
return (InsetsSourceConsumer)(type == 19 ? new ImeInsetsSourceConsumer(controller.mState, Transaction::new, controller) : new InsetsSourceConsumer(type, controller.mState, Transaction::new, controller));
}, host.getHandler());
}
public InsetsController(InsetsController.Host host, BiFunction<InsetsController, Integer, InsetsSourceConsumer> consumerCreator, Handler handler) {
...代码省略...
this.mHost = host;
...代码省略...
}
private void updateRequestedVisibilities() {
boolean changed = false;
for(int i = this.mRequestedVisibilityChanged.size() - 1; i >= 0; --i) {
InsetsSourceConsumer consumer = (InsetsSourceConsumer)this.mRequestedVisibilityChanged.valueAt(i);
int type = consumer.getType();
if (type != 2) {
boolean requestedVisible = consumer.isRequestedVisible();
if (this.mRequestedVisibilities.getVisibility(type) != requestedVisible) {
this.mRequestedVisibilities.setVisibility(type, requestedVisible);
changed = true;
}
}
}
this.mRequestedVisibilityChanged.clear();
if (changed) {
//调用mHost的updateRequestedVisibilities方法
this.mHost.updateRequestedVisibilities(this.mRequestedVisibilities);
}
}
}
最终会调用类型为mHost的updateRequestedVisibilities方法,而mHost是在构造方法中被赋值的,结合ViewRootImpl的构造方法我们知道这里的传入的对象实例是ViewRootInsetsControllerHost。
5、ViewRootInsetsControllerHost的updateRequestedVisibilities方法如下所示。
public class ViewRootInsetsControllerHost implements InsetsController.Host {
@Override
public void updateRequestedVisibilities(InsetsVisibilities vis) {
try {
if (mViewRoot.mAdded) {
//调用ViewRootImpl所对应的窗口会话的updateRequestedVisibilities方法
mViewRoot.mWindowSession.updateRequestedVisibilities(mViewRoot.mWindow, vis);
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to call insetsModified", e);
}
}
}
ViewRootInsetsControllerHost的updateRequestedVisibilities方法最终会调用ViewRootImpl所对应的窗口Session的updateRequestedVisibilities方法。
6、Session的updateRequestedVisibilities方法如下所示。
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
@Override
public void updateRequestedVisibilities(IWindow window, InsetsVisibilities visibilities) {
synchronized (mService.mGlobalLock) {
final WindowState windowState = mService.windowForClientLocked(this, window,
false /* throwOnError */);
if (windowState != null) {
windowState.setRequestedVisibilities(visibilities);
//继续调用
windowState.getDisplayContent().getInsetsPolicy().onInsetsModified(windowState);
}
}
}
}
Session的updateRequestedVisibilities方法会调用WindowState.getDisplayContent().getInsetsPolicy()方法获取InsetsPolicy对象实例,然后调用该对象的onInsetsModified方法。
7、InsetsPolicy的onInsetsModified方法如下所示。
class InsetsPolicy {
private final InsetsStateController mStateController;
void onInsetsModified(InsetsControlTarget caller) {
//调用InsetsStateController的onInsetsModified方法。
mStateController.onInsetsModified(caller);
checkAbortTransient(caller);
updateBarControlTarget(mFocusedWin);
}
}
8、InsetsStateController 的onInsetsModified方法如下所示。
class InsetsStateController{
void onInsetsModified(InsetsControlTarget caller) {
boolean changed = false;
for (int i = mProviders.size() - 1; i >= 0; i--) {
changed |= mProviders.valueAt(i).updateClientVisibility(caller);
}
if (changed) {
notifyInsetsChanged();
mDisplayContent.updateSystemGestureExclusion();
//调用DisplayPolicy的updateSystemBarAttributes方法
mDisplayContent.getDisplayPolicy().updateSystemBarAttributes();
}
}
}
9、DisplayPolicy的updateSystemBarAttributes方法如下所示。
public class DisplayPolicy {
void updateSystemBarAttributes() {
WindowState winCandidate = mFocusedWindow;
if (winCandidate == null && mTopFullscreenOpaqueWindowState != null
&& (mTopFullscreenOpaqueWindowState.mAttrs.flags
& WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0) {
// Only focusable window can take system bar control.
winCandidate = mTopFullscreenOpaqueWindowState;
}
// 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.
if (winCandidate == null) {
return;
}
// The immersive mode confirmation should never affect the system bar visibility, otherwise
// it will unhide the navigation bar and hide itself.
if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) {
// The immersive mode confirmation took the focus from mLastFocusedWindow which was
// controlling the system ui visibility. So if mLastFocusedWindow can still receive
// keys, we let it keep controlling the visibility.
final boolean lastFocusCanReceiveKeys =
(mLastFocusedWindow != null && mLastFocusedWindow.canReceiveKeys());
winCandidate = isKeyguardShowing() && !isKeyguardOccluded() ? mNotificationShade
: lastFocusCanReceiveKeys ? mLastFocusedWindow
: mTopFullscreenOpaqueWindowState;
if (winCandidate == null) {
return;
}
}
final WindowState win = winCandidate;
mSystemUiControllingWindow = win;
final int displayId = getDisplayId();
final int disableFlags = win.getDisableFlags();
final int opaqueAppearance = updateSystemBarsLw(win, disableFlags);
final WindowState navColorWin = chooseNavigationColorWindowLw(mNavBarColorWindowCandidate,
mDisplayContent.mInputMethodWindow, mNavigationBarPosition);
final boolean isNavbarColorManagedByIme =
navColorWin != null && navColorWin == mDisplayContent.mInputMethodWindow;
final int appearance = updateLightNavigationBarLw(win.mAttrs.insetsFlags.appearance,
navColorWin) | opaqueAppearance;
final int behavior = win.mAttrs.insetsFlags.behavior;
final String focusedApp = win.mAttrs.packageName;
final boolean isFullscreen = !win.getRequestedVisibility(ITYPE_STATUS_BAR)
|| !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR);
final AppearanceRegion[] appearanceRegions =
new AppearanceRegion[mStatusBarColorWindows.size()];
for (int i = mStatusBarColorWindows.size() - 1; i >= 0; i--) {
final WindowState windowState = mStatusBarColorWindows.get(i);
appearanceRegions[i] = new AppearanceRegion(
getStatusBarAppearance(windowState, windowState),
new Rect(windowState.getFrame()));
}
if (mLastDisableFlags != disableFlags) {
mLastDisableFlags = disableFlags;
final String cause = win.toString();
callStatusBarSafely(statusBar -> statusBar.setDisableFlags(displayId, disableFlags,
cause));
}
if (mLastAppearance == appearance
&& mLastBehavior == behavior
&& mRequestedVisibilities.equals(win.getRequestedVisibilities())
&& Objects.equals(mFocusedApp, focusedApp)
&& mLastFocusIsFullscreen == isFullscreen
&& Arrays.equals(mLastStatusBarAppearanceRegions, appearanceRegions)) {
return;
}
if (mDisplayContent.isDefaultDisplay && mLastFocusIsFullscreen != isFullscreen
&& ((mLastAppearance ^ appearance) & APPEARANCE_LOW_PROFILE_BARS) != 0) {
mService.mInputManager.setSystemUiLightsOut(
isFullscreen || (appearance & APPEARANCE_LOW_PROFILE_BARS) != 0);
}
final InsetsVisibilities requestedVisibilities =
new InsetsVisibilities(win.getRequestedVisibilities());
mLastAppearance = appearance;
mLastBehavior = behavior;
mRequestedVisibilities = requestedVisibilities;
mFocusedApp = focusedApp;
mLastFocusIsFullscreen = isFullscreen;
mLastStatusBarAppearanceRegions = appearanceRegions;
callStatusBarSafely(statusBar -> statusBar.onSystemBarAttributesChanged(displayId,
appearance, appearanceRegions, isNavbarColorManagedByIme, behavior,
requestedVisibilities, focusedApp));
}
}