Android 12系统源码_SystemUI(九)WindowInsetsController源码解析
前言
上一篇我们具体分析了在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;}@Overridepublic 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);//遍历internalTypesfor (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) {//是输入法,此类继承自InsetsSourceConsumerreturn 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();}}//继续调用applyAnimationapplyAnimation(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 {@Overridepublic 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 {@Overridepublic 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));}
}