1 /*
2  * Copyright (C) 2024 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 @file:OptIn(ExperimentalCoroutinesApi::class)
18 
19 package com.android.systemui.statusbar.phone.ongoingcall.domain.interactor
20 
21 import android.app.PendingIntent
22 import androidx.test.ext.junit.runners.AndroidJUnit4
23 import androidx.test.filters.SmallTest
24 import com.android.systemui.SysuiTestCase
25 import com.android.systemui.activity.data.repository.activityManagerRepository
26 import com.android.systemui.activity.data.repository.fake
27 import com.android.systemui.coroutines.collectLastValue
28 import com.android.systemui.kosmos.Kosmos
29 import com.android.systemui.kosmos.collectLastValue
30 import com.android.systemui.kosmos.runTest
31 import com.android.systemui.kosmos.useUnconfinedTestDispatcher
32 import com.android.systemui.statusbar.StatusBarIconView
33 import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
34 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
35 import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
36 import com.android.systemui.statusbar.notification.shared.CallType
37 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
38 import com.google.common.truth.Truth.assertThat
39 import kotlinx.coroutines.ExperimentalCoroutinesApi
40 import kotlinx.coroutines.test.runTest
41 import org.junit.Test
42 import org.junit.runner.RunWith
43 import org.mockito.kotlin.mock
44 
45 @SmallTest
46 @RunWith(AndroidJUnit4::class)
47 class OngoingCallInteractorTest : SysuiTestCase() {
48     private val kosmos = Kosmos().useUnconfinedTestDispatcher()
49     private val repository = kosmos.activeNotificationListRepository
50     private val underTest = kosmos.ongoingCallInteractor
51 
52     @Test
<lambda>null53     fun noNotification_emitsNoCall() = runTest {
54         val state by collectLastValue(underTest.ongoingCallState)
55         assertThat(state).isInstanceOf(OngoingCallModel.NoCall::class.java)
56     }
57 
58     @Test
ongoingCallNotification_setsNotificationIconAndIntentnull59     fun ongoingCallNotification_setsNotificationIconAndIntent() =
60         kosmos.runTest {
61             val latest by collectLastValue(underTest.ongoingCallState)
62 
63             // Set up notification with icon view and intent
64             val testIconView: StatusBarIconView = mock()
65             val testIntent: PendingIntent = mock()
66             repository.activeNotifications.value =
67                 ActiveNotificationsStore.Builder()
68                     .apply {
69                         addIndividualNotif(
70                             activeNotificationModel(
71                                 key = "notif1",
72                                 whenTime = 1000L,
73                                 callType = CallType.Ongoing,
74                                 statusBarChipIcon = testIconView,
75                                 contentIntent = testIntent
76                             )
77                         )
78                     }
79                     .build()
80 
81             // Verify model is InCall and has the correct icon and intent.
82             assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
83             val model = latest as OngoingCallModel.InCall
84             assertThat(model.notificationIconView).isSameInstanceAs(testIconView)
85             assertThat(model.intent).isSameInstanceAs(testIntent)
86         }
87 
88     @Test
ongoingCallNotification_emitsInCallnull89     fun ongoingCallNotification_emitsInCall() =
90         kosmos.runTest {
91             val latest by collectLastValue(underTest.ongoingCallState)
92 
93             repository.activeNotifications.value =
94                 ActiveNotificationsStore.Builder()
95                     .apply {
96                         addIndividualNotif(
97                             activeNotificationModel(
98                                 key = "notif1", whenTime = 1000L, callType = CallType.Ongoing
99                             )
100                         )
101                     }
102                     .build()
103 
104             assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
105         }
106 
107     @Test
notificationRemoved_emitsNoCallnull108     fun notificationRemoved_emitsNoCall() =
109         kosmos.runTest {
110             val latest by collectLastValue(underTest.ongoingCallState)
111 
112             repository.activeNotifications.value =
113                 ActiveNotificationsStore.Builder()
114                     .apply {
115                         addIndividualNotif(
116                             activeNotificationModel(
117                                 key = "notif1", whenTime = 1000L, callType = CallType.Ongoing
118                             )
119                         )
120                     }
121                     .build()
122 
123             repository.activeNotifications.value = ActiveNotificationsStore()
124             assertThat(latest).isInstanceOf(OngoingCallModel.NoCall::class.java)
125         }
126 
127     @Test
ongoingCallNotification_appVisibleInitially_emitsInCallWithVisibleAppnull128     fun ongoingCallNotification_appVisibleInitially_emitsInCallWithVisibleApp() =
129         kosmos.runTest {
130             kosmos.activityManagerRepository.fake.startingIsAppVisibleValue = true
131             val latest by collectLastValue(underTest.ongoingCallState)
132 
133             repository.activeNotifications.value =
134                 ActiveNotificationsStore.Builder()
135                     .apply {
136                         addIndividualNotif(
137                             activeNotificationModel(
138                                 key = "notif1",
139                                 whenTime = 1000L,
140                                 callType = CallType.Ongoing,
141                                 uid = UID
142                             )
143                         )
144                     }
145                     .build()
146 
147             assertThat(latest).isInstanceOf(OngoingCallModel.InCallWithVisibleApp::class.java)
148         }
149 
150     @Test
ongoingCallNotification_appNotVisibleInitially_emitsInCallnull151     fun ongoingCallNotification_appNotVisibleInitially_emitsInCall() =
152         kosmos.runTest {
153             kosmos.activityManagerRepository.fake.startingIsAppVisibleValue = false
154             val latest by collectLastValue(underTest.ongoingCallState)
155 
156             repository.activeNotifications.value =
157                 ActiveNotificationsStore.Builder()
158                     .apply {
159                         addIndividualNotif(
160                             activeNotificationModel(
161                                 key = "notif1",
162                                 whenTime = 1000L,
163                                 callType = CallType.Ongoing,
164                                 uid = UID
165                             )
166                         )
167                     }
168                     .build()
169 
170             assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
171         }
172 
173     @Test
ongoingCallNotification_visibilityChanges_updatesStatenull174     fun ongoingCallNotification_visibilityChanges_updatesState() =
175         kosmos.runTest {
176             val latest by collectLastValue(underTest.ongoingCallState)
177 
178             // Start with notification and app not visible
179             kosmos.activityManagerRepository.fake.startingIsAppVisibleValue = false
180             repository.activeNotifications.value =
181                 ActiveNotificationsStore.Builder()
182                     .apply {
183                         addIndividualNotif(
184                             activeNotificationModel(
185                                 key = "notif1",
186                                 whenTime = 1000L,
187                                 callType = CallType.Ongoing,
188                                 uid = UID
189                             )
190                         )
191                     }
192                     .build()
193             assertThat(latest)
194                 .isInstanceOf(OngoingCallModel.InCall::class.java)
195 
196             // App becomes visible
197             kosmos.activityManagerRepository.fake.setIsAppVisible(UID, true)
198             assertThat(latest).isInstanceOf(OngoingCallModel.InCallWithVisibleApp::class.java)
199 
200             // App becomes invisible again
201             kosmos.activityManagerRepository.fake.setIsAppVisible(UID, false)
202             assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
203         }
204 
205     companion object {
206         private const val UID = 885
207     }
208 }
209