diff options
| author | 2023-01-13 08:19:01 +0000 | |
|---|---|---|
| committer | 2023-01-31 13:55:44 +0000 | |
| commit | 9053d728be57f6cd356f6113fb3e10eaa7863727 (patch) | |
| tree | 175f5a7e18cc27cfa0e7a3c64d648cf16f1d3358 | |
| parent | 8165fae2c8286f1b620e47aa741980f60849a5b6 (diff) | |
Fix IME being shifted when the app setRequestedOrientation
Fix an edge case happens on large-screen devices that
DisplayContent#updateImeParent may not be called to update IME surface
parent from the activity to the display area when the app actvitity is
letterboxed by
1) activity handles configChange without being relaunched
by window configuration change
2) calling setRequestedOrientation to fix orientation as portrait
In this case, if the activity bounds size is not suitable to attach
IME surface, we need to recompute the IME surface parent in
WindowState#onConfigurationChanged to make IME surface can be placed on
the display area to avoid IME position being shifted by
letterboxed activity.
Fix: 249081451
Test: atest FlickerTests:OpenImeWindowToFixedPortraitAppTest
Merged-In: I7a66fd84e4094be249714c2597706ee25938adbe
Change-Id: I7a66fd84e4094be249714c2597706ee25938adbe
7 files changed, 186 insertions, 16 deletions
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 04699619bfc8..72f791847e35 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2395,7 +2395,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // IME parent may failed to attach to the app during rotating the screen. // See DisplayContent#shouldImeAttachedToApp, DisplayContent#isImeControlledByApp if (windowConfigChanged) { - getDisplayContent().updateImeControlTarget(); + // If the window was the IME layering target, updates the IME surface parent in case + // the IME surface may be wrongly positioned when the window configuration affects the + // IME surface association. (e.g. Attach IME surface on the display instead of the + // app when the app bounds being letterboxed.) + mDisplayContent.updateImeControlTarget(isImeLayeringTarget() /* updateImeParent */); } } 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 0568f2acf366..b178ee26520a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -1119,7 +1119,9 @@ public class WindowStateTests extends WindowTestsBase { spyOn(app.getDisplayContent()); app.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN); - verify(app.getDisplayContent()).updateImeControlTarget(); + // Expect updateImeParent will be invoked when the configuration of the IME control + // target has changed. + verify(app.getDisplayContent()).updateImeControlTarget(eq(true) /* updateImeParent */); assertEquals(mAppWindow, mDisplayContent.getImeTarget(IME_TARGET_CONTROL).getWindow()); } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt index aacc17a49a24..3361502f2ea0 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt @@ -113,4 +113,18 @@ class ImeAppAutoFocusHelper @JvmOverloads constructor( } return false } + + fun toggleFixPortraitOrientation(wmHelper: WindowManagerStateHelper) { + val button = uiDevice.wait(Until.findObject(By.res(getPackage(), + "toggle_fixed_portrait_btn")), FIND_TIMEOUT) + require(button != null) { + "Button not found, this usually happens when the device " + + "was left in an unknown state (e.g. Screen turned off)" + } + button.click() + mInstrumentation.waitForIdleSync() + // Ensure app relaunching transition finish and the IME has shown + wmHelper.waitForAppTransitionIdle() + wmHelper.waitImeShown() + } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt new file mode 100644 index 000000000000..3b3bce6f39ba --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.wm.flicker.ime + +import android.app.Instrumentation +import android.platform.test.annotations.Postsubmit +import android.view.Surface +import android.view.WindowManagerPolicyConstants +import androidx.test.filters.RequiresDevice +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.annotation.Group2 +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper +import com.android.server.wm.flicker.helpers.WindowUtils +import com.android.server.wm.flicker.traces.region.RegionSubject +import com.android.server.wm.traces.common.FlickerComponentName +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test IME window shown on the app with fixing portrait orientation. + * To run this test: `atest FlickerTests:OpenImeWindowToFixedPortraitAppTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Group2 +class OpenImeWindowToFixedPortraitAppTest (private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + setup { + eachRun { + testApp.launchViaIntent(wmHelper) + testApp.openIME(device, wmHelper) + // Enable letterbox when the app calls setRequestedOrientation + device.executeShellCommand("cmd window set-ignore-orientation-request true") + } + } + transitions { + testApp.toggleFixPortraitOrientation(wmHelper) + } + teardown { + eachRun { + testApp.exit() + device.executeShellCommand("cmd window set-ignore-orientation-request false") + } + } + } + } + + @Postsubmit + @Test + fun imeLayerVisibleStart() { + testSpec.assertLayersStart { + this.isVisible(FlickerComponentName.IME) + } + } + + @Postsubmit + @Test + fun imeLayerExistsEnd() { + testSpec.assertLayersEnd { + this.isVisible(FlickerComponentName.IME) + } + } + + @Postsubmit + @Test + fun imeLayerVisibleRegionKeepsTheSame() { + var imeLayerVisibleRegionBeforeTransition: RegionSubject? = null + testSpec.assertLayersStart { + imeLayerVisibleRegionBeforeTransition = this.visibleRegion(FlickerComponentName.IME) + } + testSpec.assertLayersEnd { + this.visibleRegion(FlickerComponentName.IME) + .coversExactly(imeLayerVisibleRegionBeforeTransition!!.region) + } + } + + @Postsubmit + @Test + fun appWindowWithLetterboxCoversExactlyOnScreen() { + val displayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation) + testSpec.assertLayersEnd { + this.visibleRegion(testApp.component, FlickerComponentName.LETTERBOX) + .coversExactly(displayBounds) + } + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests( + supportedRotations = listOf(Surface.ROTATION_90, Surface.ROTATION_270), + supportedNavigationModes = listOf( + WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, + WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY + ) + ) + } + } +}
\ No newline at end of file diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml index b8ef1954d5fc..efd80f252e86 100644 --- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml +++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml @@ -45,7 +45,7 @@ android:theme="@style/CutoutShortEdges" android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivityAutoFocus" android:windowSoftInputMode="stateVisible" - android:configChanges="orientation|screenSize" + android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout" android:label="ImeAppAutoFocus" android:exported="true"> <intent-filter> diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml index baaf7073b3a6..e71fe801e11a 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml @@ -26,14 +26,27 @@ android:layout_width="match_parent" android:imeOptions="flagNoExtractUi" android:inputType="text"/> - <Button - android:id="@+id/finish_activity_btn" + <LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="Finish activity" /> - <Button - android:id="@+id/start_dialog_themed_activity_btn" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="Start dialog themed activity" /> + android:layout_height="match_parent" + android:orientation="horizontal"> + <Button + android:id="@+id/finish_activity_btn" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Finish activity" /> + <Button + android:id="@+id/start_dialog_themed_activity_btn" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Dialog activity" /> + <ToggleButton + android:id="@+id/toggle_fixed_portrait_btn" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textOn="Portrait (On)" + android:textOff="Portrait (Off)" + /> + </LinearLayout> </LinearLayout> diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java index bb200f125507..7ee8debddcf1 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java @@ -16,21 +16,29 @@ package com.android.server.wm.flicker.testapp; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + import android.content.Intent; import android.widget.Button; import android.widget.EditText; +import android.widget.ToggleButton; public class ImeActivityAutoFocus extends ImeActivity { - @Override protected void onStart() { super.onStart(); - EditText editTextField = findViewById(R.id.plain_text_input); - editTextField.requestFocus(); - Button startThemedActivityButton = findViewById(R.id.start_dialog_themed_activity_btn); startThemedActivityButton.setOnClickListener( button -> startActivity(new Intent(this, DialogThemedActivity.class))); + + ToggleButton toggleFixedPortraitButton = findViewById(R.id.toggle_fixed_portrait_btn); + toggleFixedPortraitButton.setOnCheckedChangeListener( + (button, isChecked) -> setRequestedOrientation( + isChecked ? SCREEN_ORIENTATION_PORTRAIT : SCREEN_ORIENTATION_UNSPECIFIED)); + + EditText editTextField = findViewById(R.id.plain_text_input); + editTextField.requestFocus(); } } |