Loading libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/Extensions.kt 0 → 100644 +32 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2021 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. */ @file:JvmName("Utils") package com.android.wm.shell.flicker import android.app.ActivityTaskManager import android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT import android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED fun removeAllTasksButHome() { val ALL_ACTIVITY_TYPE_BUT_HOME = intArrayOf( ACTIVITY_TYPE_STANDARD, ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS, ACTIVITY_TYPE_UNDEFINED) val atm = ActivityTaskManager.getService() atm.removeRootTasksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME) } libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt +6 −8 Original line number Original line Diff line number Diff line Loading @@ -32,13 +32,12 @@ open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper( /** /** * Opens the IME and wait for it to be displayed * Opens the IME and wait for it to be displayed * * * @param device UIDevice instance to interact with the device * @param wmHelper Helper used to wait for WindowManager states * @param wmHelper Helper used to wait for WindowManager states */ */ @JvmOverloads @JvmOverloads open fun openIME(device: UiDevice, wmHelper: WindowManagerStateHelper? = null) { open fun openIME(wmHelper: WindowManagerStateHelper? = null) { if (!isTelevision) { if (!isTelevision) { val editText = device.wait( val editText = uiDevice.wait( Until.findObject(By.res(getPackage(), "plain_text_input")), Until.findObject(By.res(getPackage(), "plain_text_input")), FIND_TIMEOUT) FIND_TIMEOUT) Loading @@ -47,7 +46,7 @@ open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper( "was left in an unknown state (e.g. in split screen)" "was left in an unknown state (e.g. in split screen)" } } editText.click() editText.click() waitAndAssertIMEShown(device, wmHelper) waitAndAssertIMEShown(uiDevice, wmHelper) } else { } else { // If we do the same thing as above - editText.click() - on TV, that's going to force TV // If we do the same thing as above - editText.click() - on TV, that's going to force TV // into the touch mode. We really don't want that. // into the touch mode. We really don't want that. Loading @@ -69,16 +68,15 @@ open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper( /** /** * Opens the IME and wait for it to be gone * Opens the IME and wait for it to be gone * * * @param device UIDevice instance to interact with the device * @param wmHelper Helper used to wait for WindowManager states * @param wmHelper Helper used to wait for WindowManager states */ */ @JvmOverloads @JvmOverloads open fun closeIME(device: UiDevice, wmHelper: WindowManagerStateHelper? = null) { open fun closeIME(wmHelper: WindowManagerStateHelper? = null) { if (!isTelevision) { if (!isTelevision) { device.pressBack() uiDevice.pressBack() // Using only the AccessibilityInfo it is not possible to identify if the IME is active // Using only the AccessibilityInfo it is not possible to identify if the IME is active if (wmHelper == null) { if (wmHelper == null) { device.waitForIdle() uiDevice.waitForIdle() } else { } else { require(wmHelper.waitImeWindowGone()) { "IME did did not close" } require(wmHelper.waitImeWindowGone()) { "IME did did not close" } } } Loading libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt +81 −12 Original line number Original line Diff line number Diff line Loading @@ -17,17 +17,20 @@ package com.android.wm.shell.flicker.helpers package com.android.wm.shell.flicker.helpers import android.app.Instrumentation import android.app.Instrumentation import android.graphics.Point import android.media.session.MediaController import android.media.session.MediaController import android.media.session.MediaSessionManager import android.media.session.MediaSessionManager import android.os.SystemClock import android.os.SystemClock import androidx.test.uiautomator.By import androidx.test.uiautomator.By import androidx.test.uiautomator.BySelector import androidx.test.uiautomator.BySelector import com.android.server.wm.flicker.helpers.SYSTEMUI_PACKAGE import com.android.server.wm.flicker.helpers.closePipWindow import com.android.server.wm.flicker.helpers.closePipWindow import com.android.server.wm.flicker.helpers.hasPipWindow import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.pip.tv.closeTvPipWindow import com.android.wm.shell.flicker.pip.tv.closeTvPipWindow import com.android.wm.shell.flicker.pip.tv.isFocusedOrHasFocusedChild import com.android.wm.shell.flicker.pip.tv.isFocusedOrHasFocusedChild import com.android.wm.shell.flicker.pip.waitPipWindowGone import com.android.wm.shell.flicker.pip.waitPipWindowShown import com.android.wm.shell.flicker.testapp.Components import com.android.wm.shell.flicker.testapp.Components import org.junit.Assert.fail class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( instrumentation, instrumentation, Loading Loading @@ -55,6 +58,17 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( } } } } /** {@inheritDoc} */ override fun launchViaIntent( wmHelper: WindowManagerStateHelper, expectedWindowName: String, action: String?, stringExtras: Map<String, String> ) { super.launchViaIntent(wmHelper, expectedWindowName, action, stringExtras) wmHelper.waitPipWindowShown() } private fun focusOnObject(selector: BySelector): Boolean { private fun focusOnObject(selector: BySelector): Boolean { // We expect all the focusable UI elements to be arranged in a way so that it is possible // We expect all the focusable UI elements to be arranged in a way so that it is possible // to "cycle" over all them by clicking the D-Pad DOWN button, going back up to "the top" // to "cycle" over all them by clicking the D-Pad DOWN button, going back up to "the top" Loading @@ -69,16 +83,12 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( return false return false } } fun clickEnterPipButton() { @JvmOverloads fun clickEnterPipButton(wmHelper: WindowManagerStateHelper? = null) { clickObject(ENTER_PIP_BUTTON_ID) clickObject(ENTER_PIP_BUTTON_ID) // TODO(b/172321238): remove this check once hasPipWindow is fixed on TVs // Wait on WMHelper or simply wait for 3 seconds if (!isTelevision) { wmHelper?.waitPipWindowShown() ?: SystemClock.sleep(3_000) uiDevice.hasPipWindow() } else { // Simply wait for 3 seconds SystemClock.sleep(3_000) } } } fun clickStartMediaSessionButton() { fun clickStartMediaSessionButton() { Loading @@ -97,16 +107,75 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( fun stopMedia() = mediaController?.transportControls?.stop() fun stopMedia() = mediaController?.transportControls?.stop() ?: error("No active media session found") ?: error("No active media session found") @Deprecated("Use PipAppHelper.closePipWindow(wmHelper) instead", ReplaceWith("closePipWindow(wmHelper)")) fun closePipWindow() { fun closePipWindow() { if (isTelevision) { if (isTelevision) { uiDevice.closeTvPipWindow() uiDevice.closeTvPipWindow() } else { } else { uiDevice.closePipWindow() uiDevice.closePipWindow() } } } if (!waitUntilClosed()) { /** fail("Couldn't close Pip") * Expands the pip window and dismisses it by clicking on the X button. * * Note, currently the View coordinates reported by the accessibility are relative to * the window, so the correct coordinates need to be calculated * * For example, in a PIP window located at Rect(508, 1444 - 1036, 1741), the * dismiss button coordinates are shown as Rect(650, 0 - 782, 132), with center in * Point(716, 66), instead of Point(970, 1403) * * See b/179337864 */ fun closePipWindow(wmHelper: WindowManagerStateHelper) { if (isTelevision) { uiDevice.closeTvPipWindow() } else { expandPipWindow(wmHelper) val exitPipObject = uiDevice.findObject(By.res(SYSTEMUI_PACKAGE, "dismiss")) requireNotNull(exitPipObject) { "PIP window dismiss button not found" } val coordinatesInWindow = exitPipObject.visibleBounds val windowOffset = wmHelper.getWindowRegion(component).bounds val newCoordinates = Point(windowOffset.left + coordinatesInWindow.centerX(), windowOffset.top + coordinatesInWindow.centerY()) uiDevice.click(newCoordinates.x, newCoordinates.y) } // Wait for animation to complete. wmHelper.waitPipWindowGone() wmHelper.waitForHomeActivityVisible() } /** * Click once on the PIP window to expand it */ fun expandPipWindow(wmHelper: WindowManagerStateHelper) { val windowRegion = wmHelper.getWindowRegion(component) require(!windowRegion.isEmpty) { "Unable to find a PIP window in the current state" } val windowRect = windowRegion.bounds uiDevice.click(windowRect.centerX(), windowRect.centerY()) // Ensure WindowManagerService wait until all animations have completed wmHelper.waitForAppTransitionIdle() mInstrumentation.uiAutomation.syncInputTransactions() } /** * Double click on the PIP window to reopen to app */ fun expandPipWindowToApp(wmHelper: WindowManagerStateHelper) { val windowRegion = wmHelper.getWindowRegion(component) require(!windowRegion.isEmpty) { "Unable to find a PIP window in the current state" } } val windowRect = windowRegion.bounds uiDevice.click(windowRect.centerX(), windowRect.centerY()) uiDevice.click(windowRect.centerX(), windowRect.centerY()) wmHelper.waitPipWindowGone() wmHelper.waitForAppTransitionIdle() } } companion object { companion object { Loading libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt +0 −13 Original line number Original line Diff line number Diff line Loading @@ -16,11 +16,6 @@ package com.android.wm.shell.flicker.pip package com.android.wm.shell.flicker.pip import android.app.ActivityTaskManager import android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT import android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED import android.os.SystemClock import android.os.SystemClock import com.android.wm.shell.flicker.NonRotationTestBase import com.android.wm.shell.flicker.NonRotationTestBase Loading @@ -29,14 +24,6 @@ abstract class AppTestBase( rotation: Int rotation: Int ) : NonRotationTestBase(rotationName, rotation) { ) : NonRotationTestBase(rotationName, rotation) { companion object { companion object { fun removeAllTasksButHome() { val ALL_ACTIVITY_TYPE_BUT_HOME = intArrayOf( ACTIVITY_TYPE_STANDARD, ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS, ACTIVITY_TYPE_UNDEFINED) val atm = ActivityTaskManager.getService() atm.removeRootTasksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME) } fun waitForAnimationComplete() { fun waitForAnimationComplete() { // TODO: UiDevice doesn't have reliable way to wait for the completion of animation. // TODO: UiDevice doesn't have reliable way to wait for the completion of animation. // Consider to introduce WindowManagerStateHelper to access Activity state. // Consider to introduce WindowManagerStateHelper to access Activity state. Loading libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt +58 −73 Original line number Original line Diff line number Diff line Loading @@ -16,21 +16,21 @@ package com.android.wm.shell.flicker.pip package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit import android.os.Bundle import android.view.Surface import android.view.Surface import androidx.test.filters.RequiresDevice import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.dsl.runFlicker import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.FlickerTestRunner import com.android.server.wm.flicker.FlickerTestRunnerFactory import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.wm.shell.flicker.helpers.FixedAppHelper import com.android.wm.shell.flicker.helpers.FixedAppHelper import com.android.wm.shell.flicker.helpers.PipAppHelper import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP import org.junit.FixMethodOrder import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.MethodSorters import org.junit.runners.Parameterized import org.junit.runners.Parameterized Loading @@ -39,42 +39,31 @@ import org.junit.runners.Parameterized * Test Pip launch and exit. * Test Pip launch and exit. * To run this test: `atest WMShellFlickerTests:EnterExitPipTest` * To run this test: `atest WMShellFlickerTests:EnterExitPipTest` */ */ @Presubmit @RequiresDevice @RequiresDevice @RunWith(Parameterized::class) @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class EnterExitPipTest( class EnterExitPipTest( rotationName: String, testSpec: FlickerTestRunnerFactory.TestSpec rotation: Int ) : FlickerTestRunner(testSpec) { ) : AppTestBase(rotationName, rotation) { companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) { private val pipApp = PipAppHelper(instrumentation) @Parameterized.Parameters(name = "{0}") private val testApp = FixedAppHelper(instrumentation) @JvmStatic fun getParams(): List<Array<Any>> { @Test val testApp = FixedAppHelper(instrumentation) fun testDisplayMetricsPinUnpin() { val baseConfig = getTransitionLaunch(eachRun = true) runFlicker(instrumentation) { val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> withTestName { "testDisplayMetricsPinUnpin" } setup { setup { test { eachRun { removeAllTasksButHome() testApp.launchViaIntent(wmHelper) device.wakeUpAndGoToHomeScreen() pipApp.launchViaIntent(stringExtras = mapOf(EXTRA_ENTER_PIP to "true")) testApp.launchViaIntent() waitForAnimationComplete() } } } } transitions { transitions { // This will bring PipApp to fullscreen // This will bring PipApp to fullscreen pipApp.launchViaIntent() pipApp.launchViaIntent(wmHelper) waitForAnimationComplete() } teardown { test { removeAllTasksButHome() } } } assertions { assertions { val displayBounds = WindowUtils.getDisplayBounds(rotation) val displayBounds = WindowUtils.getDisplayBounds(configuration.startRotation) presubmit { windowManagerTrace { windowManagerTrace { all("pipApp must remain inside visible bounds") { all("pipApp must remain inside visible bounds") { coversAtMostRegion(pipApp.defaultWindowName, displayBounds) coversAtMostRegion(pipApp.defaultWindowName, displayBounds) Loading Loading @@ -108,13 +97,9 @@ class EnterExitPipTest( } } } } } } return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, baseConfig, companion object { testSpec, supportedRotations = listOf(Surface.ROTATION_0), @Parameterized.Parameters(name = "{0}") repetitions = 5) @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 Loading
libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/Extensions.kt 0 → 100644 +32 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2021 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. */ @file:JvmName("Utils") package com.android.wm.shell.flicker import android.app.ActivityTaskManager import android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT import android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED fun removeAllTasksButHome() { val ALL_ACTIVITY_TYPE_BUT_HOME = intArrayOf( ACTIVITY_TYPE_STANDARD, ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS, ACTIVITY_TYPE_UNDEFINED) val atm = ActivityTaskManager.getService() atm.removeRootTasksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME) }
libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt +6 −8 Original line number Original line Diff line number Diff line Loading @@ -32,13 +32,12 @@ open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper( /** /** * Opens the IME and wait for it to be displayed * Opens the IME and wait for it to be displayed * * * @param device UIDevice instance to interact with the device * @param wmHelper Helper used to wait for WindowManager states * @param wmHelper Helper used to wait for WindowManager states */ */ @JvmOverloads @JvmOverloads open fun openIME(device: UiDevice, wmHelper: WindowManagerStateHelper? = null) { open fun openIME(wmHelper: WindowManagerStateHelper? = null) { if (!isTelevision) { if (!isTelevision) { val editText = device.wait( val editText = uiDevice.wait( Until.findObject(By.res(getPackage(), "plain_text_input")), Until.findObject(By.res(getPackage(), "plain_text_input")), FIND_TIMEOUT) FIND_TIMEOUT) Loading @@ -47,7 +46,7 @@ open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper( "was left in an unknown state (e.g. in split screen)" "was left in an unknown state (e.g. in split screen)" } } editText.click() editText.click() waitAndAssertIMEShown(device, wmHelper) waitAndAssertIMEShown(uiDevice, wmHelper) } else { } else { // If we do the same thing as above - editText.click() - on TV, that's going to force TV // If we do the same thing as above - editText.click() - on TV, that's going to force TV // into the touch mode. We really don't want that. // into the touch mode. We really don't want that. Loading @@ -69,16 +68,15 @@ open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper( /** /** * Opens the IME and wait for it to be gone * Opens the IME and wait for it to be gone * * * @param device UIDevice instance to interact with the device * @param wmHelper Helper used to wait for WindowManager states * @param wmHelper Helper used to wait for WindowManager states */ */ @JvmOverloads @JvmOverloads open fun closeIME(device: UiDevice, wmHelper: WindowManagerStateHelper? = null) { open fun closeIME(wmHelper: WindowManagerStateHelper? = null) { if (!isTelevision) { if (!isTelevision) { device.pressBack() uiDevice.pressBack() // Using only the AccessibilityInfo it is not possible to identify if the IME is active // Using only the AccessibilityInfo it is not possible to identify if the IME is active if (wmHelper == null) { if (wmHelper == null) { device.waitForIdle() uiDevice.waitForIdle() } else { } else { require(wmHelper.waitImeWindowGone()) { "IME did did not close" } require(wmHelper.waitImeWindowGone()) { "IME did did not close" } } } Loading
libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt +81 −12 Original line number Original line Diff line number Diff line Loading @@ -17,17 +17,20 @@ package com.android.wm.shell.flicker.helpers package com.android.wm.shell.flicker.helpers import android.app.Instrumentation import android.app.Instrumentation import android.graphics.Point import android.media.session.MediaController import android.media.session.MediaController import android.media.session.MediaSessionManager import android.media.session.MediaSessionManager import android.os.SystemClock import android.os.SystemClock import androidx.test.uiautomator.By import androidx.test.uiautomator.By import androidx.test.uiautomator.BySelector import androidx.test.uiautomator.BySelector import com.android.server.wm.flicker.helpers.SYSTEMUI_PACKAGE import com.android.server.wm.flicker.helpers.closePipWindow import com.android.server.wm.flicker.helpers.closePipWindow import com.android.server.wm.flicker.helpers.hasPipWindow import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.pip.tv.closeTvPipWindow import com.android.wm.shell.flicker.pip.tv.closeTvPipWindow import com.android.wm.shell.flicker.pip.tv.isFocusedOrHasFocusedChild import com.android.wm.shell.flicker.pip.tv.isFocusedOrHasFocusedChild import com.android.wm.shell.flicker.pip.waitPipWindowGone import com.android.wm.shell.flicker.pip.waitPipWindowShown import com.android.wm.shell.flicker.testapp.Components import com.android.wm.shell.flicker.testapp.Components import org.junit.Assert.fail class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( instrumentation, instrumentation, Loading Loading @@ -55,6 +58,17 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( } } } } /** {@inheritDoc} */ override fun launchViaIntent( wmHelper: WindowManagerStateHelper, expectedWindowName: String, action: String?, stringExtras: Map<String, String> ) { super.launchViaIntent(wmHelper, expectedWindowName, action, stringExtras) wmHelper.waitPipWindowShown() } private fun focusOnObject(selector: BySelector): Boolean { private fun focusOnObject(selector: BySelector): Boolean { // We expect all the focusable UI elements to be arranged in a way so that it is possible // We expect all the focusable UI elements to be arranged in a way so that it is possible // to "cycle" over all them by clicking the D-Pad DOWN button, going back up to "the top" // to "cycle" over all them by clicking the D-Pad DOWN button, going back up to "the top" Loading @@ -69,16 +83,12 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( return false return false } } fun clickEnterPipButton() { @JvmOverloads fun clickEnterPipButton(wmHelper: WindowManagerStateHelper? = null) { clickObject(ENTER_PIP_BUTTON_ID) clickObject(ENTER_PIP_BUTTON_ID) // TODO(b/172321238): remove this check once hasPipWindow is fixed on TVs // Wait on WMHelper or simply wait for 3 seconds if (!isTelevision) { wmHelper?.waitPipWindowShown() ?: SystemClock.sleep(3_000) uiDevice.hasPipWindow() } else { // Simply wait for 3 seconds SystemClock.sleep(3_000) } } } fun clickStartMediaSessionButton() { fun clickStartMediaSessionButton() { Loading @@ -97,16 +107,75 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( fun stopMedia() = mediaController?.transportControls?.stop() fun stopMedia() = mediaController?.transportControls?.stop() ?: error("No active media session found") ?: error("No active media session found") @Deprecated("Use PipAppHelper.closePipWindow(wmHelper) instead", ReplaceWith("closePipWindow(wmHelper)")) fun closePipWindow() { fun closePipWindow() { if (isTelevision) { if (isTelevision) { uiDevice.closeTvPipWindow() uiDevice.closeTvPipWindow() } else { } else { uiDevice.closePipWindow() uiDevice.closePipWindow() } } } if (!waitUntilClosed()) { /** fail("Couldn't close Pip") * Expands the pip window and dismisses it by clicking on the X button. * * Note, currently the View coordinates reported by the accessibility are relative to * the window, so the correct coordinates need to be calculated * * For example, in a PIP window located at Rect(508, 1444 - 1036, 1741), the * dismiss button coordinates are shown as Rect(650, 0 - 782, 132), with center in * Point(716, 66), instead of Point(970, 1403) * * See b/179337864 */ fun closePipWindow(wmHelper: WindowManagerStateHelper) { if (isTelevision) { uiDevice.closeTvPipWindow() } else { expandPipWindow(wmHelper) val exitPipObject = uiDevice.findObject(By.res(SYSTEMUI_PACKAGE, "dismiss")) requireNotNull(exitPipObject) { "PIP window dismiss button not found" } val coordinatesInWindow = exitPipObject.visibleBounds val windowOffset = wmHelper.getWindowRegion(component).bounds val newCoordinates = Point(windowOffset.left + coordinatesInWindow.centerX(), windowOffset.top + coordinatesInWindow.centerY()) uiDevice.click(newCoordinates.x, newCoordinates.y) } // Wait for animation to complete. wmHelper.waitPipWindowGone() wmHelper.waitForHomeActivityVisible() } /** * Click once on the PIP window to expand it */ fun expandPipWindow(wmHelper: WindowManagerStateHelper) { val windowRegion = wmHelper.getWindowRegion(component) require(!windowRegion.isEmpty) { "Unable to find a PIP window in the current state" } val windowRect = windowRegion.bounds uiDevice.click(windowRect.centerX(), windowRect.centerY()) // Ensure WindowManagerService wait until all animations have completed wmHelper.waitForAppTransitionIdle() mInstrumentation.uiAutomation.syncInputTransactions() } /** * Double click on the PIP window to reopen to app */ fun expandPipWindowToApp(wmHelper: WindowManagerStateHelper) { val windowRegion = wmHelper.getWindowRegion(component) require(!windowRegion.isEmpty) { "Unable to find a PIP window in the current state" } } val windowRect = windowRegion.bounds uiDevice.click(windowRect.centerX(), windowRect.centerY()) uiDevice.click(windowRect.centerX(), windowRect.centerY()) wmHelper.waitPipWindowGone() wmHelper.waitForAppTransitionIdle() } } companion object { companion object { Loading
libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt +0 −13 Original line number Original line Diff line number Diff line Loading @@ -16,11 +16,6 @@ package com.android.wm.shell.flicker.pip package com.android.wm.shell.flicker.pip import android.app.ActivityTaskManager import android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT import android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED import android.os.SystemClock import android.os.SystemClock import com.android.wm.shell.flicker.NonRotationTestBase import com.android.wm.shell.flicker.NonRotationTestBase Loading @@ -29,14 +24,6 @@ abstract class AppTestBase( rotation: Int rotation: Int ) : NonRotationTestBase(rotationName, rotation) { ) : NonRotationTestBase(rotationName, rotation) { companion object { companion object { fun removeAllTasksButHome() { val ALL_ACTIVITY_TYPE_BUT_HOME = intArrayOf( ACTIVITY_TYPE_STANDARD, ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS, ACTIVITY_TYPE_UNDEFINED) val atm = ActivityTaskManager.getService() atm.removeRootTasksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME) } fun waitForAnimationComplete() { fun waitForAnimationComplete() { // TODO: UiDevice doesn't have reliable way to wait for the completion of animation. // TODO: UiDevice doesn't have reliable way to wait for the completion of animation. // Consider to introduce WindowManagerStateHelper to access Activity state. // Consider to introduce WindowManagerStateHelper to access Activity state. Loading
libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt +58 −73 Original line number Original line Diff line number Diff line Loading @@ -16,21 +16,21 @@ package com.android.wm.shell.flicker.pip package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit import android.os.Bundle import android.view.Surface import android.view.Surface import androidx.test.filters.RequiresDevice import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.dsl.runFlicker import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.FlickerTestRunner import com.android.server.wm.flicker.FlickerTestRunnerFactory import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.wm.shell.flicker.helpers.FixedAppHelper import com.android.wm.shell.flicker.helpers.FixedAppHelper import com.android.wm.shell.flicker.helpers.PipAppHelper import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP import org.junit.FixMethodOrder import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.MethodSorters import org.junit.runners.Parameterized import org.junit.runners.Parameterized Loading @@ -39,42 +39,31 @@ import org.junit.runners.Parameterized * Test Pip launch and exit. * Test Pip launch and exit. * To run this test: `atest WMShellFlickerTests:EnterExitPipTest` * To run this test: `atest WMShellFlickerTests:EnterExitPipTest` */ */ @Presubmit @RequiresDevice @RequiresDevice @RunWith(Parameterized::class) @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class EnterExitPipTest( class EnterExitPipTest( rotationName: String, testSpec: FlickerTestRunnerFactory.TestSpec rotation: Int ) : FlickerTestRunner(testSpec) { ) : AppTestBase(rotationName, rotation) { companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) { private val pipApp = PipAppHelper(instrumentation) @Parameterized.Parameters(name = "{0}") private val testApp = FixedAppHelper(instrumentation) @JvmStatic fun getParams(): List<Array<Any>> { @Test val testApp = FixedAppHelper(instrumentation) fun testDisplayMetricsPinUnpin() { val baseConfig = getTransitionLaunch(eachRun = true) runFlicker(instrumentation) { val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> withTestName { "testDisplayMetricsPinUnpin" } setup { setup { test { eachRun { removeAllTasksButHome() testApp.launchViaIntent(wmHelper) device.wakeUpAndGoToHomeScreen() pipApp.launchViaIntent(stringExtras = mapOf(EXTRA_ENTER_PIP to "true")) testApp.launchViaIntent() waitForAnimationComplete() } } } } transitions { transitions { // This will bring PipApp to fullscreen // This will bring PipApp to fullscreen pipApp.launchViaIntent() pipApp.launchViaIntent(wmHelper) waitForAnimationComplete() } teardown { test { removeAllTasksButHome() } } } assertions { assertions { val displayBounds = WindowUtils.getDisplayBounds(rotation) val displayBounds = WindowUtils.getDisplayBounds(configuration.startRotation) presubmit { windowManagerTrace { windowManagerTrace { all("pipApp must remain inside visible bounds") { all("pipApp must remain inside visible bounds") { coversAtMostRegion(pipApp.defaultWindowName, displayBounds) coversAtMostRegion(pipApp.defaultWindowName, displayBounds) Loading Loading @@ -108,13 +97,9 @@ class EnterExitPipTest( } } } } } } return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, baseConfig, companion object { testSpec, supportedRotations = listOf(Surface.ROTATION_0), @Parameterized.Parameters(name = "{0}") repetitions = 5) @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