From b9510efabdf3393502f2f0442e4e0bb7f02e5a80 Mon Sep 17 00:00:00 2001 From: Tiger Huang Date: Tue, 3 Mar 2020 23:03:39 +0800 Subject: [PATCH] Prevent causing insets if the system bar is shown forcibly Forcibly-shown system bars are usually transient. Prevent them from causing insets can reduce the UI shakiness, and can be compatible with the behavior in the legacy insets mode. Fix: 150582388 Test: atest InsetsSourceProviderTest InsetsStateControllerTest InsetsPolicyTest Change-Id: I3c0fa4fb7555b2f63e1c4849db7b169489ab64e4 --- core/java/android/view/InsetsState.java | 14 +++ .../server/wm/InsetsControlTarget.java | 7 +- .../com/android/server/wm/InsetsPolicy.java | 86 +++++++++---------- .../server/wm/InsetsStateController.java | 3 +- .../android/server/wm/InsetsPolicyTest.java | 9 +- 5 files changed, 67 insertions(+), 52 deletions(-) diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index b740c58ec15f6..e4aa410742213 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -342,6 +342,20 @@ public class InsetsState implements Parcelable { } } + /** + * A shortcut for setting the visibility of the source. + * + * @param type The {@link InternalInsetsType} of the source to set the visibility + * @param referenceState The {@link InsetsState} for reference + */ + public void setSourceVisible(@InternalInsetsType int type, InsetsState referenceState) { + InsetsSource source = mSources.get(type); + InsetsSource referenceSource = referenceState.mSources.get(type); + if (source != null && referenceSource != null) { + source.setVisible(referenceSource.isVisible()); + } + } + public void set(InsetsState other) { set(other, false /* copySources */); } diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java index 20e8f2ba485ab..bbc6c2bd4bcd8 100644 --- a/services/core/java/com/android/server/wm/InsetsControlTarget.java +++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java @@ -23,7 +23,12 @@ import android.view.WindowInsets.Type.InsetsType; * Generalization of an object that can control insets state. */ interface InsetsControlTarget { - void notifyInsetsControlChanged(); + + /** + * Notifies the control target that the insets control has changed. + */ + default void notifyInsetsControlChanged() { + }; /** * @return {@link WindowState} of this target, if any. diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index f6bf39739cb86..01f98888c7778 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -57,9 +57,11 @@ class InsetsPolicy { private final InsetsStateController mStateController; private final DisplayContent mDisplayContent; private final DisplayPolicy mPolicy; - private final TransientControlTarget mTransientControlTarget = new TransientControlTarget(); private final IntArray mShowingTransientTypes = new IntArray(); + /** For resetting visibilities of insets sources. */ + private final InsetsControlTarget mDummyControlTarget = new InsetsControlTarget() { }; + private WindowState mFocusedWin; private BarWindow mStatusBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR); private BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR); @@ -143,12 +145,15 @@ class InsetsPolicy { */ InsetsState getInsetsForDispatch(WindowState target) { InsetsState state = mStateController.getInsetsForDispatch(target); - if (mShowingTransientTypes.size() == 0) { - return state; - } for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) { state.setSourceVisible(mShowingTransientTypes.get(i), false); } + if (mFocusedWin != null && getStatusControlTarget(mFocusedWin) == mDummyControlTarget) { + state.setSourceVisible(ITYPE_STATUS_BAR, mFocusedWin.getRequestedInsetsState()); + } + if (mFocusedWin != null && getNavControlTarget(mFocusedWin) == mDummyControlTarget) { + state.setSourceVisible(ITYPE_NAVIGATION_BAR, mFocusedWin.getRequestedInsetsState()); + } return state; } @@ -194,71 +199,71 @@ class InsetsPolicy { private @Nullable InsetsControlTarget getFakeStatusControlTarget( @Nullable WindowState focused) { - if (mShowingTransientTypes.indexOf(ITYPE_STATUS_BAR) != -1) { - return focused; - } - return null; + return getStatusControlTarget(focused) == mDummyControlTarget ? focused : null; } private @Nullable InsetsControlTarget getFakeNavControlTarget(@Nullable WindowState focused) { - if (mShowingTransientTypes.indexOf(ITYPE_NAVIGATION_BAR) != -1) { - return focused; - } - return null; + return getNavControlTarget(focused) == mDummyControlTarget ? focused : null; } private @Nullable InsetsControlTarget getStatusControlTarget(@Nullable WindowState focusedWin) { if (mShowingTransientTypes.indexOf(ITYPE_STATUS_BAR) != -1) { - return mTransientControlTarget; + return mDummyControlTarget; } if (focusedWin == mPolicy.getNotificationShade()) { // Notification shade has control anyways, no reason to force anything. return focusedWin; } - if (areSystemBarsForciblyVisible() || isKeyguardOrStatusBarForciblyVisible()) { + if (forceShowsSystemBarsForWindowingMode()) { + // Status bar is forcibly shown for the windowing mode which is a steady state. + // We don't want the client to control the status bar, and we will dispatch the real + // visibility of status bar to the client. return null; } + if (forceShowsStatusBarTransiently()) { + // Status bar is forcibly shown transiently, and its new visibility won't be + // dispatched to the client so that we can keep the layout stable. We will dispatch the + // fake control to the client, so that it can re-show the bar during this scenario. + return mDummyControlTarget; + } return focusedWin; } private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin) { if (mShowingTransientTypes.indexOf(ITYPE_NAVIGATION_BAR) != -1) { - return mTransientControlTarget; + return mDummyControlTarget; } if (focusedWin == mPolicy.getNotificationShade()) { // Notification shade has control anyways, no reason to force anything. return focusedWin; } - if (areSystemBarsForciblyVisible() || isNavBarForciblyVisible()) { + if (forceShowsSystemBarsForWindowingMode()) { + // Navigation bar is forcibly shown for the windowing mode which is a steady state. + // We don't want the client to control the navigation bar, and we will dispatch the real + // visibility of navigation bar to the client. return null; } + if (forceShowsNavigationBarTransiently()) { + // Navigation bar is forcibly shown transiently, and its new visibility won't be + // dispatched to the client so that we can keep the layout stable. We will dispatch the + // fake control to the client, so that it can re-show the bar during this scenario. + return mDummyControlTarget; + } return focusedWin; } - private boolean isKeyguardOrStatusBarForciblyVisible() { - final WindowState statusBar = mPolicy.getStatusBar(); - if (statusBar != null) { - // TODO(b/118118435): Pretend to the app that it's still able to control it? - if ((statusBar.mAttrs.privateFlags & PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR) != 0) { - return true; - } - } - return false; + private boolean forceShowsStatusBarTransiently() { + final WindowState win = mPolicy.getStatusBar(); + return win != null && (win.mAttrs.privateFlags & PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR) != 0; } - private boolean isNavBarForciblyVisible() { - final WindowState notificationShade = mPolicy.getNotificationShade(); - if (notificationShade == null) { - return false; - } - if ((notificationShade.mAttrs.privateFlags - & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0) { - return true; - } - return false; + private boolean forceShowsNavigationBarTransiently() { + final WindowState win = mPolicy.getNotificationShade(); + return win != null + && (win.mAttrs.privateFlags & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0; } - private boolean areSystemBarsForciblyVisible() { + private boolean forceShowsSystemBarsForWindowingMode() { final boolean isDockedStackVisible = mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); final boolean isFreeformStackVisible = @@ -279,7 +284,7 @@ class InsetsPolicy { for (int i = showingTransientTypes.size() - 1; i >= 0; i--) { InsetsSourceProvider provider = mStateController.getSourceProvider(showingTransientTypes.get(i)); - InsetsSourceControl control = provider.getControl(mTransientControlTarget); + InsetsSourceControl control = provider.getControl(mDummyControlTarget); if (control == null || control.getLeash() == null) { continue; } @@ -412,11 +417,4 @@ class InsetsPolicy { } } } - - private class TransientControlTarget implements InsetsControlTarget { - - @Override - public void notifyInsetsControlChanged() { - } - } } diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index b3890cd84196d..e1906da3378fe 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -61,8 +61,7 @@ class InsetsStateController { w.notifyInsetsChanged(); } }; - private final InsetsControlTarget mEmptyImeControlTarget = () -> { - }; + private final InsetsControlTarget mEmptyImeControlTarget = new InsetsControlTarget() { }; InsetsStateController(DisplayContent displayContent) { mDisplayContent = displayContent; diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java index eae007d3a767e..5e30477e1e3ff 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java @@ -120,7 +120,6 @@ public class InsetsPolicyTest extends WindowTestsBase { assertNull(controls); } - // TODO: adjust this test if we pretend to the app that it's still able to control it. @Test public void testControlsForDispatch_forceStatusBarVisible() { addWindow(TYPE_STATUS_BAR, "statusBar").mAttrs.privateFlags |= @@ -129,9 +128,9 @@ public class InsetsPolicyTest extends WindowTestsBase { final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch(); - // The app must not control the status bar. + // The focused app window can control both system bars. assertNotNull(controls); - assertEquals(1, controls.length); + assertEquals(2, controls.length); } @Test @@ -143,9 +142,9 @@ public class InsetsPolicyTest extends WindowTestsBase { final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch(); - // The app must not control the navigation bar. + // The focused app window can control both system bars. assertNotNull(controls); - assertEquals(1, controls.length); + assertEquals(2, controls.length); } @Test