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 android.app.StatusBarManager.WINDOW_STATE_HIDDEN; 20 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; 21 22 import static com.google.common.truth.Truth.assertThat; 23 24 import static org.mockito.ArgumentMatchers.any; 25 import static org.mockito.ArgumentMatchers.anyInt; 26 import static org.mockito.ArgumentMatchers.eq; 27 import static org.mockito.ArgumentMatchers.isNull; 28 import static org.mockito.Mockito.doCallRealMethod; 29 import static org.mockito.Mockito.never; 30 import static org.mockito.Mockito.reset; 31 import static org.mockito.Mockito.verify; 32 import static org.mockito.Mockito.when; 33 34 import android.app.ActivityManager; 35 import android.app.AlarmManager; 36 import android.content.Context; 37 import android.content.res.Resources; 38 import android.hardware.SensorPrivacyManager; 39 import android.net.ConnectivityManager; 40 import android.net.Network; 41 import android.net.NetworkCapabilities; 42 import android.net.NetworkRequest; 43 import android.provider.Settings; 44 import android.testing.AndroidTestingRunner; 45 import android.view.View; 46 47 import androidx.test.filters.SmallTest; 48 49 import com.android.systemui.R; 50 import com.android.systemui.SysuiTestCase; 51 import com.android.systemui.log.LogBuffer; 52 import com.android.systemui.log.core.FakeLogBuffer; 53 import com.android.systemui.settings.UserTracker; 54 import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; 55 import com.android.systemui.statusbar.policy.NextAlarmController; 56 import com.android.systemui.statusbar.policy.ZenModeController; 57 import com.android.systemui.statusbar.window.StatusBarWindowStateController; 58 import com.android.systemui.statusbar.window.StatusBarWindowStateListener; 59 import com.android.systemui.touch.TouchInsetManager; 60 import com.android.systemui.util.time.DateFormatUtil; 61 62 import org.junit.Before; 63 import org.junit.Test; 64 import org.junit.runner.RunWith; 65 import org.mockito.ArgumentCaptor; 66 import org.mockito.Captor; 67 import org.mockito.Mock; 68 import org.mockito.MockitoAnnotations; 69 70 import java.util.List; 71 import java.util.Optional; 72 import java.util.concurrent.Executor; 73 74 @SmallTest 75 @RunWith(AndroidTestingRunner.class) 76 public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { 77 private static final String NOTIFICATION_INDICATOR_FORMATTER_STRING = 78 "{count, plural, =1 {# notification} other {# notifications}}"; 79 80 @Mock 81 MockDreamOverlayStatusBarView mView; 82 @Mock 83 ConnectivityManager mConnectivityManager; 84 @Mock 85 NetworkCapabilities mNetworkCapabilities; 86 @Mock 87 Network mNetwork; 88 @Mock 89 TouchInsetManager.TouchInsetSession mTouchSession; 90 @Mock 91 Resources mResources; 92 @Mock 93 AlarmManager mAlarmManager; 94 @Mock 95 AlarmManager.AlarmClockInfo mAlarmClockInfo; 96 @Mock 97 NextAlarmController mNextAlarmController; 98 @Mock 99 DateFormatUtil mDateFormatUtil; 100 @Mock 101 IndividualSensorPrivacyController mSensorPrivacyController; 102 @Mock 103 ZenModeController mZenModeController; 104 @Mock 105 DreamOverlayNotificationCountProvider mDreamOverlayNotificationCountProvider; 106 @Mock 107 StatusBarWindowStateController mStatusBarWindowStateController; 108 @Mock 109 DreamOverlayStatusBarItemsProvider mDreamOverlayStatusBarItemsProvider; 110 @Mock 111 DreamOverlayStatusBarItemsProvider.StatusBarItem mStatusBarItem; 112 @Mock 113 View mStatusBarItemView; 114 @Mock 115 DreamOverlayStateController mDreamOverlayStateController; 116 @Mock 117 UserTracker mUserTracker; 118 119 LogBuffer mLogBuffer = FakeLogBuffer.Factory.Companion.create(); 120 121 @Captor 122 private ArgumentCaptor<DreamOverlayStateController.Callback> mCallbackCaptor; 123 124 private final Executor mMainExecutor = Runnable::run; 125 126 DreamOverlayStatusBarViewController mController; 127 128 @Before setup()129 public void setup() { 130 MockitoAnnotations.initMocks(this); 131 132 when(mResources.getString(R.string.dream_overlay_status_bar_notification_indicator)) 133 .thenReturn(NOTIFICATION_INDICATOR_FORMATTER_STRING); 134 doCallRealMethod().when(mView).setVisibility(anyInt()); 135 doCallRealMethod().when(mView).getVisibility(); 136 when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser()); 137 138 mController = new DreamOverlayStatusBarViewController( 139 mView, 140 mResources, 141 mMainExecutor, 142 mConnectivityManager, 143 mTouchSession, 144 mAlarmManager, 145 mNextAlarmController, 146 mDateFormatUtil, 147 mSensorPrivacyController, 148 Optional.of(mDreamOverlayNotificationCountProvider), 149 mZenModeController, 150 mStatusBarWindowStateController, 151 mDreamOverlayStatusBarItemsProvider, 152 mDreamOverlayStateController, 153 mUserTracker, 154 mLogBuffer); 155 } 156 157 @Test testOnViewAttachedAddsCallbacks()158 public void testOnViewAttachedAddsCallbacks() { 159 mController.onViewAttached(); 160 verify(mNextAlarmController).addCallback(any()); 161 verify(mSensorPrivacyController).addCallback(any()); 162 verify(mZenModeController).addCallback(any()); 163 verify(mDreamOverlayNotificationCountProvider).addCallback(any()); 164 verify(mDreamOverlayStatusBarItemsProvider).addCallback(any()); 165 verify(mDreamOverlayStateController).addCallback(any()); 166 } 167 168 @Test testOnViewAttachedRegistersNetworkCallback()169 public void testOnViewAttachedRegistersNetworkCallback() { 170 mController.onViewAttached(); 171 verify(mConnectivityManager) 172 .registerNetworkCallback(any(NetworkRequest.class), any( 173 ConnectivityManager.NetworkCallback.class)); 174 } 175 176 @Test testOnViewAttachedShowsWifiIconWhenWifiUnavailable()177 public void testOnViewAttachedShowsWifiIconWhenWifiUnavailable() { 178 when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) 179 .thenReturn(false); 180 when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities); 181 mController.onViewAttached(); 182 verify(mView).showIcon( 183 DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true, null); 184 } 185 186 @Test testOnViewAttachedHidesWifiIconWhenWifiAvailable()187 public void testOnViewAttachedHidesWifiIconWhenWifiAvailable() { 188 when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) 189 .thenReturn(true); 190 when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities); 191 mController.onViewAttached(); 192 verify(mView).showIcon( 193 DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false, null); 194 } 195 196 @Test testOnViewAttachedShowsWifiIconWhenNetworkCapabilitiesUnavailable()197 public void testOnViewAttachedShowsWifiIconWhenNetworkCapabilitiesUnavailable() { 198 when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(null); 199 mController.onViewAttached(); 200 verify(mView).showIcon( 201 DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true, null); 202 } 203 204 @Test testOnViewAttachedShowsAlarmIconWhenAlarmExists()205 public void testOnViewAttachedShowsAlarmIconWhenAlarmExists() { 206 when(mAlarmClockInfo.getTriggerTime()).thenReturn(1L); 207 when(mAlarmManager.getNextAlarmClock(anyInt())).thenReturn(mAlarmClockInfo); 208 mController.onViewAttached(); 209 verify(mView).showIcon( 210 eq(DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET), eq(true), any()); 211 } 212 213 @Test testOnViewAttachedHidesAlarmIconWhenNoAlarmExists()214 public void testOnViewAttachedHidesAlarmIconWhenNoAlarmExists() { 215 when(mAlarmManager.getNextAlarmClock(anyInt())).thenReturn(null); 216 mController.onViewAttached(); 217 verify(mView).showIcon( 218 eq(DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET), eq(false), isNull()); 219 } 220 221 @Test testOnViewAttachedShowsMicIconWhenDisabled()222 public void testOnViewAttachedShowsMicIconWhenDisabled() { 223 when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE)) 224 .thenReturn(true); 225 when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA)) 226 .thenReturn(false); 227 mController.onViewAttached(); 228 verify(mView).showIcon( 229 DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED, true, null); 230 } 231 232 @Test testOnViewAttachedShowsCameraIconWhenDisabled()233 public void testOnViewAttachedShowsCameraIconWhenDisabled() { 234 when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE)) 235 .thenReturn(false); 236 when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA)) 237 .thenReturn(true); 238 mController.onViewAttached(); 239 verify(mView).showIcon( 240 DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED, true, null); 241 } 242 243 @Test testOnViewAttachedShowsMicCameraIconWhenDisabled()244 public void testOnViewAttachedShowsMicCameraIconWhenDisabled() { 245 when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE)) 246 .thenReturn(true); 247 when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA)) 248 .thenReturn(true); 249 mController.onViewAttached(); 250 verify(mView).showIcon( 251 DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null); 252 } 253 254 @Test testOnViewAttachedShowsNotificationsIconWhenNotificationsExist()255 public void testOnViewAttachedShowsNotificationsIconWhenNotificationsExist() { 256 mController.onViewAttached(); 257 258 final ArgumentCaptor<DreamOverlayNotificationCountProvider.Callback> callbackCapture = 259 ArgumentCaptor.forClass(DreamOverlayNotificationCountProvider.Callback.class); 260 verify(mDreamOverlayNotificationCountProvider).addCallback(callbackCapture.capture()); 261 callbackCapture.getValue().onNotificationCountChanged(1); 262 263 verify(mView).showIcon( 264 eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any()); 265 } 266 267 @Test testOnViewAttachedHidesNotificationsIconWhenNoNotificationsExist()268 public void testOnViewAttachedHidesNotificationsIconWhenNoNotificationsExist() { 269 mController.onViewAttached(); 270 271 final ArgumentCaptor<DreamOverlayNotificationCountProvider.Callback> callbackCapture = 272 ArgumentCaptor.forClass(DreamOverlayNotificationCountProvider.Callback.class); 273 verify(mDreamOverlayNotificationCountProvider).addCallback(callbackCapture.capture()); 274 callbackCapture.getValue().onNotificationCountChanged(0); 275 276 verify(mView).showIcon( 277 eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(false), isNull()); 278 } 279 280 @Test testNotificationsIconNotShownWhenCountProviderAbsent()281 public void testNotificationsIconNotShownWhenCountProviderAbsent() { 282 DreamOverlayStatusBarViewController controller = new DreamOverlayStatusBarViewController( 283 mView, 284 mResources, 285 mMainExecutor, 286 mConnectivityManager, 287 mTouchSession, 288 mAlarmManager, 289 mNextAlarmController, 290 mDateFormatUtil, 291 mSensorPrivacyController, 292 Optional.empty(), 293 mZenModeController, 294 mStatusBarWindowStateController, 295 mDreamOverlayStatusBarItemsProvider, 296 mDreamOverlayStateController, 297 mUserTracker, 298 mLogBuffer); 299 controller.onViewAttached(); 300 verify(mView, never()).showIcon( 301 eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any()); 302 } 303 304 @Test testOnViewAttachedShowsPriorityModeIconWhenEnabled()305 public void testOnViewAttachedShowsPriorityModeIconWhenEnabled() { 306 when(mZenModeController.getZen()).thenReturn( 307 Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS); 308 mController.onViewAttached(); 309 verify(mView).showIcon( 310 DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true, null); 311 } 312 313 @Test testOnViewAttachedHidesPriorityModeIconWhenDisabled()314 public void testOnViewAttachedHidesPriorityModeIconWhenDisabled() { 315 when(mZenModeController.getZen()).thenReturn( 316 Settings.Global.ZEN_MODE_OFF); 317 mController.onViewAttached(); 318 verify(mView).showIcon( 319 DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false, null); 320 } 321 322 @Test testOnViewDetachedUnregistersNetworkCallback()323 public void testOnViewDetachedUnregistersNetworkCallback() { 324 mController.onViewDetached(); 325 verify(mConnectivityManager) 326 .unregisterNetworkCallback(any(ConnectivityManager.NetworkCallback.class)); 327 } 328 329 @Test testOnViewDetachedRemovesCallbacks()330 public void testOnViewDetachedRemovesCallbacks() { 331 mController.onViewDetached(); 332 verify(mNextAlarmController).removeCallback(any()); 333 verify(mSensorPrivacyController).removeCallback(any()); 334 verify(mZenModeController).removeCallback(any()); 335 verify(mDreamOverlayNotificationCountProvider).removeCallback(any()); 336 verify(mDreamOverlayStatusBarItemsProvider).removeCallback(any()); 337 verify(mDreamOverlayStateController).removeCallback(any()); 338 } 339 340 @Test testOnViewDetachedRemovesViews()341 public void testOnViewDetachedRemovesViews() { 342 mController.onViewDetached(); 343 verify(mView).removeAllExtraStatusBarItemViews(); 344 } 345 346 @Test testWifiIconHiddenWhenWifiBecomesAvailable()347 public void testWifiIconHiddenWhenWifiBecomesAvailable() { 348 // Make sure wifi starts out unavailable when onViewAttached is called, and then returns 349 // true on the second query. 350 when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) 351 .thenReturn(false).thenReturn(true); 352 when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities); 353 mController.onViewAttached(); 354 355 final ArgumentCaptor<ConnectivityManager.NetworkCallback> callbackCapture = 356 ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class); 357 verify(mConnectivityManager).registerNetworkCallback(any(), callbackCapture.capture()); 358 callbackCapture.getValue().onAvailable(mNetwork); 359 360 verify(mView).showIcon( 361 DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false, null); 362 } 363 364 @Test testWifiIconShownWhenWifiBecomesUnavailable()365 public void testWifiIconShownWhenWifiBecomesUnavailable() { 366 // Make sure wifi starts out available when onViewAttached is called, then returns false 367 // on the second query. 368 when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) 369 .thenReturn(true).thenReturn(false); 370 when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities); 371 mController.onViewAttached(); 372 373 final ArgumentCaptor<ConnectivityManager.NetworkCallback> callbackCapture = 374 ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class); 375 verify(mConnectivityManager).registerNetworkCallback(any(), callbackCapture.capture()); 376 callbackCapture.getValue().onLost(mNetwork); 377 378 verify(mView).showIcon( 379 DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true, null); 380 } 381 382 @Test testWifiIconHiddenWhenCapabilitiesChange()383 public void testWifiIconHiddenWhenCapabilitiesChange() { 384 // Make sure wifi starts out unavailable when onViewAttached is called. 385 when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) 386 .thenReturn(false); 387 when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities); 388 mController.onViewAttached(); 389 390 final ArgumentCaptor<ConnectivityManager.NetworkCallback> callbackCapture = 391 ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class); 392 verify(mConnectivityManager).registerNetworkCallback(any(), callbackCapture.capture()); 393 when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) 394 .thenReturn(true); 395 callbackCapture.getValue().onCapabilitiesChanged(mNetwork, mNetworkCapabilities); 396 397 verify(mView).showIcon( 398 DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false, null); 399 } 400 401 @Test testNotificationsIconShownWhenNotificationAdded()402 public void testNotificationsIconShownWhenNotificationAdded() { 403 mController.onViewAttached(); 404 405 final ArgumentCaptor<DreamOverlayNotificationCountProvider.Callback> callbackCapture = 406 ArgumentCaptor.forClass(DreamOverlayNotificationCountProvider.Callback.class); 407 verify(mDreamOverlayNotificationCountProvider).addCallback(callbackCapture.capture()); 408 callbackCapture.getValue().onNotificationCountChanged(1); 409 410 verify(mView).showIcon( 411 eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any()); 412 } 413 414 @Test testNotificationsIconHiddenWhenLastNotificationRemoved()415 public void testNotificationsIconHiddenWhenLastNotificationRemoved() { 416 mController.onViewAttached(); 417 418 final ArgumentCaptor<DreamOverlayNotificationCountProvider.Callback> callbackCapture = 419 ArgumentCaptor.forClass(DreamOverlayNotificationCountProvider.Callback.class); 420 verify(mDreamOverlayNotificationCountProvider).addCallback(callbackCapture.capture()); 421 callbackCapture.getValue().onNotificationCountChanged(0); 422 423 verify(mView).showIcon( 424 eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(false), any()); 425 } 426 427 @Test testMicCameraIconShownWhenSensorsBlocked()428 public void testMicCameraIconShownWhenSensorsBlocked() { 429 mController.onViewAttached(); 430 431 when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE)) 432 .thenReturn(true); 433 when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA)) 434 .thenReturn(true); 435 436 final ArgumentCaptor<IndividualSensorPrivacyController.Callback> callbackCapture = 437 ArgumentCaptor.forClass(IndividualSensorPrivacyController.Callback.class); 438 verify(mSensorPrivacyController).addCallback(callbackCapture.capture()); 439 callbackCapture.getValue().onSensorBlockedChanged( 440 SensorPrivacyManager.Sensors.MICROPHONE, true); 441 442 verify(mView).showIcon( 443 DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null); 444 } 445 446 @Test testPriorityModeIconShownWhenZenModeEnabled()447 public void testPriorityModeIconShownWhenZenModeEnabled() { 448 mController.onViewAttached(); 449 450 when(mZenModeController.getZen()).thenReturn(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS); 451 452 final ArgumentCaptor<ZenModeController.Callback> callbackCapture = 453 ArgumentCaptor.forClass(ZenModeController.Callback.class); 454 verify(mZenModeController).addCallback(callbackCapture.capture()); 455 callbackCapture.getValue().onZenChanged(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS); 456 457 verify(mView).showIcon( 458 DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true, null); 459 } 460 461 @Test testPriorityModeIconHiddenWhenZenModeDisabled()462 public void testPriorityModeIconHiddenWhenZenModeDisabled() { 463 when(mZenModeController.getZen()).thenReturn(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS) 464 .thenReturn(Settings.Global.ZEN_MODE_OFF); 465 mController.onViewAttached(); 466 467 final ArgumentCaptor<ZenModeController.Callback> callbackCapture = 468 ArgumentCaptor.forClass(ZenModeController.Callback.class); 469 verify(mZenModeController).addCallback(callbackCapture.capture()); 470 callbackCapture.getValue().onZenChanged(Settings.Global.ZEN_MODE_OFF); 471 472 verify(mView).showIcon( 473 DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false, null); 474 } 475 476 @Test testAssistantAttentionIconShownWhenAttentionGained()477 public void testAssistantAttentionIconShownWhenAttentionGained() { 478 mController.onViewAttached(); 479 480 when(mDreamOverlayStateController.hasAssistantAttention()).thenReturn(true); 481 482 final ArgumentCaptor<DreamOverlayStateController.Callback> callbackCapture = 483 ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class); 484 verify(mDreamOverlayStateController).addCallback(callbackCapture.capture()); 485 callbackCapture.getValue().onStateChanged(); 486 487 verify(mView).showIcon( 488 DreamOverlayStatusBarView.STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE, true, null); 489 } 490 491 @Test testStatusBarHiddenWhenSystemStatusBarShown()492 public void testStatusBarHiddenWhenSystemStatusBarShown() { 493 mController.onViewAttached(); 494 495 updateEntryAnimationsFinished(); 496 updateStatusBarWindowState(true); 497 498 assertThat(mView.getVisibility()).isEqualTo(View.INVISIBLE); 499 } 500 501 @Test testStatusBarShownWhenSystemStatusBarHidden()502 public void testStatusBarShownWhenSystemStatusBarHidden() { 503 mController.onViewAttached(); 504 reset(mView); 505 506 updateEntryAnimationsFinished(); 507 updateStatusBarWindowState(false); 508 509 assertThat(mView.getVisibility()).isEqualTo(View.VISIBLE); 510 } 511 512 @Test testUnattachedStatusBarVisibilityUnchangedWhenSystemStatusBarHidden()513 public void testUnattachedStatusBarVisibilityUnchangedWhenSystemStatusBarHidden() { 514 mController.onViewAttached(); 515 updateEntryAnimationsFinished(); 516 mController.onViewDetached(); 517 reset(mView); 518 519 updateStatusBarWindowState(true); 520 521 verify(mView, never()).setVisibility(anyInt()); 522 } 523 524 @Test testNoChangeToVisibilityBeforeDreamStartedWhenStatusBarHidden()525 public void testNoChangeToVisibilityBeforeDreamStartedWhenStatusBarHidden() { 526 mController.onViewAttached(); 527 528 // Trigger status bar window state change. 529 final StatusBarWindowStateListener listener = updateStatusBarWindowState(false); 530 531 // Verify no visibility change because dream not started. 532 verify(mView, never()).setVisibility(anyInt()); 533 534 // Dream entry animations finished. 535 updateEntryAnimationsFinished(); 536 537 // Trigger another status bar window state change, and verify visibility change. 538 listener.onStatusBarWindowStateChanged(WINDOW_STATE_HIDDEN); 539 assertThat(mView.getVisibility()).isEqualTo(View.VISIBLE); 540 } 541 542 @Test testExtraStatusBarItemSetWhenItemsChange()543 public void testExtraStatusBarItemSetWhenItemsChange() { 544 mController.onViewAttached(); 545 when(mStatusBarItem.getView()).thenReturn(mStatusBarItemView); 546 547 final ArgumentCaptor<DreamOverlayStatusBarItemsProvider.Callback> 548 callbackCapture = ArgumentCaptor.forClass( 549 DreamOverlayStatusBarItemsProvider.Callback.class); 550 verify(mDreamOverlayStatusBarItemsProvider).addCallback(callbackCapture.capture()); 551 callbackCapture.getValue().onStatusBarItemsChanged(List.of(mStatusBarItem)); 552 553 verify(mView).setExtraStatusBarItemViews(List.of(mStatusBarItemView)); 554 } 555 556 @Test testLowLightHidesStatusBar()557 public void testLowLightHidesStatusBar() { 558 when(mDreamOverlayStateController.isLowLightActive()).thenReturn(true); 559 mController.onViewAttached(); 560 updateEntryAnimationsFinished(); 561 562 final ArgumentCaptor<DreamOverlayStateController.Callback> callbackCapture = 563 ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class); 564 verify(mDreamOverlayStateController).addCallback(callbackCapture.capture()); 565 callbackCapture.getValue().onStateChanged(); 566 567 assertThat(mView.getVisibility()).isEqualTo(View.INVISIBLE); 568 reset(mView); 569 570 when(mDreamOverlayStateController.isLowLightActive()).thenReturn(false); 571 callbackCapture.getValue().onStateChanged(); 572 573 assertThat(mView.getVisibility()).isEqualTo(View.VISIBLE); 574 } 575 576 @Test testNoChangeToVisibilityBeforeDreamStartedWhenLowLightStateChange()577 public void testNoChangeToVisibilityBeforeDreamStartedWhenLowLightStateChange() { 578 when(mDreamOverlayStateController.isLowLightActive()).thenReturn(false); 579 mController.onViewAttached(); 580 581 // No change to visibility because dream not fully started. 582 verify(mView, never()).setVisibility(anyInt()); 583 584 // Dream entry animations finished. 585 updateEntryAnimationsFinished(); 586 587 // Trigger state change and verify visibility changed. 588 final ArgumentCaptor<DreamOverlayStateController.Callback> callbackCapture = 589 ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class); 590 verify(mDreamOverlayStateController).addCallback(callbackCapture.capture()); 591 callbackCapture.getValue().onStateChanged(); 592 593 assertThat(mView.getVisibility()).isEqualTo(View.VISIBLE); 594 } 595 596 @Test testDreamOverlayStatusBarVisibleSetToFalseOnDetach()597 public void testDreamOverlayStatusBarVisibleSetToFalseOnDetach() { 598 mController.onViewAttached(); 599 mController.onViewDetached(); 600 verify(mDreamOverlayStateController).setDreamOverlayStatusBarVisible(false); 601 } 602 updateStatusBarWindowState(boolean show)603 private StatusBarWindowStateListener updateStatusBarWindowState(boolean show) { 604 when(mStatusBarWindowStateController.windowIsShowing()).thenReturn(show); 605 final ArgumentCaptor<StatusBarWindowStateListener> 606 callbackCapture = ArgumentCaptor.forClass(StatusBarWindowStateListener.class); 607 verify(mStatusBarWindowStateController).addListener(callbackCapture.capture()); 608 final StatusBarWindowStateListener listener = callbackCapture.getValue(); 609 listener.onStatusBarWindowStateChanged(show ? WINDOW_STATE_SHOWING : WINDOW_STATE_HIDDEN); 610 return listener; 611 } 612 updateEntryAnimationsFinished()613 private void updateEntryAnimationsFinished() { 614 when(mDreamOverlayStateController.areEntryAnimationsFinished()).thenReturn(true); 615 616 verify(mDreamOverlayStateController).addCallback(mCallbackCaptor.capture()); 617 final DreamOverlayStateController.Callback callback = mCallbackCaptor.getValue(); 618 callback.onStateChanged(); 619 } 620 621 private static class MockDreamOverlayStatusBarView extends DreamOverlayStatusBarView { 622 private int mVisibility = View.VISIBLE; 623 MockDreamOverlayStatusBarView(Context context)624 private MockDreamOverlayStatusBarView(Context context) { 625 super(context); 626 } 627 628 @Override setVisibility(int visibility)629 public void setVisibility(int visibility) { 630 mVisibility = visibility; 631 } 632 633 @Override getVisibility()634 public int getVisibility() { 635 return mVisibility; 636 } 637 } 638 } 639