Loading core/java/android/view/ImeInsetsSourceConsumer.java +8 −7 Original line number Diff line number Diff line Loading @@ -65,6 +65,9 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { public void onWindowFocusGained(boolean hasViewFocus) { super.onWindowFocusGained(hasViewFocus); getImm().registerImeConsumer(this); if (isRequestedVisible() && getControl() == null) { mIsRequestedVisibleAwaitingControl = true; } } @Override Loading Loading @@ -149,14 +152,11 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { } @Override public void setControl(@Nullable InsetsSourceControl control, int[] showTypes, public boolean setControl(@Nullable InsetsSourceControl control, int[] showTypes, int[] hideTypes) { super.setControl(control, showTypes, hideTypes); // TODO(b/204524304): clean-up how to deal with the timing issues of hiding IME: // 1) Already requested show IME, in the meantime of WM callback the control but got null // control when relayout comes first // 2) Make sure no regression on some implicit request IME visibility calls (e.g. // toggleSoftInput) if (!super.setControl(control, showTypes, hideTypes)) { return false; } if (control == null && !mIsRequestedVisibleAwaitingControl) { hide(); removeSurface(); Loading @@ -164,6 +164,7 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { if (control != null) { mIsRequestedVisibleAwaitingControl = false; } return true; } @Override Loading core/java/android/view/InsetsSourceConsumer.java +4 −2 Original line number Diff line number Diff line Loading @@ -122,8 +122,9 @@ public class InsetsSourceConsumer { * animation should be run after setting the control. * @param hideTypes An integer array with a single entry that determines which types a hide * animation should be run after setting the control. * @return Whether the control has changed from the server */ public void setControl(@Nullable InsetsSourceControl control, public boolean setControl(@Nullable InsetsSourceControl control, @InsetsType int[] showTypes, @InsetsType int[] hideTypes) { if (mType == ITYPE_IME) { ImeTracing.getInstance().triggerClientDump("InsetsSourceConsumer#setControl", Loading @@ -134,7 +135,7 @@ public class InsetsSourceConsumer { mSourceControl.release(SurfaceControl::release); mSourceControl = control; } return; return false; } SurfaceControl oldLeash = mSourceControl != null ? mSourceControl.getLeash() : null; Loading Loading @@ -201,6 +202,7 @@ public class InsetsSourceConsumer { if (lastControl != null) { lastControl.release(SurfaceControl::release); } return true; } @VisibleForTesting Loading tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt +30 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,10 @@ package com.android.server.wm.flicker.helpers import android.view.WindowInsets.Type.ime import android.view.WindowInsets.Type.navigationBars import android.view.WindowInsets.Type.statusBars import android.app.Instrumentation import androidx.test.uiautomator.By import androidx.test.uiautomator.UiDevice Loading @@ -25,6 +29,8 @@ import com.android.server.wm.traces.common.FlickerComponentName import com.android.server.wm.traces.parser.toFlickerComponent import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import java.util.regex.Pattern class ImeAppAutoFocusHelper @JvmOverloads constructor( instr: Instrumentation, private val rotation: Int, Loading Loading @@ -72,6 +78,7 @@ class ImeAppAutoFocusHelper @JvmOverloads constructor( wmHelper.waitForAppTransitionIdle() wmHelper.waitForFullScreenApp( ActivityOptions.DIALOG_THEMED_ACTIVITY_COMPONENT_NAME.toFlickerComponent()) mInstrumentation.waitForIdleSync() } fun dismissDialog(wmHelper: WindowManagerStateHelper) { val dialog = uiDevice.wait( Loading @@ -83,4 +90,27 @@ class ImeAppAutoFocusHelper @JvmOverloads constructor( wmHelper.waitForAppTransitionIdle() } } fun getInsetsVisibleFromDialog(type: Int): Boolean { var insetsVisibilityTextView = uiDevice.wait( Until.findObject(By.res("android:id/text1")), FIND_TIMEOUT) if (insetsVisibilityTextView != null) { var visibility = insetsVisibilityTextView.text.toString() val matcher = when (type) { ime() -> { Pattern.compile("IME\\: (VISIBLE|INVISIBLE)").matcher(visibility) } statusBars() -> { Pattern.compile("StatusBar\\: (VISIBLE|INVISIBLE)").matcher(visibility) } navigationBars() -> { Pattern.compile("NavBar\\: (VISIBLE|INVISIBLE)").matcher(visibility) } else -> null } if (matcher != null && matcher.find()) { return matcher.group(1).equals("VISIBLE") } } return false } } tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt +10 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,10 @@ package com.android.server.wm.flicker.ime import android.view.WindowInsets.Type.ime import android.view.WindowInsets.Type.navigationBars import android.view.WindowInsets.Type.statusBars import android.app.Instrumentation import android.platform.test.annotations.Presubmit import android.view.Surface Loading @@ -35,6 +39,8 @@ import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue /** * Test IME snapshot mechanism won't apply when transitioning from non-IME focused dialog activity. Loading @@ -56,6 +62,10 @@ class LaunchAppShowImeAndDialogThemeAppTest(private val testSpec: FlickerTestPar testApp.launchViaIntent(wmHelper) wmHelper.waitImeShown() testApp.startDialogThemedActivity(wmHelper) // Verify IME insets isn't visible on dialog since it's non-IME focusable window assertFalse(testApp.getInsetsVisibleFromDialog(ime())) assertTrue(testApp.getInsetsVisibleFromDialog(statusBars())) assertTrue(testApp.getInsetsVisibleFromDialog(navigationBars())) } } teardown { Loading tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/DialogThemedActivity.java +19 −1 Original line number Diff line number Diff line Loading @@ -17,11 +17,16 @@ package com.android.server.wm.flicker.testapp; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.navigationBars; import static android.view.WindowInsets.Type.statusBars; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.graphics.Color; import android.os.Bundle; import android.view.WindowManager; Loading @@ -33,9 +38,12 @@ public class DialogThemedActivity extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_simple); getWindow().addFlags(FLAG_NOT_FOCUSABLE); getWindow().getDecorView().setBackgroundColor(Color.TRANSPARENT); TextView textView = new TextView(this); textView.setText("This is a test dialog"); // Print SystemBars' insets visibility on this window for demonstrating during the test. textView.setId(android.R.id.text1); textView.setText("Insets visibility\n\n"); textView.setTextColor(Color.BLACK); LinearLayout layout = new LinearLayout(this); layout.setBackgroundColor(Color.GREEN); Loading @@ -51,7 +59,17 @@ public class DialogThemedActivity extends Activity { attrs.flags = FLAG_DIM_BEHIND | FLAG_ALT_FOCUSABLE_IM; dialog.getWindow().getDecorView().setLayoutParams(attrs); dialog.setCanceledOnTouchOutside(true); dialog.setOnShowListener(d -> textView.setText(textView.getText() + "IME: " + isInsetsVisible(dialog, ime()) + "\n" + "StatusBar: " + isInsetsVisible(dialog, statusBars()) + "\n" + "NavBar: " + isInsetsVisible(dialog, navigationBars()) + "\n") ); dialog.show(); dialog.setOnDismissListener((d) -> finish()); } private String isInsetsVisible(Dialog d, int type) { return d.getWindow().getDecorView().getRootWindowInsets().isVisible(type) ? "VISIBLE" : "INVISIBLE"; } } Loading
core/java/android/view/ImeInsetsSourceConsumer.java +8 −7 Original line number Diff line number Diff line Loading @@ -65,6 +65,9 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { public void onWindowFocusGained(boolean hasViewFocus) { super.onWindowFocusGained(hasViewFocus); getImm().registerImeConsumer(this); if (isRequestedVisible() && getControl() == null) { mIsRequestedVisibleAwaitingControl = true; } } @Override Loading Loading @@ -149,14 +152,11 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { } @Override public void setControl(@Nullable InsetsSourceControl control, int[] showTypes, public boolean setControl(@Nullable InsetsSourceControl control, int[] showTypes, int[] hideTypes) { super.setControl(control, showTypes, hideTypes); // TODO(b/204524304): clean-up how to deal with the timing issues of hiding IME: // 1) Already requested show IME, in the meantime of WM callback the control but got null // control when relayout comes first // 2) Make sure no regression on some implicit request IME visibility calls (e.g. // toggleSoftInput) if (!super.setControl(control, showTypes, hideTypes)) { return false; } if (control == null && !mIsRequestedVisibleAwaitingControl) { hide(); removeSurface(); Loading @@ -164,6 +164,7 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { if (control != null) { mIsRequestedVisibleAwaitingControl = false; } return true; } @Override Loading
core/java/android/view/InsetsSourceConsumer.java +4 −2 Original line number Diff line number Diff line Loading @@ -122,8 +122,9 @@ public class InsetsSourceConsumer { * animation should be run after setting the control. * @param hideTypes An integer array with a single entry that determines which types a hide * animation should be run after setting the control. * @return Whether the control has changed from the server */ public void setControl(@Nullable InsetsSourceControl control, public boolean setControl(@Nullable InsetsSourceControl control, @InsetsType int[] showTypes, @InsetsType int[] hideTypes) { if (mType == ITYPE_IME) { ImeTracing.getInstance().triggerClientDump("InsetsSourceConsumer#setControl", Loading @@ -134,7 +135,7 @@ public class InsetsSourceConsumer { mSourceControl.release(SurfaceControl::release); mSourceControl = control; } return; return false; } SurfaceControl oldLeash = mSourceControl != null ? mSourceControl.getLeash() : null; Loading Loading @@ -201,6 +202,7 @@ public class InsetsSourceConsumer { if (lastControl != null) { lastControl.release(SurfaceControl::release); } return true; } @VisibleForTesting Loading
tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt +30 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,10 @@ package com.android.server.wm.flicker.helpers import android.view.WindowInsets.Type.ime import android.view.WindowInsets.Type.navigationBars import android.view.WindowInsets.Type.statusBars import android.app.Instrumentation import androidx.test.uiautomator.By import androidx.test.uiautomator.UiDevice Loading @@ -25,6 +29,8 @@ import com.android.server.wm.traces.common.FlickerComponentName import com.android.server.wm.traces.parser.toFlickerComponent import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import java.util.regex.Pattern class ImeAppAutoFocusHelper @JvmOverloads constructor( instr: Instrumentation, private val rotation: Int, Loading Loading @@ -72,6 +78,7 @@ class ImeAppAutoFocusHelper @JvmOverloads constructor( wmHelper.waitForAppTransitionIdle() wmHelper.waitForFullScreenApp( ActivityOptions.DIALOG_THEMED_ACTIVITY_COMPONENT_NAME.toFlickerComponent()) mInstrumentation.waitForIdleSync() } fun dismissDialog(wmHelper: WindowManagerStateHelper) { val dialog = uiDevice.wait( Loading @@ -83,4 +90,27 @@ class ImeAppAutoFocusHelper @JvmOverloads constructor( wmHelper.waitForAppTransitionIdle() } } fun getInsetsVisibleFromDialog(type: Int): Boolean { var insetsVisibilityTextView = uiDevice.wait( Until.findObject(By.res("android:id/text1")), FIND_TIMEOUT) if (insetsVisibilityTextView != null) { var visibility = insetsVisibilityTextView.text.toString() val matcher = when (type) { ime() -> { Pattern.compile("IME\\: (VISIBLE|INVISIBLE)").matcher(visibility) } statusBars() -> { Pattern.compile("StatusBar\\: (VISIBLE|INVISIBLE)").matcher(visibility) } navigationBars() -> { Pattern.compile("NavBar\\: (VISIBLE|INVISIBLE)").matcher(visibility) } else -> null } if (matcher != null && matcher.find()) { return matcher.group(1).equals("VISIBLE") } } return false } }
tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt +10 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,10 @@ package com.android.server.wm.flicker.ime import android.view.WindowInsets.Type.ime import android.view.WindowInsets.Type.navigationBars import android.view.WindowInsets.Type.statusBars import android.app.Instrumentation import android.platform.test.annotations.Presubmit import android.view.Surface Loading @@ -35,6 +39,8 @@ import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue /** * Test IME snapshot mechanism won't apply when transitioning from non-IME focused dialog activity. Loading @@ -56,6 +62,10 @@ class LaunchAppShowImeAndDialogThemeAppTest(private val testSpec: FlickerTestPar testApp.launchViaIntent(wmHelper) wmHelper.waitImeShown() testApp.startDialogThemedActivity(wmHelper) // Verify IME insets isn't visible on dialog since it's non-IME focusable window assertFalse(testApp.getInsetsVisibleFromDialog(ime())) assertTrue(testApp.getInsetsVisibleFromDialog(statusBars())) assertTrue(testApp.getInsetsVisibleFromDialog(navigationBars())) } } teardown { Loading
tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/DialogThemedActivity.java +19 −1 Original line number Diff line number Diff line Loading @@ -17,11 +17,16 @@ package com.android.server.wm.flicker.testapp; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.navigationBars; import static android.view.WindowInsets.Type.statusBars; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.graphics.Color; import android.os.Bundle; import android.view.WindowManager; Loading @@ -33,9 +38,12 @@ public class DialogThemedActivity extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_simple); getWindow().addFlags(FLAG_NOT_FOCUSABLE); getWindow().getDecorView().setBackgroundColor(Color.TRANSPARENT); TextView textView = new TextView(this); textView.setText("This is a test dialog"); // Print SystemBars' insets visibility on this window for demonstrating during the test. textView.setId(android.R.id.text1); textView.setText("Insets visibility\n\n"); textView.setTextColor(Color.BLACK); LinearLayout layout = new LinearLayout(this); layout.setBackgroundColor(Color.GREEN); Loading @@ -51,7 +59,17 @@ public class DialogThemedActivity extends Activity { attrs.flags = FLAG_DIM_BEHIND | FLAG_ALT_FOCUSABLE_IM; dialog.getWindow().getDecorView().setLayoutParams(attrs); dialog.setCanceledOnTouchOutside(true); dialog.setOnShowListener(d -> textView.setText(textView.getText() + "IME: " + isInsetsVisible(dialog, ime()) + "\n" + "StatusBar: " + isInsetsVisible(dialog, statusBars()) + "\n" + "NavBar: " + isInsetsVisible(dialog, navigationBars()) + "\n") ); dialog.show(); dialog.setOnDismissListener((d) -> finish()); } private String isInsetsVisible(Dialog d, int type) { return d.getWindow().getDecorView().getRootWindowInsets().isVisible(type) ? "VISIBLE" : "INVISIBLE"; } }