diff options
| author | 2023-01-13 08:19:01 +0000 | |
|---|---|---|
| committer | 2023-01-18 17:56:47 +0000 | |
| commit | 45537b756720bd6248f281fa9cbf7187008e8ce8 (patch) | |
| tree | 00fb5fd1f8081f2fca338c6f06453a5f494d6790 | |
| parent | 5975f9dc72297286acdfed3e65ca3ef34563901f (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
Change-Id: I7a66fd84e4094be249714c2597706ee25938adbe
7 files changed, 180 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 828a89a5abca..f3700258e075 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2374,7 +2374,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 1f60e79ee5eb..d3e5a8a8e842 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -1145,7 +1145,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 b7eea1b58485..fed953c92a73 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 @@ -119,4 +119,17 @@ 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 + waitIMEShown(wmHelper) + } } 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..806ce99b6d81 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt @@ -0,0 +1,124 @@ +/* + * 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.platform.test.annotations.Postsubmit +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.BaseTest +import com.android.server.wm.flicker.FlickerBuilder +import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTest +import com.android.server.wm.flicker.FlickerTestFactory +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.ComponentNameMatcher +import com.android.server.wm.traces.common.service.PlatformConsts +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) +class OpenImeWindowToFixedPortraitAppTest(flicker: FlickerTest) : BaseTest(flicker) { + private val testApp = ImeAppAutoFocusHelper(instrumentation, flicker.scenario.startRotation) + + /** {@inheritDoc} */ + override val transition: FlickerBuilder.() -> Unit = { + setup { + testApp.launchViaIntent(wmHelper) + testApp.openIME(wmHelper) + // Enable letterbox when the app calls setRequestedOrientation + device.executeShellCommand("cmd window set-ignore-orientation-request true") + } + transitions { + testApp.toggleFixPortraitOrientation(wmHelper) + } + teardown { + testApp.exit() + device.executeShellCommand("cmd window set-ignore-orientation-request false") + } + } + + @Postsubmit + @Test + fun imeLayerVisibleStart() { + flicker.assertLayersStart { + this.isVisible(ComponentNameMatcher.IME) + } + } + + @Postsubmit + @Test + fun imeLayerExistsEnd() { + flicker.assertLayersEnd { + this.isVisible(ComponentNameMatcher.IME) + } + } + + @Postsubmit + @Test + fun imeLayerVisibleRegionKeepsTheSame() { + var imeLayerVisibleRegionBeforeTransition: RegionSubject? = null + flicker.assertLayersStart { + imeLayerVisibleRegionBeforeTransition = this.visibleRegion(ComponentNameMatcher.IME) + } + flicker.assertLayersEnd { + this.visibleRegion(ComponentNameMatcher.IME) + .coversExactly(imeLayerVisibleRegionBeforeTransition!!.region) + } + } + + @Postsubmit + @Test + fun appWindowWithLetterboxCoversExactlyOnScreen() { + val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.startRotation) + flicker.assertLayersEnd { + this.visibleRegion(testApp.or(ComponentNameMatcher.LETTERBOX)) + .coversExactly(displayBounds) + } + } + + companion object { + /** + * Creates the test configurations. + * + * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTest> { + return FlickerTestFactory.nonRotationTests( + supportedRotations = listOf( + PlatformConsts.Rotation.ROTATION_90, + ), + supportedNavigationModes = listOf( + PlatformConsts.NavBar.MODE_3BUTTON, + PlatformConsts.NavBar.MODE_GESTURAL + ) + ) + } + } +}
\ 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 83823eae993b..cd47f6039abf 100644 --- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml +++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml @@ -47,7 +47,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..fa73e2c63780 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 themed 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(); } } |