From a39967f4bcf6a8f413c429403a4f4294190455da Mon Sep 17 00:00:00 2001 From: Tiger Huang Date: Mon, 10 May 2021 18:36:27 +0800 Subject: [PATCH] Force showing navigation bar while IME is visible This CL revokes navigation bar control to make navigation bar visible while IME is showing, so the button to dismiss IME would be available. This CL also lets IME receive visible navigation bar insets, regardless of the navigation bar visibility. Fix: 167971834 Fix: 186789472 Test: atest WindowStateTests WindowInsetsControllerTests Change-Id: I2e723d4fc50d006127caa473d67c2f6af0d2cbcd --- .../view/InsetsAnimationControlImpl.java | 12 +++++++ .../view/InsetsAnimationControlRunner.java | 13 ++++++- .../InsetsAnimationThreadControlRunner.java | 11 ++++++ core/java/android/view/InsetsController.java | 4 ++- .../com/android/server/wm/InsetsPolicy.java | 35 ++++++++++++++++--- .../android/server/wm/WindowStateTests.java | 18 ++++++++++ 6 files changed, 86 insertions(+), 7 deletions(-) diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java index a67ec10e1bd77..3255dd65b1930 100644 --- a/core/java/android/view/InsetsAnimationControlImpl.java +++ b/core/java/android/view/InsetsAnimationControlImpl.java @@ -86,6 +86,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll private final InsetsState mInitialInsetsState; private final @AnimationType int mAnimationType; private final @InsetsType int mTypes; + private @InsetsType int mControllingTypes; private final InsetsAnimationControlCallbacks mController; private final WindowInsetsAnimation mAnimation; /** @see WindowInsetsAnimationController#hasZeroInsetsIme */ @@ -112,6 +113,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll mControls = controls; mListener = listener; mTypes = types; + mControllingTypes = types; mController = controller; mInitialInsetsState = new InsetsState(state, true /* copySources */); if (frame != null) { @@ -186,6 +188,16 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll return mTypes; } + @Override + public int getControllingTypes() { + return mControllingTypes; + } + + @Override + public void notifyControlRevoked(@InsetsType int types) { + mControllingTypes &= ~types; + } + @Override public @AnimationType int getAnimationType() { return mAnimationType; diff --git a/core/java/android/view/InsetsAnimationControlRunner.java b/core/java/android/view/InsetsAnimationControlRunner.java index 0275b521a2a27..7787af5e133bb 100644 --- a/core/java/android/view/InsetsAnimationControlRunner.java +++ b/core/java/android/view/InsetsAnimationControlRunner.java @@ -29,10 +29,21 @@ import android.view.WindowInsets.Type.InsetsType; public interface InsetsAnimationControlRunner { /** - * @return The {@link InsetsType} the animation of this runner is controlling. + * @return The {@link InsetsType} the animation of this runner controls. */ @InsetsType int getTypes(); + /** + * @return The {@link InsetsType} the animation of this runner is controlling. This can be + * changed if a control is revoked. + */ + @InsetsType int getControllingTypes(); + + /** + * Notifies {@link InsetsType types} of control are getting revoked. + */ + void notifyControlRevoked(@InsetsType int types); + /** * Cancels the animation. */ diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java index 6122c90ce06e9..436a17c0de0b9 100644 --- a/core/java/android/view/InsetsAnimationThreadControlRunner.java +++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java @@ -139,6 +139,17 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro return mControl.getTypes(); } + @Override + @UiThread + public int getControllingTypes() { + return mControl.getControllingTypes(); + } + + @Override + public void notifyControlRevoked(@InsetsType int types) { + mControl.notifyControlRevoked(types); + } + @Override @UiThread public void cancel() { diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index def9ca4e74fb7..de7cc18531a9a 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -1224,9 +1224,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } void notifyControlRevoked(InsetsSourceConsumer consumer) { + final @InsetsType int types = toPublicType(consumer.getType()); for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner; - if ((control.getTypes() & toPublicType(consumer.getType())) != 0) { + control.notifyControlRevoked(types); + if (control.getControllingTypes() == 0) { cancelAnimation(control, true /* invokeCallback */); } } diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index cda3fdaea5370..c387d338a3b27 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.view.InsetsController.ANIMATION_TYPE_HIDE; import static android.view.InsetsController.ANIMATION_TYPE_SHOW; +import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.SyncRtSurfaceTransactionApplier.applyParams; @@ -207,7 +208,8 @@ class InsetsPolicy { */ InsetsState getInsetsForWindow(WindowState target) { final InsetsState originalState = mStateController.getInsetsForWindow(target); - return adjustVisibilityForTransientTypes(originalState); + final InsetsState state = adjustVisibilityForTransientTypes(originalState); + return target.mIsImWindow ? adjustVisibilityForIme(state, state == originalState) : state; } /** @@ -237,6 +239,20 @@ class InsetsPolicy { return state; } + // Navigation bar insets is always visible to IME. + private static InsetsState adjustVisibilityForIme(InsetsState originalState, + boolean copyState) { + final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR); + if (originalNavSource != null && !originalNavSource.isVisible()) { + final InsetsState state = copyState ? new InsetsState(originalState) : originalState; + final InsetsSource navSource = new InsetsSource(originalNavSource); + navSource.setVisible(true); + state.addSource(navSource); + return state; + } + return originalState; + } + void onInsetsModified(InsetsControlTarget caller) { mStateController.onInsetsModified(caller); checkAbortTransient(caller); @@ -245,17 +261,21 @@ class InsetsPolicy { /** * Called when a control target modified the insets state. If the target set a insets source to - * visible while it is shown transiently, we need to abort the transient state. + * visible while it is shown transiently, we need to abort the transient state. While IME is + * requested visible, we also need to abort the transient state of navigation bar if it is shown + * transiently. * * @param caller who changed the insets state. */ private void checkAbortTransient(InsetsControlTarget caller) { if (mShowingTransientTypes.size() != 0) { - IntArray abortTypes = new IntArray(); + final IntArray abortTypes = new IntArray(); + final boolean imeRequestedVisible = caller.getRequestedVisibility(ITYPE_IME); for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) { final @InternalInsetsType int type = mShowingTransientTypes.get(i); - if (mStateController.isFakeTarget(type, caller) - && caller.getRequestedVisibility(type)) { + if ((mStateController.isFakeTarget(type, caller) + && caller.getRequestedVisibility(type)) + || (type == ITYPE_NAVIGATION_BAR && imeRequestedVisible)) { mShowingTransientTypes.remove(i); abortTypes.add(type); } @@ -330,6 +350,11 @@ class InsetsPolicy { private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin, boolean forceShowsSystemBarsForWindowingMode) { + final WindowState imeWin = mDisplayContent.mInputMethodWindow; + if (imeWin != null && imeWin.isVisible()) { + // Force showing navigation bar while IME is visible. + return null; + } if (mShowingTransientTypes.indexOf(ITYPE_NAVIGATION_BAR) != -1) { return mDummyControlTarget; } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 8d067beff44cc..e3b0bb263830f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_270; @@ -78,6 +79,7 @@ import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.view.Gravity; import android.view.InputWindowHandle; +import android.view.InsetsSource; import android.view.InsetsState; import android.view.SurfaceControl; import android.view.WindowManager; @@ -880,6 +882,22 @@ public class WindowStateTests extends WindowTestsBase { verify(app).notifyInsetsChanged(); } + @UseTestDisplay(addWindows = { W_INPUT_METHOD, W_ACTIVITY }) + @Test + public void testImeAlwaysReceivesVisibleNavigationBarInsets() { + final InsetsSource navSource = new InsetsSource(ITYPE_NAVIGATION_BAR); + mImeWindow.mAboveInsetsState.addSource(navSource); + mAppWindow.mAboveInsetsState.addSource(navSource); + + navSource.setVisible(false); + assertTrue(mImeWindow.getInsetsState().getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR)); + assertFalse(mAppWindow.getInsetsState().getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR)); + + navSource.setVisible(true); + assertTrue(mImeWindow.getInsetsState().getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR)); + assertTrue(mAppWindow.getInsetsState().getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR)); + } + @UseTestDisplay(addWindows = { W_ACTIVITY }) @Test public void testUpdateImeControlTargetWhenLeavingMultiWindow() {