1 /*
2  * Copyright (C) 2021 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.privacy
18 
19 import android.content.Intent
20 import android.testing.TestableLooper
21 import android.view.View
22 import android.view.ViewGroup
23 import android.view.ViewGroup.LayoutParams.MATCH_PARENT
24 import android.widget.TextView
25 import androidx.test.ext.junit.runners.AndroidJUnit4
26 import androidx.test.filters.SmallTest
27 import com.android.systemui.res.R
28 import com.android.systemui.SysuiTestCase
29 import com.google.common.truth.Truth.assertThat
30 import org.junit.After
31 import org.junit.Before
32 import org.junit.Test
33 import org.junit.runner.RunWith
34 import org.mockito.Mock
35 import org.mockito.Mockito.mock
36 import org.mockito.Mockito.never
37 import org.mockito.Mockito.verify
38 import org.mockito.MockitoAnnotations
39 
40 @SmallTest
41 @RunWith(AndroidJUnit4::class)
42 @TestableLooper.RunWithLooper(setAsMainLooper = true)
43 class PrivacyDialogV2Test : SysuiTestCase() {
44 
45     companion object {
46         private const val TEST_PACKAGE_NAME = "test_pkg"
47         private const val TEST_USER_ID = 0
48         private const val TEST_PERM_GROUP = "test_perm_group"
49 
50         private val TEST_INTENT = Intent("test_intent_action")
51 
createPrivacyElementnull52         private fun createPrivacyElement(
53             type: PrivacyType = PrivacyType.TYPE_MICROPHONE,
54             packageName: String = TEST_PACKAGE_NAME,
55             userId: Int = TEST_USER_ID,
56             applicationName: CharSequence = "App",
57             attributionTag: CharSequence? = null,
58             attributionLabel: CharSequence? = null,
59             proxyLabel: CharSequence? = null,
60             lastActiveTimestamp: Long = 0L,
61             isActive: Boolean = false,
62             isPhoneCall: Boolean = false,
63             isService: Boolean = false,
64             permGroupName: String = TEST_PERM_GROUP,
65             navigationIntent: Intent = TEST_INTENT
66         ) =
67             PrivacyDialogV2.PrivacyElement(
68                 type,
69                 packageName,
70                 userId,
71                 applicationName,
72                 attributionTag,
73                 attributionLabel,
74                 proxyLabel,
75                 lastActiveTimestamp,
76                 isActive,
77                 isPhoneCall,
78                 isService,
79                 permGroupName,
80                 navigationIntent
81             )
82     }
83 
84     @Mock private lateinit var manageApp: (String, Int, Intent) -> Unit
85     @Mock private lateinit var closeApp: (String, Int) -> Unit
86     @Mock private lateinit var openPrivacyDashboard: () -> Unit
87     private lateinit var dialog: PrivacyDialogV2
88 
89     @Before
90     fun setUp() {
91         MockitoAnnotations.initMocks(this)
92     }
93 
94     @After
teardownnull95     fun teardown() {
96         if (this::dialog.isInitialized) {
97             dialog.dismiss()
98         }
99     }
100 
101     @Test
testManageAppCalledWithCorrectParamsnull102     fun testManageAppCalledWithCorrectParams() {
103         val list = listOf(createPrivacyElement())
104         dialog = PrivacyDialogV2(context, list, manageApp, closeApp, openPrivacyDashboard)
105         dialog.show()
106 
107         dialog.requireViewById<View>(R.id.privacy_dialog_manage_app_button).callOnClick()
108 
109         verify(manageApp).invoke(TEST_PACKAGE_NAME, TEST_USER_ID, TEST_INTENT)
110     }
111 
112     @Test
testCloseAppCalledWithCorrectParamsnull113     fun testCloseAppCalledWithCorrectParams() {
114         val list = listOf(createPrivacyElement(isActive = true))
115         dialog = PrivacyDialogV2(context, list, manageApp, closeApp, openPrivacyDashboard)
116         dialog.show()
117 
118         dialog.requireViewById<View>(R.id.privacy_dialog_close_app_button).callOnClick()
119 
120         verify(closeApp).invoke(TEST_PACKAGE_NAME, TEST_USER_ID)
121     }
122 
123     @Test
testCloseAppMissingForServicenull124     fun testCloseAppMissingForService() {
125         val list = listOf(createPrivacyElement(isActive = true, isService = true))
126         dialog = PrivacyDialogV2(context, list, manageApp, closeApp, openPrivacyDashboard)
127 
128         dialog.show()
129 
130         assertThat(dialog.findViewById<View>(R.id.privacy_dialog_manage_app_button)).isNotNull()
131         assertThat(dialog.findViewById<View>(R.id.privacy_dialog_close_app_button)).isNull()
132     }
133 
134     @Test
testMoreButtonnull135     fun testMoreButton() {
136         dialog = PrivacyDialogV2(context, emptyList(), manageApp, closeApp, openPrivacyDashboard)
137         dialog.show()
138 
139         dialog.requireViewById<View>(R.id.privacy_dialog_more_button).callOnClick()
140 
141         verify(openPrivacyDashboard).invoke()
142     }
143 
144     @Test
testCloseButtonnull145     fun testCloseButton() {
146         dialog = PrivacyDialogV2(context, emptyList(), manageApp, closeApp, openPrivacyDashboard)
147         val dismissListener = mock(PrivacyDialogV2.OnDialogDismissed::class.java)
148         dialog.addOnDismissListener(dismissListener)
149         dialog.show()
150         verify(dismissListener, never()).onDialogDismissed()
151 
152         dialog.requireViewById<View>(R.id.privacy_dialog_close_button).callOnClick()
153 
154         verify(dismissListener).onDialogDismissed()
155     }
156 
157     @Test
testDismissListenerCalledOnDismissnull158     fun testDismissListenerCalledOnDismiss() {
159         dialog = PrivacyDialogV2(context, emptyList(), manageApp, closeApp, openPrivacyDashboard)
160         val dismissListener = mock(PrivacyDialogV2.OnDialogDismissed::class.java)
161         dialog.addOnDismissListener(dismissListener)
162         dialog.show()
163         verify(dismissListener, never()).onDialogDismissed()
164 
165         dialog.dismiss()
166 
167         verify(dismissListener).onDialogDismissed()
168     }
169 
170     @Test
testDismissListenerCalledImmediatelyIfDialogAlreadyDismissednull171     fun testDismissListenerCalledImmediatelyIfDialogAlreadyDismissed() {
172         dialog = PrivacyDialogV2(context, emptyList(), manageApp, closeApp, openPrivacyDashboard)
173         val dismissListener = mock(PrivacyDialogV2.OnDialogDismissed::class.java)
174         dialog.show()
175         dialog.dismiss()
176 
177         dialog.addOnDismissListener(dismissListener)
178 
179         verify(dismissListener).onDialogDismissed()
180     }
181 
182     @Test
testCorrectNumElementsnull183     fun testCorrectNumElements() {
184         val list =
185             listOf(
186                 createPrivacyElement(type = PrivacyType.TYPE_CAMERA, isActive = true),
187                 createPrivacyElement()
188             )
189         dialog = PrivacyDialogV2(context, list, manageApp, closeApp, openPrivacyDashboard)
190 
191         dialog.show()
192 
193         assertThat(
194                 dialog.requireViewById<ViewGroup>(R.id.privacy_dialog_items_container).childCount
195             )
196             .isEqualTo(2)
197     }
198 
199     @Test
testHeaderTextnull200     fun testHeaderText() {
201         val list = listOf(createPrivacyElement())
202         dialog = PrivacyDialogV2(context, list, manageApp, closeApp, openPrivacyDashboard)
203 
204         dialog.show()
205 
206         assertThat(dialog.requireViewById<TextView>(R.id.privacy_dialog_item_header_title).text)
207             .isEqualTo(TEST_PERM_GROUP)
208     }
209 
210     @Test
testUsingTextnull211     fun testUsingText() {
212         val list = listOf(createPrivacyElement(type = PrivacyType.TYPE_CAMERA, isActive = true))
213         dialog = PrivacyDialogV2(context, list, manageApp, closeApp, openPrivacyDashboard)
214 
215         dialog.show()
216 
217         assertThat(dialog.requireViewById<TextView>(R.id.privacy_dialog_item_header_summary).text)
218             .isEqualTo("In use by App")
219     }
220 
221     @Test
testRecentTextnull222     fun testRecentText() {
223         val list = listOf(createPrivacyElement())
224         dialog = PrivacyDialogV2(context, list, manageApp, closeApp, openPrivacyDashboard)
225 
226         dialog.show()
227 
228         assertThat(dialog.requireViewById<TextView>(R.id.privacy_dialog_item_header_summary).text)
229             .isEqualTo("Recently used by App")
230     }
231 
232     @Test
testPhoneCallnull233     fun testPhoneCall() {
234         val list = listOf(createPrivacyElement(isPhoneCall = true))
235         dialog = PrivacyDialogV2(context, list, manageApp, closeApp, openPrivacyDashboard)
236 
237         dialog.show()
238 
239         assertThat(dialog.requireViewById<TextView>(R.id.privacy_dialog_item_header_summary).text)
240             .isEqualTo("Recently used in phone call")
241     }
242 
243     @Test
testPhoneCallNotClickablenull244     fun testPhoneCallNotClickable() {
245         val list = listOf(createPrivacyElement(isPhoneCall = true))
246         dialog = PrivacyDialogV2(context, list, manageApp, closeApp, openPrivacyDashboard)
247 
248         dialog.show()
249 
250         assertThat(dialog.requireViewById<View>(R.id.privacy_dialog_item_card).isClickable)
251             .isFalse()
252         assertThat(
253                 dialog
254                     .requireViewById<View>(R.id.privacy_dialog_item_header_expand_toggle)
255                     .visibility
256             )
257             .isEqualTo(View.GONE)
258     }
259 
260     @Test
testProxyLabelnull261     fun testProxyLabel() {
262         val list = listOf(createPrivacyElement(proxyLabel = "proxy label"))
263         dialog = PrivacyDialogV2(context, list, manageApp, closeApp, openPrivacyDashboard)
264 
265         dialog.show()
266 
267         assertThat(dialog.requireViewById<TextView>(R.id.privacy_dialog_item_header_summary).text)
268             .isEqualTo("Recently used by App (proxy label)")
269     }
270 
271     @Test
testSubattributionnull272     fun testSubattribution() {
273         val list =
274             listOf(
275                 createPrivacyElement(
276                     attributionLabel = "For subattribution",
277                     isActive = true,
278                     isService = true
279                 )
280             )
281         dialog = PrivacyDialogV2(context, list, manageApp, closeApp, openPrivacyDashboard)
282 
283         dialog.show()
284 
285         assertThat(dialog.requireViewById<TextView>(R.id.privacy_dialog_item_header_summary).text)
286             .isEqualTo("In use by App (For subattribution)")
287     }
288 
289     @Test
testSubattributionAndProxyLabelnull290     fun testSubattributionAndProxyLabel() {
291         val list =
292             listOf(
293                 createPrivacyElement(
294                     attributionLabel = "For subattribution",
295                     proxyLabel = "proxy label",
296                     isActive = true
297                 )
298             )
299         dialog = PrivacyDialogV2(context, list, manageApp, closeApp, openPrivacyDashboard)
300         dialog.show()
301         assertThat(dialog.requireViewById<TextView>(R.id.privacy_dialog_item_header_summary).text)
302             .isEqualTo("In use by App (For subattribution \u2022 proxy label)")
303     }
304 
305     @Test
testDialogHasTitlenull306     fun testDialogHasTitle() {
307         val list = listOf(createPrivacyElement())
308         dialog = PrivacyDialogV2(context, list, manageApp, closeApp, openPrivacyDashboard)
309         dialog.show()
310 
311         assertThat(dialog.window?.attributes?.title).isEqualTo("Microphone & Camera")
312     }
313 
314     @Test
testDialogIsFullscreennull315     fun testDialogIsFullscreen() {
316         dialog = PrivacyDialogV2(context, emptyList(), manageApp, closeApp, openPrivacyDashboard)
317         dialog.show()
318 
319         assertThat(dialog.window?.attributes?.width).isEqualTo(MATCH_PARENT)
320         assertThat(dialog.window?.attributes?.height).isEqualTo(MATCH_PARENT)
321     }
322 }
323