diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml
index 8b2f6681554a4..58fc90e4d8275 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml
@@ -32,8 +32,19 @@
+
+
+
+
+
+
+
+
Pip Activity
+val TEST_APP_PIP_ACTIVITY_COMPONENT_NAME: ComponentName = ComponentName.createRelative(
+ TEST_APP_PACKAGE_NAME, ".PipActivity")
+const val TEST_APP_PIP_ACTIVITY_LABEL = "PipApp"
+const val TEST_APP_PIP_ACTIVITY_WINDOW_NAME = "PipActivity"
+// Test App > Ime Activity
+val TEST_APP_IME_ACTIVITY_COMPONENT_NAME: ComponentName = ComponentName.createRelative(
+ TEST_APP_PACKAGE_NAME, ".ImeActivity")
+const val TEST_APP_IME_ACTIVITY_LABEL = "ImeApp"
+
+const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt
index 99f824bb83419..75b55c1e6a9ff 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker
+import android.content.pm.PackageManager
import android.os.RemoteException
import android.os.SystemClock
import android.platform.helpers.IAppHelper
@@ -41,6 +42,9 @@ abstract class FlickerTestBase {
val uiDevice by lazy {
UiDevice.getInstance(instrumentation)
}
+ val packageManager: PackageManager by lazy {
+ instrumentation.context.getPackageManager()
+ }
/**
* Build a test tag for the test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NotificationListener.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NotificationListener.kt
new file mode 100644
index 0000000000000..5c7ec30ecd3ea
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NotificationListener.kt
@@ -0,0 +1,89 @@
+/*
+ * 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
+
+import android.service.notification.NotificationListenerService
+import android.service.notification.StatusBarNotification
+import android.util.Log
+import com.android.compatibility.common.util.SystemUtil.runShellCommand
+
+class NotificationListener : NotificationListenerService() {
+ private val notifications: MutableMap = mutableMapOf()
+
+ override fun onNotificationPosted(sbn: StatusBarNotification) {
+ if (DEBUG) Log.d(TAG, "onNotificationPosted: $sbn")
+ notifications[sbn.key] = sbn
+ }
+
+ override fun onNotificationRemoved(sbn: StatusBarNotification) {
+ if (DEBUG) Log.d(TAG, "onNotificationRemoved: $sbn")
+ notifications.remove(sbn.key)
+ }
+
+ override fun onListenerConnected() {
+ if (DEBUG) Log.d(TAG, "onListenerConnected")
+ instance = this
+ }
+
+ override fun onListenerDisconnected() {
+ if (DEBUG) Log.d(TAG, "onListenerDisconnected")
+ instance = null
+ notifications.clear()
+ }
+
+ companion object {
+ private const val DEBUG = false
+ private const val TAG = "WMShellFlickerTests_NotificationListener"
+
+ private const val CMD_NOTIFICATION_ALLOW_LISTENER = "cmd notification allow_listener %s"
+ private const val CMD_NOTIFICATION_DISALLOW_LISTENER =
+ "cmd notification disallow_listener %s"
+ private const val COMPONENT_NAME = "com.android.wm.shell.flicker/.NotificationListener"
+
+ private var instance: NotificationListener? = null
+
+ fun startNotificationListener(): Boolean {
+ if (instance != null) {
+ return true
+ }
+
+ runShellCommand(CMD_NOTIFICATION_ALLOW_LISTENER.format(COMPONENT_NAME))
+ return wait { instance != null }
+ }
+
+ fun stopNotificationListener(): Boolean {
+ if (instance == null) {
+ return true
+ }
+
+ runShellCommand(CMD_NOTIFICATION_DISALLOW_LISTENER.format(COMPONENT_NAME))
+ return wait { instance == null }
+ }
+
+ fun waitForNotificationToAppear(predicate: (StatusBarNotification) -> Boolean): Boolean {
+ return instance?.let {
+ wait { it.notifications.values.any(predicate) }
+ } ?: throw IllegalStateException("NotificationListenerService is not connected")
+ }
+
+ fun waitForNotificationToDisappear(predicate: (StatusBarNotification) -> Boolean): Boolean {
+ return instance?.let {
+ wait { it.notifications.values.none(predicate) }
+ } ?: throw IllegalStateException("NotificationListenerService is not connected")
+ }
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/WaitUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/WaitUtils.kt
new file mode 100644
index 0000000000000..a6d67355f2711
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/WaitUtils.kt
@@ -0,0 +1,45 @@
+/*
+ * 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
+
+import android.os.SystemClock
+
+private const val DEFAULT_TIMEOUT = 10000L
+private const val DEFAULT_POLL_INTERVAL = 1000L
+
+fun wait(condition: () -> Boolean): Boolean {
+ val (success, _) = waitForResult(extractor = condition, validator = { it })
+ return success
+}
+
+fun waitForResult(
+ timeout: Long = DEFAULT_TIMEOUT,
+ interval: Long = DEFAULT_POLL_INTERVAL,
+ extractor: () -> R,
+ validator: (R) -> Boolean = { it != null }
+): Pair {
+ val startTime = SystemClock.uptimeMillis()
+ do {
+ val result = extractor()
+ if (validator(result)) {
+ return (true to result)
+ }
+ SystemClock.sleep(interval)
+ } while (SystemClock.uptimeMillis() - startTime < timeout)
+
+ return (false to null)
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
new file mode 100644
index 0000000000000..e8cf7d978420c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.ActivityManager
+import android.app.Instrumentation
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager.FEATURE_LEANBACK
+import android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY
+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.StandardAppHelper
+import com.android.wm.shell.flicker.TEST_APP_PACKAGE_NAME
+
+abstract class BaseAppHelper(
+ instrumentation: Instrumentation,
+ launcherName: String,
+ private val launcherActivityComponent: ComponentName
+) : StandardAppHelper(
+ instrumentation,
+ TEST_APP_PACKAGE_NAME,
+ launcherName,
+ LauncherStrategyFactory.getInstance(instrumentation).launcherStrategy
+) {
+ protected val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
+
+ private val context: Context
+ get() = mInstrumentation.context
+
+ private val activityManager: ActivityManager?
+ get() = context.getSystemService(ActivityManager::class.java)
+
+ private val appSelector = By.pkg(packageName).depth(0)
+
+ protected val isTelevision: Boolean
+ get() = context.packageManager.run {
+ hasSystemFeature(FEATURE_LEANBACK) || hasSystemFeature(FEATURE_LEANBACK_ONLY)
+ }
+
+ val label: String
+ get() = context.packageManager.run {
+ getApplicationLabel(getApplicationInfo(packageName, 0)).toString()
+ }
+
+ fun launchViaIntent() {
+ context.startActivity(openAppIntent)
+
+ uiDevice.wait(Until.hasObject(appSelector), APP_LAUNCH_WAIT_TIME_MS)
+ }
+
+ fun forceStop() = activityManager?.forceStopPackage(packageName)
+
+ override fun getOpenAppIntent(): Intent {
+ val intent = Intent()
+ intent.component = launcherActivityComponent
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ return intent
+ }
+
+ companion object {
+ private const val APP_LAUNCH_WAIT_TIME_MS = 10_000L
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FlickerAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FlickerAppHelper.kt
deleted file mode 100644
index 47a62ce92d11a..0000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FlickerAppHelper.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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 com.android.server.wm.flicker.helpers.StandardAppHelper
-
-abstract class FlickerAppHelper(
- instr: Instrumentation,
- launcherName: String,
- launcherStrategy: ILauncherStrategy
-) : StandardAppHelper(instr, sFlickerPackage, launcherName, launcherStrategy) {
- companion object {
- var sFlickerPackage = "com.android.wm.shell.flicker.testapp"
- }
-}
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
index 0cedc0a7147ff..a6650d7f13d1f 100644
--- 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
@@ -17,37 +17,36 @@
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 com.android.wm.shell.flicker.TEST_APP_IME_ACTIVITY_COMPONENT_NAME
+import com.android.wm.shell.flicker.TEST_APP_IME_ACTIVITY_LABEL
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(
+ instrumentation: Instrumentation
+) : BaseAppHelper(
+ instrumentation,
+ TEST_APP_IME_ACTIVITY_LABEL,
+ TEST_APP_IME_ACTIVITY_COMPONENT_NAME
+) {
+ fun openIME() {
+ val editText = uiDevice.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()) {
+ if (!uiDevice.waitForIME()) {
Assert.fail("IME did not appear")
}
}
- open fun closeIME(device: UiDevice) {
- device.pressBack()
+ fun closeIME() {
+ uiDevice.pressBack()
// Using only the AccessibilityInfo it is not possible to identify if the IME is active
- device.waitForIdle(1000)
+ uiDevice.waitForIdle(1000)
}
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
index 539170202b8ad..d5efa409e6adf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
@@ -17,29 +17,56 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
-import android.support.test.launcherhelper.ILauncherStrategy
-import android.support.test.launcherhelper.LauncherStrategyFactory
+import android.os.SystemClock
+import android.view.KeyEvent.KEYCODE_WINDOW
import androidx.test.uiautomator.By
-import androidx.test.uiautomator.UiDevice
-import com.android.server.wm.flicker.helpers.hasPipWindow
+import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.helpers.closePipWindow
-import org.junit.Assert
+import com.android.server.wm.flicker.helpers.hasPipWindow
+import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME
+import com.android.wm.shell.flicker.TEST_APP_PIP_ACTIVITY_COMPONENT_NAME
+import com.android.wm.shell.flicker.TEST_APP_PIP_ACTIVITY_LABEL
+import org.junit.Assert.assertNotNull
class PipAppHelper(
- instr: Instrumentation,
- launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
- .getInstance(instr)
- .launcherStrategy
-) : FlickerAppHelper(instr, "PipApp", launcherStrategy) {
- fun clickEnterPipButton(device: UiDevice) {
- val enterPipButton = device.findObject(By.res(getPackage(), "enter_pip"))
- Assert.assertNotNull("Pip button not found, this usually happens when the device " +
+ instrumentation: Instrumentation
+) : BaseAppHelper(
+ instrumentation,
+ TEST_APP_PIP_ACTIVITY_LABEL,
+ TEST_APP_PIP_ACTIVITY_COMPONENT_NAME
+) {
+ fun clickEnterPipButton() {
+ val enterPipButton = uiDevice.findObject(By.res(packageName, "enter_pip"))
+ assertNotNull("Pip button not found, this usually happens when the device " +
"was left in an unknown state (e.g. in split screen)", enterPipButton)
enterPipButton.click()
- device.hasPipWindow()
+
+ // TODO(b/172321238): remove this check once hasPipWindow is fixed on TVs
+ if (!isTelevision) {
+ uiDevice.hasPipWindow()
+ } else {
+ // Simply wait for 3 seconds
+ SystemClock.sleep(3_000)
+ }
}
- fun closePipWindow(device: UiDevice) {
- device.closePipWindow()
+ fun closePipWindow() {
+ // TODO(b/172321238): remove this check once and simply call closePipWindow once the TV
+ // logic is integrated there.
+ if (!isTelevision) {
+ uiDevice.closePipWindow()
+ } else {
+ // Bring up Pip menu
+ uiDevice.pressKeyCode(KEYCODE_WINDOW)
+
+ // Wait for the menu to come up and render the close button
+ val closeButton = uiDevice.wait(
+ Until.findObject(By.res(SYSTEM_UI_PACKAGE_NAME, "close_button")), 3_000)
+ assertNotNull("Pip menu close button is not found", closeButton)
+ closeButton.click()
+
+ // Give it 1 second, just in case
+ SystemClock.sleep(1_000)
+ }
}
}
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 010aa0d7d8327..a1da7c939f602 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
@@ -31,6 +31,7 @@ import com.android.wm.shell.flicker.noUncoveredRegions
import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible
import com.android.wm.shell.flicker.statusBarLayerRotatesScales
import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible
+import com.android.wm.shell.flicker.PIP_WINDOW_NAME
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -79,7 +80,7 @@ class EnterPipTest(
}
}
transitions {
- testApp.clickEnterPipButton(device)
+ testApp.clickEnterPipButton()
device.expandPipWindow()
}
assertions {
@@ -89,7 +90,7 @@ class EnterPipTest(
all("pipWindowBecomesVisible") {
this.showsAppWindow(testApp.`package`)
.then()
- .showsAppWindow(sPipWindowTitle)
+ .showsAppWindow(PIP_WINDOW_NAME)
}
}
@@ -103,7 +104,7 @@ class EnterPipTest(
all("pipLayerBecomesVisible") {
this.showsLayer(testApp.launcherName)
.then()
- .showsLayer(sPipWindowTitle)
+ .showsLayer(PIP_WINDOW_NAME)
}
}
}
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
index 43e0225386855..d343f2a7093ba 100644
--- 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
@@ -18,7 +18,6 @@ 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
@@ -29,6 +28,9 @@ 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.TEST_APP_IME_ACTIVITY_COMPONENT_NAME
+import com.android.wm.shell.flicker.IME_WINDOW_NAME
+import com.android.wm.shell.flicker.TEST_APP_PIP_ACTIVITY_WINDOW_NAME
import com.android.wm.shell.flicker.helpers.ImeAppHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -51,19 +53,11 @@ class PipKeyboardTest(
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 keyboardApp = ImeAppHelper(instrumentation)
private val keyboardScenario: FlickerBuilder
get() = FlickerBuilder(instrumentation).apply {
- repeat { testRepetitions }
+ repeat { TEST_REPETITIONS }
// disable layer tracing
withLayerTracing { null }
setup {
@@ -73,11 +67,11 @@ class PipKeyboardTest(
// launch our target pip app
testApp.open()
this.setRotation(rotation)
- testApp.clickEnterPipButton(device)
+ testApp.clickEnterPipButton()
// 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)
+ launchActivity(TEST_APP_IME_ACTIVITY_COMPONENT_NAME)
}
}
teardown {
@@ -101,10 +95,10 @@ class PipKeyboardTest(
withTestName { testTag }
transitions {
// open the soft keyboard
- keyboardApp.openIME(device)
+ keyboardApp.openIME()
// then close it again
- keyboardApp.closeIME(device)
+ keyboardApp.closeIME()
}
assertions {
windowManagerTrace {
@@ -127,18 +121,18 @@ class PipKeyboardTest(
withTestName { testTag }
transitions {
// open the soft keyboard
- keyboardApp.openIME(device)
+ keyboardApp.openIME()
}
teardown {
eachRun {
// close the keyboard
- keyboardApp.closeIME(device)
+ keyboardApp.closeIME()
}
}
assertions {
windowManagerTrace {
end {
- isAboveWindow(INPUT_METHOD_WINDOW_NAME, PIP_ACTIVITY_WINDOW_NAME)
+ isAboveWindow(IME_WINDOW_NAME, TEST_APP_PIP_ACTIVITY_WINDOW_NAME)
}
}
}
@@ -207,6 +201,8 @@ class PipKeyboardTest(
}
companion object {
+ private const val TEST_REPETITIONS = 10
+
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection> {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt
index 3822d69a65f50..c1c34ecfbaec5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt
@@ -24,8 +24,4 @@ abstract class PipTestBase(
rotation: Int
) : NonRotationTestBase(rotationName, rotation) {
protected val testApp = PipAppHelper(instrumentation)
-
- companion object {
- const val sPipWindowTitle = "PipMenuActivity"
- }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt
new file mode 100644
index 0000000000000..569da1ddd953d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.tv
+
+import android.app.Notification
+import android.service.notification.StatusBarNotification
+import android.view.Surface
+import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.NotificationListener.Companion.startNotificationListener
+import com.android.wm.shell.flicker.NotificationListener.Companion.stopNotificationListener
+import com.android.wm.shell.flicker.NotificationListener.Companion.waitForNotificationToAppear
+import com.android.wm.shell.flicker.NotificationListener.Companion.waitForNotificationToDisappear
+import org.junit.After
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test Pip Notifications on TV.
+ * To run this test: `atest WMShellFlickerTests:TvPipNotificationTests`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class TvPipNotificationTests(rotationName: String, rotation: Int)
+ : TvPipTestBase(rotationName, rotation) {
+
+ @Before
+ override fun setUp() {
+ super.setUp()
+ val started = startNotificationListener()
+ if (!started) {
+ error("NotificationListener hasn't started")
+ }
+ }
+
+ @After
+ override fun tearDown() {
+ stopNotificationListener()
+ testApp.forceStop()
+ super.tearDown()
+ }
+
+ @Test
+ fun pipNotification_postedAndDismissed() {
+ testApp.launchViaIntent()
+ testApp.clickEnterPipButton()
+
+ assertTrue("Pip notification should have been posted",
+ waitForNotificationToAppear { it.isPipNotificationWithTitle(testApp.label) })
+
+ testApp.closePipWindow()
+
+ assertTrue("Pip notification should have been dismissed",
+ waitForNotificationToDisappear { it.isPipNotificationWithTitle(testApp.label) })
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection> {
+ val supportedRotations = intArrayOf(Surface.ROTATION_0)
+ return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+ }
+ }
+}
+
+private const val PIP_NOTIFICATION_TAG = "PipNotification"
+
+private val StatusBarNotification.title: String
+ get() = notification?.extras?.getString(Notification.EXTRA_TITLE) ?: ""
+
+private fun StatusBarNotification.isPipNotificationWithTitle(expectedTitle: String): Boolean =
+ tag == PIP_NOTIFICATION_TAG && title == expectedTitle
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
new file mode 100644
index 0000000000000..104248c64dddf
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.tv
+
+import android.content.pm.PackageManager.FEATURE_LEANBACK
+import android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.wm.shell.flicker.pip.PipTestBase
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+
+abstract class TvPipTestBase(rotationName: String, rotation: Int)
+ : PipTestBase(rotationName, rotation) {
+
+ private val isTelevision: Boolean
+ get() = packageManager.run {
+ hasSystemFeature(FEATURE_LEANBACK) || hasSystemFeature(FEATURE_LEANBACK_ONLY)
+ }
+
+ @Before
+ open fun setUp() {
+ Assume.assumeTrue(isTelevision)
+ uiDevice.wakeUpAndGoToHomeScreen()
+ }
+
+ @After
+ open fun tearDown() {
+ }
+}
\ No newline at end of file