1 /*
<lambda>null2  * 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 package com.android.settingslib.notification.data.repository
18 
19 import android.app.NotificationManager
20 import android.content.BroadcastReceiver
21 import android.content.ContentResolver
22 import android.content.Context
23 import android.content.Intent
24 import android.database.ContentObserver
25 import android.os.Parcelable
26 import android.platform.test.annotations.EnableFlags
27 import android.platform.test.flag.junit.SetFlagsRule
28 import android.provider.Settings.Global
29 import androidx.test.filters.SmallTest
30 import com.android.settingslib.notification.modes.TestModeBuilder
31 import com.android.settingslib.notification.modes.ZenMode
32 import com.android.settingslib.notification.modes.ZenModesBackend
33 import com.google.common.truth.Truth.assertThat
34 import kotlinx.coroutines.ExperimentalCoroutinesApi
35 import kotlinx.coroutines.flow.launchIn
36 import kotlinx.coroutines.flow.onEach
37 import kotlinx.coroutines.test.TestScope
38 import kotlinx.coroutines.test.runCurrent
39 import kotlinx.coroutines.test.runTest
40 import org.junit.Before
41 import org.junit.Rule
42 import org.junit.Test
43 import org.junit.runner.RunWith
44 import org.mockito.ArgumentCaptor
45 import org.mockito.ArgumentMatchers.eq
46 import org.mockito.Captor
47 import org.mockito.Mock
48 import org.mockito.Mockito.any
49 import org.mockito.Mockito.verify
50 import org.mockito.Mockito.`when`
51 import org.mockito.MockitoAnnotations
52 import org.robolectric.RobolectricTestRunner
53 
54 @OptIn(ExperimentalCoroutinesApi::class)
55 @RunWith(RobolectricTestRunner::class)
56 @SmallTest
57 class ZenModeRepositoryTest {
58     @get:Rule val setFlagsRule = SetFlagsRule()
59 
60     @Mock private lateinit var context: Context
61 
62     @Mock private lateinit var notificationManager: NotificationManager
63 
64     @Mock private lateinit var zenModesBackend: ZenModesBackend
65 
66     @Mock private lateinit var contentResolver: ContentResolver
67 
68     @Captor private lateinit var receiverCaptor: ArgumentCaptor<BroadcastReceiver>
69 
70     @Captor private lateinit var zenModeObserverCaptor: ArgumentCaptor<ContentObserver>
71 
72     @Captor private lateinit var zenConfigObserverCaptor: ArgumentCaptor<ContentObserver>
73 
74     private lateinit var underTest: ZenModeRepository
75 
76     private val testScope: TestScope = TestScope()
77 
78     @Before
79     fun setup() {
80         MockitoAnnotations.initMocks(this)
81 
82         underTest =
83             ZenModeRepositoryImpl(
84                 context,
85                 notificationManager,
86                 zenModesBackend,
87                 contentResolver,
88                 testScope.backgroundScope,
89                 testScope.testScheduler,
90                 backgroundHandler = null,
91             )
92     }
93 
94     @EnableFlags(android.app.Flags.FLAG_MODES_API)
95     @Test
96     fun consolidatedPolicyChanges_repositoryEmits_flagsOn() {
97         testScope.runTest {
98             val values = mutableListOf<NotificationManager.Policy?>()
99             `when`(notificationManager.consolidatedNotificationPolicy).thenReturn(testPolicy1)
100             underTest.consolidatedNotificationPolicy
101                 .onEach { values.add(it) }
102                 .launchIn(backgroundScope)
103             runCurrent()
104 
105             `when`(notificationManager.consolidatedNotificationPolicy).thenReturn(testPolicy2)
106             triggerIntent(NotificationManager.ACTION_CONSOLIDATED_NOTIFICATION_POLICY_CHANGED)
107             runCurrent()
108 
109             assertThat(values).containsExactly(null, testPolicy1, testPolicy2).inOrder()
110         }
111     }
112 
113     @EnableFlags(android.app.Flags.FLAG_MODES_API)
114     @Test
115     fun consolidatedPolicyChanges_repositoryEmitsFromExtras() {
116         testScope.runTest {
117             val values = mutableListOf<NotificationManager.Policy?>()
118             `when`(notificationManager.consolidatedNotificationPolicy).thenReturn(testPolicy1)
119             underTest.consolidatedNotificationPolicy
120                 .onEach { values.add(it) }
121                 .launchIn(backgroundScope)
122             runCurrent()
123 
124             triggerIntent(
125                 NotificationManager.ACTION_CONSOLIDATED_NOTIFICATION_POLICY_CHANGED,
126                 extras = mapOf(NotificationManager.EXTRA_NOTIFICATION_POLICY to testPolicy2))
127             runCurrent()
128 
129             assertThat(values).containsExactly(null, testPolicy1, testPolicy2).inOrder()
130         }
131     }
132 
133     @Test
134     fun zenModeChanges_repositoryEmits() {
135         testScope.runTest {
136             val values = mutableListOf<Int?>()
137             `when`(notificationManager.zenMode).thenReturn(Global.ZEN_MODE_OFF)
138             underTest.globalZenMode.onEach { values.add(it) }.launchIn(backgroundScope)
139             runCurrent()
140 
141             `when`(notificationManager.zenMode).thenReturn(Global.ZEN_MODE_ALARMS)
142             triggerIntent(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED)
143             runCurrent()
144 
145             assertThat(values)
146                 .containsExactly(null, Global.ZEN_MODE_OFF, Global.ZEN_MODE_ALARMS)
147                 .inOrder()
148         }
149     }
150 
151     @EnableFlags(android.app.Flags.FLAG_MODES_UI)
152     @Test
153     fun modesListEmitsOnSettingsChange() {
154         testScope.runTest {
155             val values = mutableListOf<List<ZenMode>>()
156             val modes1 = listOf(TestModeBuilder().setId("One").build())
157             `when`(zenModesBackend.modes).thenReturn(modes1)
158             underTest.modes.onEach { values.add(it) }.launchIn(backgroundScope)
159             runCurrent()
160 
161             // zen mode change triggers update
162             val modes2 = listOf(TestModeBuilder().setId("Two").build())
163             `when`(zenModesBackend.modes).thenReturn(modes2)
164             triggerZenModeSettingUpdate()
165             runCurrent()
166 
167             // zen config change also triggers update
168             val modes3 = listOf(TestModeBuilder().setId("Three").build())
169             `when`(zenModesBackend.modes).thenReturn(modes3)
170             triggerZenConfigSettingUpdate()
171             runCurrent()
172 
173             // setting update with no list change doesn't trigger update
174             triggerZenModeSettingUpdate()
175             runCurrent()
176 
177             assertThat(values).containsExactly(modes1, modes2, modes3).inOrder()
178         }
179     }
180 
181     @EnableFlags(android.app.Flags.FLAG_MODES_UI)
182     @Test
183     fun getModes_returnsModes() {
184         val modesList = listOf(TestModeBuilder().setId("One").build())
185         `when`(zenModesBackend.modes).thenReturn(modesList)
186 
187         assertThat(underTest.getModes()).isEqualTo(modesList)
188     }
189 
190     private fun triggerIntent(action: String, extras: Map<String, Parcelable>? = null) {
191         verify(context).registerReceiver(receiverCaptor.capture(), any(), any(), any())
192         val intent = Intent(action)
193         if (extras?.isNotEmpty() == true) {
194             extras.forEach { (key, value) -> intent.putExtra(key, value) }
195         }
196         receiverCaptor.value.onReceive(context, intent)
197     }
198 
199     private fun triggerZenModeSettingUpdate() {
200         verify(contentResolver)
201             .registerContentObserver(
202                 eq(Global.getUriFor(Global.ZEN_MODE)),
203                 eq(false),
204                 zenModeObserverCaptor.capture(),
205             )
206         zenModeObserverCaptor.value.onChange(false)
207     }
208 
209     private fun triggerZenConfigSettingUpdate() {
210         verify(contentResolver)
211             .registerContentObserver(
212                 eq(Global.getUriFor(Global.ZEN_MODE_CONFIG_ETAG)),
213                 eq(false),
214                 zenConfigObserverCaptor.capture(),
215             )
216         zenConfigObserverCaptor.value.onChange(false)
217     }
218 
219     private companion object {
220         val testPolicy1 =
221             NotificationManager.Policy(
222                 /* priorityCategories = */ 1,
223                 /* priorityCallSenders =*/ 1,
224                 /* priorityMessageSenders = */ 1,
225             )
226         val testPolicy2 =
227             NotificationManager.Policy(
228                 /* priorityCategories = */ 2,
229                 /* priorityCallSenders =*/ 2,
230                 /* priorityMessageSenders = */ 2,
231             )
232     }
233 }
234