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 index 5c7ec30ecd3ea..3957f5a1c6f50 100644 --- 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 @@ -74,13 +74,17 @@ class NotificationListener : NotificationListenerService() { return wait { instance == null } } - fun waitForNotificationToAppear(predicate: (StatusBarNotification) -> Boolean): Boolean { + fun waitForNotificationToAppear( + predicate: (StatusBarNotification) -> Boolean + ): StatusBarNotification? { return instance?.let { - wait { it.notifications.values.any(predicate) } + waitForResult(extractor = { it.notifications.values.first(predicate) }).second } ?: throw IllegalStateException("NotificationListenerService is not connected") } - fun waitForNotificationToDisappear(predicate: (StatusBarNotification) -> Boolean): Boolean { + fun waitForNotificationToDisappear( + predicate: (StatusBarNotification) -> Boolean + ): Boolean { return instance?.let { wait { it.notifications.values.none(predicate) } } ?: throw IllegalStateException("NotificationListenerService is not connected") 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 index e8cf7d978420c..a588888c59021 100644 --- 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 @@ -66,6 +66,10 @@ abstract class BaseAppHelper( uiDevice.wait(Until.hasObject(appSelector), APP_LAUNCH_WAIT_TIME_MS) } + fun waitUntilClosed(): Boolean { + return uiDevice.wait(Until.gone(appSelector), APP_CLOSE_WAIT_TIME_MS) + } + fun forceStop() = activityManager?.forceStopPackage(packageName) override fun getOpenAppIntent(): Intent { @@ -77,5 +81,6 @@ abstract class BaseAppHelper( companion object { private const val APP_LAUNCH_WAIT_TIME_MS = 10_000L + private const val APP_CLOSE_WAIT_TIME_MS = 3_000L } } 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 d5efa409e6adf..edde581824d16 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 @@ -65,8 +65,7 @@ class PipAppHelper( assertNotNull("Pip menu close button is not found", closeButton) closeButton.click() - // Give it 1 second, just in case - SystemClock.sleep(1_000) + waitUntilClosed() } } } 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 index 569da1ddd953d..8bb78c232732a 100644 --- 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 @@ -17,6 +17,8 @@ package com.android.wm.shell.flicker.pip.tv import android.app.Notification +import android.app.PendingIntent +import android.os.Bundle import android.service.notification.StatusBarNotification import android.view.Surface import androidx.test.filters.RequiresDevice @@ -25,6 +27,7 @@ import com.android.wm.shell.flicker.NotificationListener.Companion.stopNotificat 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.assertNotNull import org.junit.Assert.assertTrue import org.junit.Before import org.junit.FixMethodOrder @@ -64,7 +67,7 @@ class TvPipNotificationTests(rotationName: String, rotation: Int) testApp.launchViaIntent() testApp.clickEnterPipButton() - assertTrue("Pip notification should have been posted", + assertNotNull("Pip notification should have been posted", waitForNotificationToAppear { it.isPipNotificationWithTitle(testApp.label) }) testApp.closePipWindow() @@ -73,6 +76,43 @@ class TvPipNotificationTests(rotationName: String, rotation: Int) waitForNotificationToDisappear { it.isPipNotificationWithTitle(testApp.label) }) } + @Test + fun pipNotification_closeIntent() { + testApp.launchViaIntent() + testApp.clickEnterPipButton() + + val notification: StatusBarNotification = waitForNotificationToAppear { + it.isPipNotificationWithTitle(testApp.label) + } ?: throw AssertionError("Pip notification should have been posted") + + notification.deleteIntent?.send() + ?: throw AssertionError("Pip notification should contain `delete_intent`") + + assertTrue("Pip should have closed by sending the `delete_intent`", + testApp.waitUntilClosed()) + assertTrue("Pip notification should have been dismissed", + waitForNotificationToDisappear { it.isPipNotificationWithTitle(testApp.label) }) + } + + @Test + fun pipNotification_menuIntent() { + testApp.launchViaIntent() + testApp.clickEnterPipButton() + + val notification: StatusBarNotification = waitForNotificationToAppear { + it.isPipNotificationWithTitle(testApp.label) + } ?: throw AssertionError("Pip notification should have been posted") + + notification.contentIntent?.send() + ?: throw AssertionError("Pip notification should contain `content_intent`") + + assertTrue("Pip menu should have been shown after sending `content_intent`", + uiDevice.waitForTvPipMenu()) + + uiDevice.pressBack() + testApp.closePipWindow() + } + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic @@ -83,10 +123,23 @@ class TvPipNotificationTests(rotationName: String, rotation: Int) } } -private const val PIP_NOTIFICATION_TAG = "PipNotification" +private val StatusBarNotification.extras: Bundle? + get() = notification?.extras private val StatusBarNotification.title: String - get() = notification?.extras?.getString(Notification.EXTRA_TITLE) ?: "" + get() = extras?.getString(Notification.EXTRA_TITLE) ?: "" + +/** Get TV extensions with [android.app.Notification.TvExtender.EXTRA_TV_EXTENDER]. */ +private val StatusBarNotification.tvExtensions: Bundle? + get() = extras?.getBundle("android.tv.EXTENSIONS") + +/** "Content" TV intent with key [android.app.Notification.TvExtender.EXTRA_CONTENT_INTENT]. */ +private val StatusBarNotification.contentIntent: PendingIntent? + get() = tvExtensions?.getParcelable("content_intent") + +/** "Delete" TV intent with key [android.app.Notification.TvExtender.EXTRA_DELETE_INTENT]. */ +private val StatusBarNotification.deleteIntent: PendingIntent? + get() = tvExtensions?.getParcelable("delete_intent") private fun StatusBarNotification.isPipNotificationWithTitle(expectedTitle: String): Boolean = - tag == PIP_NOTIFICATION_TAG && title == expectedTitle \ No newline at end of file + tag == "PipNotification" && title == expectedTitle \ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt new file mode 100644 index 0000000000000..ac9ab130876dc --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt @@ -0,0 +1,26 @@ +/* + * 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 androidx.test.uiautomator.By +import androidx.test.uiautomator.UiDevice +import androidx.test.uiautomator.Until +import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME + +fun UiDevice.waitForTvPipMenu(): Boolean { + return wait(Until.findObject(By.res(SYSTEM_UI_PACKAGE_NAME, "pip_controls")), 3_000) != null +} \ No newline at end of file