1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.mediaprojection.taskswitcher.ui 18 19 import android.app.Notification 20 import android.app.NotificationManager 21 import androidx.test.ext.junit.runners.AndroidJUnit4 22 import androidx.test.filters.SmallTest 23 import com.android.systemui.SysuiTestCase 24 import com.android.systemui.kosmos.testScope 25 import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask 26 import com.android.systemui.mediaprojection.taskswitcher.FakeMediaProjectionManager 27 import com.android.systemui.mediaprojection.taskswitcher.fakeActivityTaskManager 28 import com.android.systemui.mediaprojection.taskswitcher.fakeMediaProjectionManager 29 import com.android.systemui.mediaprojection.taskswitcher.taskSwitcherKosmos 30 import com.android.systemui.mediaprojection.taskswitcher.taskSwitcherViewModel 31 import com.android.systemui.res.R 32 import com.android.systemui.util.mockito.any 33 import com.android.systemui.util.mockito.argumentCaptor 34 import com.android.systemui.util.mockito.mock 35 import com.google.common.truth.Truth.assertThat 36 import junit.framework.Assert.assertEquals 37 import kotlinx.coroutines.test.runTest 38 import org.junit.Before 39 import org.junit.Test 40 import org.junit.runner.RunWith 41 import org.mockito.ArgumentCaptor 42 import org.mockito.Mockito.never 43 import org.mockito.Mockito.reset 44 import org.mockito.Mockito.verify 45 46 @RunWith(AndroidJUnit4::class) 47 @SmallTest 48 class TaskSwitcherNotificationCoordinatorTest : SysuiTestCase() { 49 50 private val notificationManager = mock<NotificationManager>() 51 private val kosmos = taskSwitcherKosmos() 52 private val testScope = kosmos.testScope 53 private val fakeActivityTaskManager = kosmos.fakeActivityTaskManager 54 private val fakeMediaProjectionManager = kosmos.fakeMediaProjectionManager 55 private val viewModel = kosmos.taskSwitcherViewModel 56 57 private lateinit var coordinator: TaskSwitcherNotificationCoordinator 58 59 @Before setupnull60 fun setup() { 61 coordinator = 62 TaskSwitcherNotificationCoordinator( 63 context, 64 notificationManager, 65 testScope.backgroundScope, 66 viewModel, 67 fakeBroadcastDispatcher, 68 ) 69 coordinator.start() 70 // When the coordinator starts up, the view model will immediately emit a NotShowing event 71 // and hide the notification. That's fine, but we should reset the notification manager so 72 // that the initial emission isn't part of the tests. 73 reset(notificationManager) 74 } 75 76 @Test showNotificationnull77 fun showNotification() { 78 testScope.runTest { 79 switchTask() 80 81 val notification = ArgumentCaptor.forClass(Notification::class.java) 82 verify(notificationManager).notify(any(), any(), notification.capture()) 83 assertNotification(notification) 84 } 85 } 86 87 @Test hideNotificationnull88 fun hideNotification() { 89 testScope.runTest { 90 // First, show a notification 91 switchTask() 92 93 // WHEN the projection is stopped 94 fakeMediaProjectionManager.dispatchOnStop() 95 96 // THEN the notification is hidden 97 verify(notificationManager).cancel(any(), any()) 98 } 99 } 100 101 @Test notificationIdIsConsistentnull102 fun notificationIdIsConsistent() { 103 testScope.runTest { 104 // First, show a notification 105 switchTask() 106 val idNotify = argumentCaptor<Int>() 107 verify(notificationManager).notify(any(), idNotify.capture(), any()) 108 109 // Then, hide the notification 110 fakeMediaProjectionManager.dispatchOnStop() 111 val idCancel = argumentCaptor<Int>() 112 verify(notificationManager).cancel(any(), idCancel.capture()) 113 114 assertEquals(idCancel.value, idNotify.value) 115 } 116 } 117 118 @Test switchTaskAction_hidesNotificationnull119 fun switchTaskAction_hidesNotification() = 120 testScope.runTest { 121 switchTask() 122 val notification = argumentCaptor<Notification>() 123 verify(notificationManager).notify(any(), any(), notification.capture()) 124 verify(notificationManager, never()).cancel(any(), any()) 125 126 val action = findSwitchAction(notification.value) 127 fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( 128 context, 129 action.actionIntent.intent 130 ) 131 132 verify(notificationManager).cancel(any(), any()) 133 } 134 135 @Test goBackAction_hidesNotificationnull136 fun goBackAction_hidesNotification() = 137 testScope.runTest { 138 switchTask() 139 val notification = argumentCaptor<Notification>() 140 verify(notificationManager).notify(any(), any(), notification.capture()) 141 verify(notificationManager, never()).cancel(any(), any()) 142 143 val action = findGoBackAction(notification.value) 144 fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( 145 context, 146 action.actionIntent.intent 147 ) 148 149 verify(notificationManager).cancel(any(), any()) 150 } 151 findSwitchActionnull152 private fun findSwitchAction(notification: Notification): Notification.Action { 153 return notification.actions.first { 154 it.title == context.getString(R.string.media_projection_task_switcher_action_switch) 155 } 156 } 157 findGoBackActionnull158 private fun findGoBackAction(notification: Notification): Notification.Action { 159 return notification.actions.first { 160 it.title == context.getString(R.string.media_projection_task_switcher_action_back) 161 } 162 } 163 switchTasknull164 private fun switchTask() { 165 val projectedTask = createTask(taskId = 1) 166 val foregroundTask = createTask(taskId = 2) 167 fakeActivityTaskManager.addRunningTasks(projectedTask, foregroundTask) 168 fakeMediaProjectionManager.dispatchOnSessionSet( 169 session = 170 FakeMediaProjectionManager.createSingleTaskSession(projectedTask.token.asBinder()) 171 ) 172 fakeActivityTaskManager.moveTaskToForeground(foregroundTask) 173 } 174 assertNotificationnull175 private fun assertNotification(notification: ArgumentCaptor<Notification>) { 176 val text = notification.value.extras.getCharSequence(Notification.EXTRA_TEXT) 177 assertEquals(context.getString(R.string.media_projection_task_switcher_text), text) 178 179 val actions = notification.value.actions 180 assertThat(actions).hasLength(2) 181 } 182 } 183