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.dreams; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.junit.Assert.assertEquals; 22 import static org.junit.Assert.assertTrue; 23 import static org.mockito.Mockito.clearInvocations; 24 import static org.mockito.Mockito.never; 25 import static org.mockito.Mockito.times; 26 import static org.mockito.Mockito.verify; 27 import static org.mockito.Mockito.when; 28 29 import android.testing.AndroidTestingRunner; 30 31 import androidx.test.filters.SmallTest; 32 33 import com.android.systemui.SysuiTestCase; 34 import com.android.systemui.complication.Complication; 35 import com.android.systemui.flags.FeatureFlags; 36 import com.android.systemui.flags.Flags; 37 import com.android.systemui.log.LogBuffer; 38 import com.android.systemui.log.core.FakeLogBuffer; 39 import com.android.systemui.util.concurrency.FakeExecutor; 40 import com.android.systemui.util.time.FakeSystemClock; 41 42 import org.junit.Before; 43 import org.junit.Test; 44 import org.junit.runner.RunWith; 45 import org.mockito.Mock; 46 import org.mockito.Mockito; 47 import org.mockito.MockitoAnnotations; 48 49 import java.util.Collection; 50 51 @SmallTest 52 @RunWith(AndroidTestingRunner.class) 53 public class DreamOverlayStateControllerTest extends SysuiTestCase { 54 @Mock 55 DreamOverlayStateController.Callback mCallback; 56 57 @Mock 58 Complication mComplication; 59 60 @Mock 61 private FeatureFlags mFeatureFlags; 62 63 private final LogBuffer mLogBuffer = FakeLogBuffer.Factory.Companion.create(); 64 65 final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); 66 67 @Before setup()68 public void setup() { 69 MockitoAnnotations.initMocks(this); 70 71 when(mFeatureFlags.isEnabled(Flags.ALWAYS_SHOW_HOME_CONTROLS_ON_DREAMS)).thenReturn(false); 72 } 73 74 @Test testStateChange_overlayActive()75 public void testStateChange_overlayActive() { 76 final DreamOverlayStateController stateController = getDreamOverlayStateController(true); 77 stateController.addCallback(mCallback); 78 stateController.setOverlayActive(true); 79 mExecutor.runAllReady(); 80 81 verify(mCallback).onStateChanged(); 82 assertThat(stateController.isOverlayActive()).isTrue(); 83 84 Mockito.clearInvocations(mCallback); 85 stateController.setOverlayActive(true); 86 mExecutor.runAllReady(); 87 verify(mCallback, never()).onStateChanged(); 88 89 stateController.setOverlayActive(false); 90 mExecutor.runAllReady(); 91 verify(mCallback).onStateChanged(); 92 assertThat(stateController.isOverlayActive()).isFalse(); 93 } 94 95 @Test testCallback()96 public void testCallback() { 97 final DreamOverlayStateController stateController = getDreamOverlayStateController(true); 98 stateController.addCallback(mCallback); 99 100 // Add complication and verify callback is notified. 101 stateController.addComplication(mComplication); 102 103 mExecutor.runAllReady(); 104 105 verify(mCallback, times(1)).onComplicationsChanged(); 106 107 final Collection<Complication> complications = stateController.getComplications(); 108 assertEquals(complications.size(), 1); 109 assertTrue(complications.contains(mComplication)); 110 111 clearInvocations(mCallback); 112 113 // Remove complication and verify callback is notified. 114 stateController.removeComplication(mComplication); 115 mExecutor.runAllReady(); 116 verify(mCallback, times(1)).onComplicationsChanged(); 117 assertTrue(stateController.getComplications().isEmpty()); 118 } 119 120 @Test testNotifyOnCallbackAdd()121 public void testNotifyOnCallbackAdd() { 122 final DreamOverlayStateController stateController = getDreamOverlayStateController(true); 123 124 stateController.addComplication(mComplication); 125 mExecutor.runAllReady(); 126 127 // Verify callback occurs on add when an overlay is already present. 128 stateController.addCallback(mCallback); 129 mExecutor.runAllReady(); 130 verify(mCallback, times(1)).onComplicationsChanged(); 131 } 132 133 @Test testNotifyOnCallbackAddOverlayDisabled()134 public void testNotifyOnCallbackAddOverlayDisabled() { 135 final DreamOverlayStateController stateController = getDreamOverlayStateController(false); 136 137 stateController.addComplication(mComplication); 138 mExecutor.runAllReady(); 139 140 // Verify callback occurs on add when an overlay is already present. 141 stateController.addCallback(mCallback); 142 mExecutor.runAllReady(); 143 verify(mCallback, never()).onComplicationsChanged(); 144 } 145 146 147 @Test testComplicationFilteringWhenShouldShowComplications()148 public void testComplicationFilteringWhenShouldShowComplications() { 149 final DreamOverlayStateController stateController = getDreamOverlayStateController(true); 150 stateController.setShouldShowComplications(true); 151 152 final Complication alwaysAvailableComplication = Mockito.mock(Complication.class); 153 final Complication weatherComplication = Mockito.mock(Complication.class); 154 when(alwaysAvailableComplication.getRequiredTypeAvailability()) 155 .thenReturn(Complication.COMPLICATION_TYPE_NONE); 156 when(weatherComplication.getRequiredTypeAvailability()) 157 .thenReturn(Complication.COMPLICATION_TYPE_WEATHER); 158 159 stateController.addComplication(alwaysAvailableComplication); 160 stateController.addComplication(weatherComplication); 161 162 final DreamOverlayStateController.Callback callback = 163 Mockito.mock(DreamOverlayStateController.Callback.class); 164 165 stateController.addCallback(callback); 166 mExecutor.runAllReady(); 167 168 { 169 final Collection<Complication> complications = stateController.getComplications(); 170 assertThat(complications.contains(alwaysAvailableComplication)).isTrue(); 171 assertThat(complications.contains(weatherComplication)).isFalse(); 172 } 173 174 stateController.setAvailableComplicationTypes(Complication.COMPLICATION_TYPE_WEATHER); 175 mExecutor.runAllReady(); 176 verify(callback).onAvailableComplicationTypesChanged(); 177 178 { 179 final Collection<Complication> complications = stateController.getComplications(); 180 assertThat(complications.contains(alwaysAvailableComplication)).isTrue(); 181 assertThat(complications.contains(weatherComplication)).isTrue(); 182 } 183 184 } 185 186 @Test testComplicationFilteringWhenShouldHideComplications()187 public void testComplicationFilteringWhenShouldHideComplications() { 188 final DreamOverlayStateController stateController = getDreamOverlayStateController(true); 189 stateController.setShouldShowComplications(true); 190 191 final Complication alwaysAvailableComplication = Mockito.mock(Complication.class); 192 final Complication weatherComplication = Mockito.mock(Complication.class); 193 when(alwaysAvailableComplication.getRequiredTypeAvailability()) 194 .thenReturn(Complication.COMPLICATION_TYPE_NONE); 195 when(weatherComplication.getRequiredTypeAvailability()) 196 .thenReturn(Complication.COMPLICATION_TYPE_WEATHER); 197 198 stateController.addComplication(alwaysAvailableComplication); 199 stateController.addComplication(weatherComplication); 200 201 final DreamOverlayStateController.Callback callback = 202 Mockito.mock(DreamOverlayStateController.Callback.class); 203 204 stateController.setAvailableComplicationTypes(Complication.COMPLICATION_TYPE_WEATHER); 205 stateController.addCallback(callback); 206 mExecutor.runAllReady(); 207 208 { 209 clearInvocations(callback); 210 stateController.setShouldShowComplications(true); 211 mExecutor.runAllReady(); 212 213 verify(callback).onAvailableComplicationTypesChanged(); 214 final Collection<Complication> complications = stateController.getComplications(); 215 assertThat(complications.contains(alwaysAvailableComplication)).isTrue(); 216 assertThat(complications.contains(weatherComplication)).isTrue(); 217 } 218 219 { 220 clearInvocations(callback); 221 stateController.setShouldShowComplications(false); 222 mExecutor.runAllReady(); 223 224 verify(callback).onAvailableComplicationTypesChanged(); 225 final Collection<Complication> complications = stateController.getComplications(); 226 assertThat(complications.contains(alwaysAvailableComplication)).isTrue(); 227 assertThat(complications.contains(weatherComplication)).isFalse(); 228 } 229 } 230 231 @Test testComplicationWithNoTypeNotFiltered()232 public void testComplicationWithNoTypeNotFiltered() { 233 final Complication complication = Mockito.mock(Complication.class); 234 final DreamOverlayStateController stateController = getDreamOverlayStateController(true); 235 stateController.addComplication(complication); 236 mExecutor.runAllReady(); 237 assertThat(stateController.getComplications(true).contains(complication)) 238 .isTrue(); 239 } 240 241 @Test testComplicationsNotShownForLowLight()242 public void testComplicationsNotShownForLowLight() { 243 final Complication complication = Mockito.mock(Complication.class); 244 final DreamOverlayStateController stateController = getDreamOverlayStateController(true); 245 246 // Add a complication and verify it's returned in getComplications. 247 stateController.addComplication(complication); 248 mExecutor.runAllReady(); 249 assertThat(stateController.getComplications().contains(complication)) 250 .isTrue(); 251 252 stateController.setLowLightActive(true); 253 mExecutor.runAllReady(); 254 255 assertThat(stateController.getComplications()).isEmpty(); 256 } 257 258 @Test testNotifyLowLightChanged()259 public void testNotifyLowLightChanged() { 260 final DreamOverlayStateController stateController = getDreamOverlayStateController(true); 261 262 stateController.addCallback(mCallback); 263 mExecutor.runAllReady(); 264 assertThat(stateController.isLowLightActive()).isFalse(); 265 266 stateController.setLowLightActive(true); 267 268 mExecutor.runAllReady(); 269 verify(mCallback, times(1)).onStateChanged(); 270 assertThat(stateController.isLowLightActive()).isTrue(); 271 } 272 273 @Test testNotifyLowLightExit()274 public void testNotifyLowLightExit() { 275 final DreamOverlayStateController stateController = getDreamOverlayStateController(true); 276 277 stateController.addCallback(mCallback); 278 mExecutor.runAllReady(); 279 assertThat(stateController.isLowLightActive()).isFalse(); 280 281 // Turn low light on then off to trigger the exiting callback. 282 stateController.setLowLightActive(true); 283 stateController.setLowLightActive(false); 284 285 // Callback was only called once, when 286 mExecutor.runAllReady(); 287 verify(mCallback, times(1)).onExitLowLight(); 288 assertThat(stateController.isLowLightActive()).isFalse(); 289 290 // Set with false again, which should not cause the callback to trigger again. 291 stateController.setLowLightActive(false); 292 mExecutor.runAllReady(); 293 verify(mCallback, times(1)).onExitLowLight(); 294 } 295 296 @Test testNotifyEntryAnimationsFinishedChanged()297 public void testNotifyEntryAnimationsFinishedChanged() { 298 final DreamOverlayStateController stateController = getDreamOverlayStateController(true); 299 300 stateController.addCallback(mCallback); 301 mExecutor.runAllReady(); 302 assertThat(stateController.areEntryAnimationsFinished()).isFalse(); 303 304 stateController.setEntryAnimationsFinished(true); 305 mExecutor.runAllReady(); 306 307 verify(mCallback, times(1)).onStateChanged(); 308 assertThat(stateController.areEntryAnimationsFinished()).isTrue(); 309 } 310 311 @Test testNotifyDreamOverlayStatusBarVisibleChanged()312 public void testNotifyDreamOverlayStatusBarVisibleChanged() { 313 final DreamOverlayStateController stateController = getDreamOverlayStateController(true); 314 315 stateController.addCallback(mCallback); 316 mExecutor.runAllReady(); 317 assertThat(stateController.isDreamOverlayStatusBarVisible()).isFalse(); 318 319 stateController.setDreamOverlayStatusBarVisible(true); 320 mExecutor.runAllReady(); 321 322 verify(mCallback, times(1)).onStateChanged(); 323 assertThat(stateController.isDreamOverlayStatusBarVisible()).isTrue(); 324 } 325 326 @Test testNotifyHasAssistantAttentionChanged()327 public void testNotifyHasAssistantAttentionChanged() { 328 final DreamOverlayStateController stateController = getDreamOverlayStateController(true); 329 330 stateController.addCallback(mCallback); 331 mExecutor.runAllReady(); 332 assertThat(stateController.hasAssistantAttention()).isFalse(); 333 334 stateController.setHasAssistantAttention(true); 335 mExecutor.runAllReady(); 336 337 verify(mCallback, times(1)).onStateChanged(); 338 assertThat(stateController.hasAssistantAttention()).isTrue(); 339 } 340 341 @Test testShouldShowComplicationsSetToFalse_stillShowsHomeControls_featureEnabled()342 public void testShouldShowComplicationsSetToFalse_stillShowsHomeControls_featureEnabled() { 343 when(mFeatureFlags.isEnabled(Flags.ALWAYS_SHOW_HOME_CONTROLS_ON_DREAMS)).thenReturn(true); 344 345 final DreamOverlayStateController stateController = getDreamOverlayStateController(true); 346 stateController.setShouldShowComplications(true); 347 348 final Complication homeControlsComplication = Mockito.mock(Complication.class); 349 when(homeControlsComplication.getRequiredTypeAvailability()) 350 .thenReturn(Complication.COMPLICATION_TYPE_HOME_CONTROLS); 351 352 stateController.addComplication(homeControlsComplication); 353 354 final DreamOverlayStateController.Callback callback = 355 Mockito.mock(DreamOverlayStateController.Callback.class); 356 357 stateController.setAvailableComplicationTypes( 358 Complication.COMPLICATION_TYPE_HOME_CONTROLS); 359 stateController.addCallback(callback); 360 mExecutor.runAllReady(); 361 362 { 363 clearInvocations(callback); 364 stateController.setShouldShowComplications(true); 365 mExecutor.runAllReady(); 366 367 verify(callback).onAvailableComplicationTypesChanged(); 368 final Collection<Complication> complications = stateController.getComplications(); 369 assertThat(complications.contains(homeControlsComplication)).isTrue(); 370 } 371 372 { 373 clearInvocations(callback); 374 stateController.setShouldShowComplications(false); 375 mExecutor.runAllReady(); 376 377 verify(callback).onAvailableComplicationTypesChanged(); 378 final Collection<Complication> complications = stateController.getComplications(); 379 assertThat(complications.contains(homeControlsComplication)).isTrue(); 380 } 381 } 382 383 @Test testHomeControlsDoNotShowIfNotAvailable_featureEnabled()384 public void testHomeControlsDoNotShowIfNotAvailable_featureEnabled() { 385 when(mFeatureFlags.isEnabled(Flags.ALWAYS_SHOW_HOME_CONTROLS_ON_DREAMS)).thenReturn(true); 386 387 final DreamOverlayStateController stateController = getDreamOverlayStateController(true); 388 stateController.setShouldShowComplications(true); 389 390 final Complication homeControlsComplication = Mockito.mock(Complication.class); 391 when(homeControlsComplication.getRequiredTypeAvailability()) 392 .thenReturn(Complication.COMPLICATION_TYPE_HOME_CONTROLS); 393 394 stateController.addComplication(homeControlsComplication); 395 396 final DreamOverlayStateController.Callback callback = 397 Mockito.mock(DreamOverlayStateController.Callback.class); 398 399 stateController.addCallback(callback); 400 mExecutor.runAllReady(); 401 402 // No home controls since it is not available. 403 assertThat(stateController.getComplications()).doesNotContain(homeControlsComplication); 404 405 stateController.setAvailableComplicationTypes(Complication.COMPLICATION_TYPE_HOME_CONTROLS 406 | Complication.COMPLICATION_TYPE_WEATHER); 407 mExecutor.runAllReady(); 408 assertThat(stateController.getComplications()).contains(homeControlsComplication); 409 } 410 getDreamOverlayStateController(boolean overlayEnabled)411 private DreamOverlayStateController getDreamOverlayStateController(boolean overlayEnabled) { 412 return new DreamOverlayStateController( 413 mExecutor, 414 overlayEnabled, 415 mFeatureFlags, 416 mLogBuffer 417 ); 418 } 419 } 420