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.qs.tiles
18 
19 import android.app.Dialog
20 import android.content.ContextWrapper
21 import android.content.SharedPreferences
22 import android.os.Handler
23 import android.provider.Settings
24 import android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS
25 import android.provider.Settings.Global.ZEN_MODE_OFF
26 import android.testing.AndroidTestingRunner
27 import android.testing.TestableLooper
28 import android.view.ContextThemeWrapper
29 import android.view.View
30 import androidx.test.filters.SmallTest
31 import com.android.internal.logging.MetricsLogger
32 import com.android.systemui.R
33 import com.android.systemui.SysuiTestCase
34 import com.android.systemui.animation.DialogLaunchAnimator
35 import com.android.systemui.classifier.FalsingManagerFake
36 import com.android.systemui.plugins.ActivityStarter
37 import com.android.systemui.plugins.qs.QSTile
38 import com.android.systemui.plugins.statusbar.StatusBarStateController
39 import com.android.systemui.qs.QSHost
40 import com.android.systemui.qs.QsEventLogger
41 import com.android.systemui.qs.logging.QSLogger
42 import com.android.systemui.qs.tileimpl.QSTileImpl
43 import com.android.systemui.statusbar.policy.ZenModeController
44 import com.android.systemui.util.mockito.any
45 import com.android.systemui.util.mockito.eq
46 import com.android.systemui.util.mockito.nullable
47 import com.android.systemui.util.settings.FakeSettings
48 import com.android.systemui.util.settings.SecureSettings
49 import com.google.common.truth.Truth.assertThat
50 import org.junit.After
51 import org.junit.Before
52 import org.junit.Test
53 import org.junit.runner.RunWith
54 import org.mockito.Mock
55 import org.mockito.Mockito.anyBoolean
56 import org.mockito.Mockito.never
57 import org.mockito.Mockito.verify
58 import org.mockito.MockitoAnnotations
59 import java.io.File
60 import org.mockito.Mockito.`when` as whenever
61 
62 @SmallTest
63 @RunWith(AndroidTestingRunner::class)
64 @TestableLooper.RunWithLooper(setAsMainLooper = true)
65 class DndTileTest : SysuiTestCase() {
66 
67     companion object {
68         private const val DEFAULT_USER = 0
69         private const val KEY = Settings.Secure.ZEN_DURATION
70     }
71 
72     @Mock
73     private lateinit var qsHost: QSHost
74 
75     @Mock
76     private lateinit var metricsLogger: MetricsLogger
77 
78     @Mock
79     private lateinit var statusBarStateController: StatusBarStateController
80 
81     @Mock
82     private lateinit var activityStarter: ActivityStarter
83 
84     @Mock
85     private lateinit var qsLogger: QSLogger
86 
87     @Mock
88     private lateinit var uiEventLogger: QsEventLogger
89 
90     @Mock
91     private lateinit var zenModeController: ZenModeController
92 
93     @Mock
94     private lateinit var sharedPreferences: SharedPreferences
95 
96     @Mock
97     private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
98 
99     @Mock
100     private lateinit var hostDialog: Dialog
101 
102     private lateinit var secureSettings: SecureSettings
103     private lateinit var testableLooper: TestableLooper
104     private lateinit var tile: DndTile
105 
106     @Before
107     fun setUp() {
108         MockitoAnnotations.initMocks(this)
109         testableLooper = TestableLooper.get(this)
110         secureSettings = FakeSettings()
111 
112         whenever(qsHost.userId).thenReturn(DEFAULT_USER)
113 
114         val wrappedContext = object : ContextWrapper(
115                 ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings)
116         ) {
117             override fun getSharedPreferences(file: File?, mode: Int): SharedPreferences {
118                 return sharedPreferences
119             }
120         }
121         whenever(qsHost.context).thenReturn(wrappedContext)
122 
123         tile = DndTile(
124             qsHost,
125             uiEventLogger,
126             testableLooper.looper,
127             Handler(testableLooper.looper),
128             FalsingManagerFake(),
129             metricsLogger,
130             statusBarStateController,
131             activityStarter,
132             qsLogger,
133             zenModeController,
134             sharedPreferences,
135             secureSettings,
136             dialogLaunchAnimator
137         )
138     }
139 
140     @After
141     fun tearDown() {
142         tile.destroy()
143         testableLooper.processAllMessages()
144     }
145 
146     @Test
147     fun testForceExpandIcon_durationAskAlways_true() {
148         secureSettings.putIntForUser(KEY, Settings.Secure.ZEN_DURATION_PROMPT, DEFAULT_USER)
149 
150         tile.refreshState()
151         testableLooper.processAllMessages()
152 
153         assertThat(tile.state.forceExpandIcon).isTrue()
154     }
155 
156     @Test
157     fun testForceExpandIcon_durationNotAskAlways_false() {
158         secureSettings.putIntForUser(KEY, 60, DEFAULT_USER)
159 
160         tile.refreshState()
161         testableLooper.processAllMessages()
162 
163         assertThat(tile.state.forceExpandIcon).isFalse()
164     }
165 
166     @Test
167     fun testForceExpandIcon_changeWhileListening() {
168         secureSettings.putIntForUser(KEY, 60, DEFAULT_USER)
169 
170         tile.refreshState()
171         testableLooper.processAllMessages()
172 
173         assertThat(tile.state.forceExpandIcon).isFalse()
174 
175         tile.handleSetListening(true)
176 
177         secureSettings.putIntForUser(KEY, Settings.Secure.ZEN_DURATION_PROMPT, DEFAULT_USER)
178         testableLooper.processAllMessages()
179 
180         assertThat(tile.state.forceExpandIcon).isTrue()
181     }
182 
183     @Test
184     fun testLaunchDialogFromViewWhenPrompt() {
185         whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
186 
187         secureSettings.putIntForUser(KEY, Settings.Secure.ZEN_DURATION_PROMPT, DEFAULT_USER)
188         testableLooper.processAllMessages()
189 
190         val view = View(context)
191         tile.handleClick(view)
192         testableLooper.processAllMessages()
193 
194         verify(dialogLaunchAnimator).showFromView(any(), eq(view), nullable(), anyBoolean())
195     }
196 
197     @Test
198     fun testNoLaunchDialogWhenNotPrompt() {
199         whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
200 
201         secureSettings.putIntForUser(KEY, 60, DEFAULT_USER)
202         testableLooper.processAllMessages()
203 
204         val view = View(context)
205         tile.handleClick(view)
206         testableLooper.processAllMessages()
207 
208         verify(dialogLaunchAnimator, never()).showFromView(any(), any(), nullable(), anyBoolean())
209     }
210 
211     @Test
212     fun testIcon_whenDndModeOff_isOffState() {
213         whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
214         val state = QSTile.BooleanState()
215 
216         tile.handleUpdateState(state, /* arg= */ null)
217 
218         assertThat(state.icon).isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_dnd_icon_off))
219     }
220 
221     @Test
222     fun testIcon_whenDndModeOn_isOnState() {
223         whenever(zenModeController.zen).thenReturn(ZEN_MODE_NO_INTERRUPTIONS)
224         val state = QSTile.BooleanState()
225 
226         tile.handleUpdateState(state, /* arg= */ null)
227 
228         assertThat(state.icon).isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_dnd_icon_on))
229     }
230 }
231