diff options
6 files changed, 340 insertions, 1 deletions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt new file mode 100644 index 000000000000..0cedc0a7147f --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2020 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.wm.shell.flicker.helpers + +import android.app.Instrumentation +import android.support.test.launcherhelper.ILauncherStrategy +import android.support.test.launcherhelper.LauncherStrategyFactory +import androidx.test.uiautomator.By +import androidx.test.uiautomator.UiDevice +import androidx.test.uiautomator.Until +import com.android.server.wm.flicker.helpers.FIND_TIMEOUT +import com.android.server.wm.flicker.helpers.waitForIME +import org.junit.Assert + +open class ImeAppHelper( + instr: Instrumentation, + launcherName: String = "ImeApp", + launcherStrategy: ILauncherStrategy = LauncherStrategyFactory + .getInstance(instr) + .launcherStrategy +) : FlickerAppHelper(instr, launcherName, launcherStrategy) { + open fun openIME(device: UiDevice) { + val editText = device.wait( + Until.findObject(By.res(getPackage(), "plain_text_input")), + FIND_TIMEOUT) + Assert.assertNotNull("Text field not found, this usually happens when the device " + + "was left in an unknown state (e.g. in split screen)", editText) + editText.click() + if (!device.waitForIME()) { + Assert.fail("IME did not appear") + } + } + + open fun closeIME(device: UiDevice) { + device.pressBack() + // Using only the AccessibilityInfo it is not possible to identify if the IME is active + device.waitForIdle(1000) + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt index c3c576d3f28c..010aa0d7d832 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt @@ -39,7 +39,7 @@ import org.junit.runners.Parameterized /** * Test Pip launch. - * To run this test: `atest FlickerTests:PipToAppTest` + * To run this test: `atest WMShellFlickerTests:PipToAppTest` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt new file mode 100644 index 000000000000..43e022538685 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2020 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.wm.shell.flicker.pip + +import android.content.ComponentName +import android.graphics.Region +import android.support.test.launcherhelper.LauncherStrategyFactory +import android.util.Log +import android.view.Surface +import android.view.WindowManager +import androidx.test.filters.RequiresDevice +import com.android.compatibility.common.util.SystemUtil +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.dsl.runWithFlicker +import com.android.server.wm.flicker.helpers.closePipWindow +import com.android.server.wm.flicker.helpers.hasPipWindow +import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen +import com.android.wm.shell.flicker.helpers.ImeAppHelper +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized +import java.io.IOException + +/** + * Test Pip launch. + * To run this test: `atest WMShellFlickerTests:PipKeyboardTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class PipKeyboardTest( + rotationName: String, + rotation: Int +) : PipTestBase(rotationName, rotation) { + private val windowManager: WindowManager = + instrumentation.context.getSystemService(WindowManager::class.java) + + private val keyboardApp = ImeAppHelper(instrumentation, "ImeApp", + LauncherStrategyFactory.getInstance(instrumentation).launcherStrategy) + + private val KEYBOARD_ACTIVITY: ComponentName = ComponentName.createRelative( + "com.android.wm.shell.flicker.testapp", ".ImeActivity") + private val PIP_ACTIVITY_WINDOW_NAME = "PipActivity" + private val INPUT_METHOD_WINDOW_NAME = "InputMethod" + + private val testRepetitions = 10 + + private val keyboardScenario: FlickerBuilder + get() = FlickerBuilder(instrumentation).apply { + repeat { testRepetitions } + // disable layer tracing + withLayerTracing { null } + setup { + test { + device.wakeUpAndGoToHomeScreen() + device.pressHome() + // launch our target pip app + testApp.open() + this.setRotation(rotation) + testApp.clickEnterPipButton(device) + // open an app with an input field and a keyboard + // UiAutomator doesn't support to launch the multiple Activities in a task. + // So use launchActivity() for the Keyboard Activity. + launchActivity(KEYBOARD_ACTIVITY) + } + } + teardown { + test { + keyboardApp.exit() + + if (device.hasPipWindow()) { + device.closePipWindow() + } + testApp.exit() + this.setRotation(Surface.ROTATION_0) + } + } + } + + /** Ensure the pip window remains visible throughout any keyboard interactions. */ + @Test + fun pipWindow_doesNotLeaveTheScreen_onKeyboardOpenClose() { + val testTag = "pipWindow_doesNotLeaveTheScreen_onKeyboardOpenClose" + runWithFlicker(keyboardScenario) { + withTestName { testTag } + transitions { + // open the soft keyboard + keyboardApp.openIME(device) + + // then close it again + keyboardApp.closeIME(device) + } + assertions { + windowManagerTrace { + all("PiP window must remain inside visible bounds") { + coversAtMostRegion( + partialWindowTitle = "PipActivity", + region = Region(windowManager.maximumWindowMetrics.bounds) + ) + } + } + } + } + } + + /** Ensure the pip window does not obscure the keyboard. */ + @Test + fun pipWindow_doesNotObscure_keyboard() { + val testTag = "pipWindow_doesNotObscure_keyboard" + runWithFlicker(keyboardScenario) { + withTestName { testTag } + transitions { + // open the soft keyboard + keyboardApp.openIME(device) + } + teardown { + eachRun { + // close the keyboard + keyboardApp.closeIME(device) + } + } + assertions { + windowManagerTrace { + end { + isAboveWindow(INPUT_METHOD_WINDOW_NAME, PIP_ACTIVITY_WINDOW_NAME) + } + } + } + } + } + + private fun launchActivity( + activity: ComponentName? = null, + action: String? = null, + flags: Set<Int> = setOf(), + boolExtras: Map<String, Boolean> = mapOf(), + intExtras: Map<String, Int> = mapOf(), + stringExtras: Map<String, String> = mapOf() + ) { + require(activity != null || !action.isNullOrBlank()) { + "Cannot launch an activity with neither activity name nor action!" + } + val command = composeCommand( + "start", activity, action, flags, boolExtras, intExtras, stringExtras) + executeShellCommand(command) + } + + private fun composeCommand( + command: String, + activity: ComponentName?, + action: String?, + flags: Set<Int>, + boolExtras: Map<String, Boolean>, + intExtras: Map<String, Int>, + stringExtras: Map<String, String> + ): String = buildString { + append("am ") + append(command) + activity?.let { + append(" -n ") + append(it.flattenToShortString()) + } + action?.let { + append(" -a ") + append(it) + } + flags.forEach { + append(" -f ") + append(it) + } + boolExtras.forEach { + append(it.withFlag("ez")) + } + intExtras.forEach { + append(it.withFlag("ei")) + } + stringExtras.forEach { + append(it.withFlag("es")) + } + } + + private fun Map.Entry<String, *>.withFlag(flag: String): String = " --$flag $key $value" + + private fun executeShellCommand(cmd: String): String { + try { + return SystemUtil.runShellCommand(instrumentation, cmd) + } catch (e: IOException) { + Log.e("FlickerTests", "Error running shell command: $cmd") + throw e + } + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<Array<Any>> { + val supportedRotations = intArrayOf(Surface.ROTATION_0) + return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) } + } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml index 95dc1d48eee8..3b66c58414e0 100644 --- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml +++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml @@ -33,5 +33,14 @@ <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> + <activity android:name=".ImeActivity" + android:taskAffinity="com.android.wm.shell.flicker.testapp.ImeActivity" + android:label="ImeApp" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> </application> </manifest> diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_ime.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_ime.xml new file mode 100644 index 000000000000..4708cfd48381 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_ime.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2018 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. +--> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:focusableInTouchMode="true" + android:background="@android:color/holo_green_light"> + <EditText android:id="@+id/plain_text_input" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:inputType="text"/> +</LinearLayout> diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/ImeActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/ImeActivity.java new file mode 100644 index 000000000000..856728715c1c --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/ImeActivity.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2018 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.wm.shell.flicker.testapp; + +import android.app.Activity; +import android.os.Bundle; +import android.view.WindowManager; + +public class ImeActivity extends Activity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + WindowManager.LayoutParams p = getWindow().getAttributes(); + p.layoutInDisplayCutoutMode = WindowManager.LayoutParams + .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + getWindow().setAttributes(p); + setContentView(R.layout.activity_ime); + } +} |