1 /*
2  * Copyright (C) 2020 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.keyguard;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertNull;
21 import static org.mockito.ArgumentMatchers.any;
22 import static org.mockito.ArgumentMatchers.anyBoolean;
23 import static org.mockito.ArgumentMatchers.eq;
24 import static org.mockito.Mockito.never;
25 import static org.mockito.Mockito.reset;
26 import static org.mockito.Mockito.times;
27 import static org.mockito.Mockito.verify;
28 import static org.mockito.Mockito.when;
29 
30 import android.database.ContentObserver;
31 import android.os.UserHandle;
32 import android.provider.Settings;
33 import android.testing.AndroidTestingRunner;
34 import android.view.View;
35 
36 import androidx.test.filters.SmallTest;
37 
38 import com.android.systemui.Flags;
39 import com.android.systemui.plugins.clocks.ClockFaceConfig;
40 import com.android.systemui.plugins.clocks.ClockTickRate;
41 import com.android.systemui.shared.clocks.ClockRegistry;
42 import com.android.systemui.statusbar.StatusBarState;
43 
44 import org.junit.Test;
45 import org.junit.runner.RunWith;
46 import org.mockito.ArgumentCaptor;
47 import org.mockito.verification.VerificationMode;
48 
49 @SmallTest
50 @RunWith(AndroidTestingRunner.class)
51 public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchControllerBaseTest {
52     @Test
testInit_viewAlreadyAttached()53     public void testInit_viewAlreadyAttached() {
54         mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
55 
56         mController.init();
57 
58         verifyAttachment(times(1));
59     }
60 
61     @Test
testInit_viewNotYetAttached()62     public void testInit_viewNotYetAttached() {
63         mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
64 
65         ArgumentCaptor<View.OnAttachStateChangeListener> listenerArgumentCaptor =
66                 ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
67 
68         when(mView.isAttachedToWindow()).thenReturn(false);
69         mController.init();
70         verify(mView).addOnAttachStateChangeListener(listenerArgumentCaptor.capture());
71 
72         verifyAttachment(never());
73 
74         listenerArgumentCaptor.getValue().onViewAttachedToWindow(mView);
75 
76         verifyAttachment(times(1));
77     }
78 
79     @Test
testInitSubControllers()80     public void testInitSubControllers() {
81         mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
82 
83         mController.init();
84         verify(mKeyguardSliceViewController).init();
85     }
86 
87     @Test
testInit_viewDetached()88     public void testInit_viewDetached() {
89         mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
90 
91         ArgumentCaptor<View.OnAttachStateChangeListener> listenerArgumentCaptor =
92                 ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
93         mController.init();
94         verify(mView).addOnAttachStateChangeListener(listenerArgumentCaptor.capture());
95 
96         verifyAttachment(times(1));
97 
98         listenerArgumentCaptor.getValue().onViewDetachedFromWindow(mView);
99         verify(mClockEventController).unregisterListeners();
100     }
101 
102     @Test
testPluginPassesStatusBarState()103     public void testPluginPassesStatusBarState() {
104         mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
105 
106         ArgumentCaptor<ClockRegistry.ClockChangeListener> listenerArgumentCaptor =
107                 ArgumentCaptor.forClass(ClockRegistry.ClockChangeListener.class);
108 
109         mController.init();
110         verify(mClockRegistry).registerClockChangeListener(listenerArgumentCaptor.capture());
111 
112         listenerArgumentCaptor.getValue().onCurrentClockChanged();
113         verify(mView, times(2)).setClock(mClockController, StatusBarState.SHADE);
114         verify(mClockEventController, times(2)).setClock(mClockController);
115     }
116 
117     @Test
testSmartspaceEnabledRemovesKeyguardStatusArea()118     public void testSmartspaceEnabledRemovesKeyguardStatusArea() {
119         mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
120 
121         when(mSmartspaceController.isEnabled()).thenReturn(true);
122         mController.init();
123 
124         assertEquals(View.GONE, mSliceView.getVisibility());
125     }
126 
127     @Test
onLocaleListChangedRebuildsSmartspaceView()128     public void onLocaleListChangedRebuildsSmartspaceView() {
129         mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
130 
131         when(mSmartspaceController.isEnabled()).thenReturn(true);
132         mController.init();
133 
134         mController.onLocaleListChanged();
135         // Should be called once on initial setup, then once again for locale change
136         verify(mSmartspaceController, times(2)).buildAndConnectView(mView);
137     }
138 
139     @Test
onLocaleListChanged_rebuildsSmartspaceViews_whenDecouplingEnabled()140     public void onLocaleListChanged_rebuildsSmartspaceViews_whenDecouplingEnabled() {
141         mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
142 
143         when(mSmartspaceController.isEnabled()).thenReturn(true);
144         when(mSmartspaceController.isDateWeatherDecoupled()).thenReturn(true);
145         mController.init();
146 
147         mController.onLocaleListChanged();
148         // Should be called once on initial setup, then once again for locale change
149         verify(mSmartspaceController, times(2)).buildAndConnectDateView(mView);
150         verify(mSmartspaceController, times(2)).buildAndConnectWeatherView(mView);
151         verify(mSmartspaceController, times(2)).buildAndConnectView(mView);
152     }
153 
154     @Test
testSmartspaceDisabledShowsKeyguardStatusArea()155     public void testSmartspaceDisabledShowsKeyguardStatusArea() {
156         mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
157 
158         when(mSmartspaceController.isEnabled()).thenReturn(false);
159         mController.init();
160 
161         assertEquals(View.VISIBLE, mSliceView.getVisibility());
162     }
163 
164     @Test
testRefresh()165     public void testRefresh() {
166         mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
167 
168         mController.refresh();
169 
170         verify(mSmartspaceController).requestSmartspaceUpdate();
171     }
172 
173     @Test
testChangeToDoubleLineClockSetsSmallClock()174     public void testChangeToDoubleLineClockSetsSmallClock() {
175         mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
176 
177         when(mSecureSettings.getIntForUser(Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK, 1,
178                 UserHandle.USER_CURRENT))
179                 .thenReturn(0);
180         ArgumentCaptor<ContentObserver> observerCaptor =
181                 ArgumentCaptor.forClass(ContentObserver.class);
182         mController.init();
183         mExecutor.runAllReady();
184         verify(mSecureSettings).registerContentObserverForUserSync(
185                 eq(Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK),
186                     anyBoolean(), observerCaptor.capture(), eq(UserHandle.USER_ALL));
187         ContentObserver observer = observerCaptor.getValue();
188         mExecutor.runAllReady();
189 
190         // When a settings change has occurred to the small clock, make sure the view is adjusted
191         reset(mView);
192         when(mView.getResources()).thenReturn(mResources);
193         observer.onChange(true);
194         mExecutor.runAllReady();
195         verify(mView).switchToClock(KeyguardClockSwitch.SMALL, /* animate */ true);
196     }
197 
198     @Test
testGetClock_ForwardsToClock()199     public void testGetClock_ForwardsToClock() {
200         mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
201 
202         assertEquals(mClockController, mController.getClock());
203     }
204 
205     @Test
testGetLargeClockBottom_returnsExpectedValue()206     public void testGetLargeClockBottom_returnsExpectedValue() {
207         mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
208 
209         when(mLargeClockFrame.getVisibility()).thenReturn(View.VISIBLE);
210         when(mLargeClockFrame.getHeight()).thenReturn(100);
211         when(mSmallClockFrame.getHeight()).thenReturn(50);
212         when(mLargeClockView.getHeight()).thenReturn(40);
213         when(mSmallClockView.getHeight()).thenReturn(20);
214         mController.init();
215 
216         assertEquals(170, mController.getClockBottom(1000));
217     }
218 
219     @Test
testGetSmallLargeClockBottom_returnsExpectedValue()220     public void testGetSmallLargeClockBottom_returnsExpectedValue() {
221         mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
222 
223         when(mLargeClockFrame.getVisibility()).thenReturn(View.GONE);
224         when(mLargeClockFrame.getHeight()).thenReturn(100);
225         when(mSmallClockFrame.getHeight()).thenReturn(50);
226         when(mLargeClockView.getHeight()).thenReturn(40);
227         when(mSmallClockView.getHeight()).thenReturn(20);
228         mController.init();
229 
230         assertEquals(1120, mController.getClockBottom(1000));
231     }
232 
233     @Test
testGetClockBottom_nullClock_returnsZero()234     public void testGetClockBottom_nullClock_returnsZero() {
235         mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
236 
237         when(mClockEventController.getClock()).thenReturn(null);
238         assertEquals(0, mController.getClockBottom(10));
239     }
240 
241     @Test
testChangeLockscreenWeatherEnabledSetsWeatherViewVisible()242     public void testChangeLockscreenWeatherEnabledSetsWeatherViewVisible() {
243         mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
244 
245         when(mSmartspaceController.isWeatherEnabled()).thenReturn(true);
246         ArgumentCaptor<ContentObserver> observerCaptor =
247                 ArgumentCaptor.forClass(ContentObserver.class);
248         mController.init();
249         mExecutor.runAllReady();
250         verify(mSecureSettings).registerContentObserverForUserSync(
251                 eq(Settings.Secure.LOCK_SCREEN_WEATHER_ENABLED), anyBoolean(),
252                     observerCaptor.capture(), eq(UserHandle.USER_ALL));
253         ContentObserver observer = observerCaptor.getValue();
254         mExecutor.runAllReady();
255         // When a settings change has occurred, check that view is visible.
256         observer.onChange(true);
257         mExecutor.runAllReady();
258         assertEquals(View.VISIBLE, mFakeWeatherView.getVisibility());
259     }
260 
261     @Test
testChangeClockDateWeatherEnabled_SetsDateWeatherViewVisibility()262     public void testChangeClockDateWeatherEnabled_SetsDateWeatherViewVisibility() {
263         mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
264 
265         ArgumentCaptor<ClockRegistry.ClockChangeListener> listenerArgumentCaptor =
266                 ArgumentCaptor.forClass(ClockRegistry.ClockChangeListener.class);
267         when(mSmartspaceController.isEnabled()).thenReturn(true);
268         when(mSmartspaceController.isDateWeatherDecoupled()).thenReturn(true);
269         when(mSmartspaceController.isWeatherEnabled()).thenReturn(true);
270         mController.init();
271         mExecutor.runAllReady();
272         assertEquals(View.VISIBLE, mFakeDateView.getVisibility());
273 
274         when(mSmallClockController.getConfig())
275                 .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true, false, true));
276         when(mLargeClockController.getConfig())
277                 .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true, false, true));
278         verify(mClockRegistry).registerClockChangeListener(listenerArgumentCaptor.capture());
279         listenerArgumentCaptor.getValue().onCurrentClockChanged();
280 
281         mExecutor.runAllReady();
282         assertEquals(View.INVISIBLE, mFakeDateView.getVisibility());
283     }
284 
285     @Test
testGetClock_nullClock_returnsNull()286     public void testGetClock_nullClock_returnsNull() {
287         mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
288 
289         when(mClockEventController.getClock()).thenReturn(null);
290         assertNull(mController.getClock());
291     }
292 
verifyAttachment(VerificationMode times)293     private void verifyAttachment(VerificationMode times) {
294         mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
295 
296         verify(mClockRegistry, times).registerClockChangeListener(
297                 any(ClockRegistry.ClockChangeListener.class));
298         verify(mClockEventController, times).registerListeners(mView);
299     }
300 
301     @Test
testSplitShadeEnabledSetToSmartspaceController()302     public void testSplitShadeEnabledSetToSmartspaceController() {
303         mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
304 
305         mController.setSplitShadeEnabled(true);
306         verify(mSmartspaceController, times(1)).setSplitShadeEnabled(true);
307         verify(mSmartspaceController, times(0)).setSplitShadeEnabled(false);
308     }
309 
310     @Test
testSplitShadeDisabledSetToSmartspaceController()311     public void testSplitShadeDisabledSetToSmartspaceController() {
312         mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
313 
314         mController.setSplitShadeEnabled(false);
315         verify(mSmartspaceController, times(1)).setSplitShadeEnabled(false);
316         verify(mSmartspaceController, times(0)).setSplitShadeEnabled(true);
317     }
318 }
319