From 9130150f17bfaab6ed778b11a91e4be3fe9fefe2 Mon Sep 17 00:00:00 2001 From: Ming-Shin Lu Date: Thu, 3 Nov 2022 13:59:17 +0000 Subject: [PATCH] Freezing IME insets when screen turned-off An animation flicker case happens when power off/on then unlocking the device to show an IME activity, sometimes will seeing the app layout jumps up-and-down after dismissing the snapshot starting window. The root cause is when turning off the screen, IME window will be hidden because of keyguard policy change. Then ImeInsetsProvider will receive onPostLayout to update the insets server frame as invisible and deliver this invisible insets change to the control target. Also, showing IME and updating IME insets visibility requires to take time until the IME starts the input after resuming the IME activity. As a result, fix this flicker issue with - Add ImeInsetsSourceProvider#setFrozen(boolean) to freeze the IME state when turning off the screen (And unfreeze when the IME is going to visible after unlocking the screen). - Override setServerVisible for ImeInsetsSourceProvider to freeze server visiblity when setFrozen(true) gets called. Bug: 246402296 Test: atest CtsInputMethodTestCases Test: atest WindowContainerTests ActivityRecordTests Test: atest ImeInsetsSourceProviderTest Test: manual with the issue steps: 1) Launch a chat app 2) Start a chat channel to show IME 3) Press power button to turn the screen off 4) Press power button to turn the screen on and unlock 5) Expect the chat activity looks fine without flickers Change-Id: Icfec9a491681406a382c74b690d8ef303848b32e --- .../com/android/server/wm/DisplayPolicy.java | 11 ++++++- .../server/wm/ImeInsetsSourceProvider.java | 32 +++++++++++++++++++ .../wm/ImeInsetsSourceProviderTest.java | 22 +++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 7bcea36415ccb..a1efd2d09184c 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -1638,7 +1638,16 @@ public class DisplayPolicy { */ private void applyKeyguardPolicy(WindowState win, WindowState imeTarget) { if (win.canBeHiddenByKeyguard()) { - if (shouldBeHiddenByKeyguard(win, imeTarget)) { + final boolean shouldBeHiddenByKeyguard = shouldBeHiddenByKeyguard(win, imeTarget); + if (win.mIsImWindow) { + // Notify IME insets provider to freeze the IME insets. In case when turning off + // the screen, the IME insets source window will be hidden because of keyguard + // policy change and affects the system to freeze the last insets state. (And + // unfreeze when the IME is going to show) + mDisplayContent.getInsetsStateController().getImeSourceProvider().setFrozen( + shouldBeHiddenByKeyguard); + } + if (shouldBeHiddenByKeyguard) { win.hide(false /* doAnimation */, true /* requestAnim */); } else { win.show(false /* doAnimation */, true /* requestAnim */); diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java index 38eca359c2bae..554791a0da918 100644 --- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -55,6 +55,12 @@ final class ImeInsetsSourceProvider extends WindowContainerInsetsSourceProvider private boolean mImeShowing; private final InsetsSource mLastSource = new InsetsSource(ITYPE_IME); + /** @see #setFrozen(boolean) */ + private boolean mFrozen; + + /** @see #setServerVisible(boolean) */ + private boolean mServerVisible; + ImeInsetsSourceProvider(InsetsSource source, InsetsStateController stateController, DisplayContent displayContent) { super(source, stateController, displayContent); @@ -80,6 +86,32 @@ final class ImeInsetsSourceProvider extends WindowContainerInsetsSourceProvider return control; } + @Override + void setServerVisible(boolean serverVisible) { + mServerVisible = serverVisible; + if (!mFrozen) { + super.setServerVisible(serverVisible); + } + } + + /** + * Freeze IME insets source state when required. + * + * When setting {@param frozen} as {@code true}, the IME insets provider will freeze the + * current IME insets state and pending the IME insets state update until setting + * {@param frozen} as {@code false}. + */ + void setFrozen(boolean frozen) { + if (mFrozen == frozen) { + return; + } + mFrozen = frozen; + if (!frozen) { + // Unfreeze and process the pending IME insets states. + super.setServerVisible(mServerVisible); + } + } + @Override void updateSourceFrame(Rect frame) { super.updateSourceFrame(frame); diff --git a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java index c839d1254397e..eb8b89d593107 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java @@ -85,4 +85,26 @@ public class ImeInsetsSourceProviderTest extends WindowTestsBase { mImeProvider.setImeShowing(false); assertFalse(mImeProvider.isImeShowing()); } + + @Test + public void testSetFrozen() { + WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime"); + makeWindowVisibleAndDrawn(ime); + mImeProvider.setWindowContainer(ime, null, null); + mImeProvider.setServerVisible(true); + mImeProvider.setClientVisible(true); + mImeProvider.updateVisibility(); + assertTrue(mImeProvider.getSource().isVisible()); + + // Freezing IME states and set the server visible as false. + mImeProvider.setFrozen(true); + mImeProvider.setServerVisible(false); + // Expect the IME insets visible won't be changed. + assertTrue(mImeProvider.getSource().isVisible()); + + // Unfreeze IME states and expect the IME insets became invisible due to pending IME + // visible state updated. + mImeProvider.setFrozen(false); + assertFalse(mImeProvider.getSource().isVisible()); + } }