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