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