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.mockito.ArgumentMatchers.any; 21 import static org.mockito.ArgumentMatchers.anyBoolean; 22 import static org.mockito.ArgumentMatchers.anyInt; 23 import static org.mockito.Mockito.mock; 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.content.res.Resources; 31 import android.database.ContentObserver; 32 import android.net.Uri; 33 import android.provider.Settings; 34 import android.testing.AndroidTestingRunner; 35 import android.view.View; 36 import android.widget.FrameLayout; 37 import android.widget.LinearLayout; 38 import android.widget.RelativeLayout; 39 40 import androidx.test.filters.SmallTest; 41 42 import com.android.internal.colorextraction.ColorExtractor; 43 import com.android.keyguard.clock.ClockManager; 44 import com.android.systemui.R; 45 import com.android.systemui.SysuiTestCase; 46 import com.android.systemui.broadcast.BroadcastDispatcher; 47 import com.android.systemui.colorextraction.SysuiColorExtractor; 48 import com.android.systemui.keyguard.KeyguardUnlockAnimationController; 49 import com.android.systemui.plugins.ClockPlugin; 50 import com.android.systemui.plugins.statusbar.StatusBarStateController; 51 import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController; 52 import com.android.systemui.statusbar.StatusBarState; 53 import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController; 54 import com.android.systemui.statusbar.phone.KeyguardBypassController; 55 import com.android.systemui.statusbar.phone.NotificationIconAreaController; 56 import com.android.systemui.statusbar.phone.NotificationIconContainer; 57 import com.android.systemui.statusbar.policy.BatteryController; 58 import com.android.systemui.util.concurrency.FakeExecutor; 59 import com.android.systemui.util.settings.SecureSettings; 60 import com.android.systemui.util.time.FakeSystemClock; 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.Mock; 67 import org.mockito.MockitoAnnotations; 68 import org.mockito.verification.VerificationMode; 69 70 @SmallTest 71 @RunWith(AndroidTestingRunner.class) 72 public class KeyguardClockSwitchControllerTest extends SysuiTestCase { 73 74 @Mock 75 private KeyguardClockSwitch mView; 76 @Mock 77 private StatusBarStateController mStatusBarStateController; 78 @Mock 79 private SysuiColorExtractor mColorExtractor; 80 @Mock 81 private ClockManager mClockManager; 82 @Mock 83 KeyguardSliceViewController mKeyguardSliceViewController; 84 @Mock 85 NotificationIconAreaController mNotificationIconAreaController; 86 @Mock 87 BroadcastDispatcher mBroadcastDispatcher; 88 @Mock 89 BatteryController mBatteryController; 90 @Mock 91 KeyguardUpdateMonitor mKeyguardUpdateMonitor; 92 @Mock 93 KeyguardBypassController mBypassController; 94 @Mock 95 LockscreenSmartspaceController mSmartspaceController; 96 97 @Mock 98 Resources mResources; 99 KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; 100 @Mock 101 SmartspaceTransitionController mSmartSpaceTransitionController; 102 @Mock 103 private ClockPlugin mClockPlugin; 104 @Mock 105 ColorExtractor.GradientColors mGradientColors; 106 107 @Mock 108 private NotificationIconContainer mNotificationIcons; 109 @Mock 110 private AnimatableClockView mClockView; 111 @Mock 112 private AnimatableClockView mLargeClockView; 113 @Mock 114 private FrameLayout mLargeClockFrame; 115 @Mock 116 private SecureSettings mSecureSettings; 117 118 private final View mFakeSmartspaceView = new View(mContext); 119 120 private KeyguardClockSwitchController mController; 121 private View mSliceView; 122 private FakeExecutor mExecutor; 123 124 @Before setup()125 public void setup() { 126 MockitoAnnotations.initMocks(this); 127 128 when(mView.findViewById(R.id.left_aligned_notification_icon_container)) 129 .thenReturn(mNotificationIcons); 130 when(mNotificationIcons.getLayoutParams()).thenReturn( 131 mock(RelativeLayout.LayoutParams.class)); 132 when(mView.getContext()).thenReturn(getContext()); 133 when(mView.getResources()).thenReturn(mResources); 134 135 when(mView.findViewById(R.id.animatable_clock_view)).thenReturn(mClockView); 136 when(mView.findViewById(R.id.animatable_clock_view_large)).thenReturn(mLargeClockView); 137 when(mView.findViewById(R.id.lockscreen_clock_view_large)).thenReturn(mLargeClockFrame); 138 when(mClockView.getContext()).thenReturn(getContext()); 139 when(mLargeClockView.getContext()).thenReturn(getContext()); 140 141 when(mView.isAttachedToWindow()).thenReturn(true); 142 when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView); 143 mExecutor = new FakeExecutor(new FakeSystemClock()); 144 mController = new KeyguardClockSwitchController( 145 mView, 146 mStatusBarStateController, 147 mColorExtractor, 148 mClockManager, 149 mKeyguardSliceViewController, 150 mNotificationIconAreaController, 151 mBroadcastDispatcher, 152 mBatteryController, 153 mKeyguardUpdateMonitor, 154 mBypassController, 155 mSmartspaceController, 156 mKeyguardUnlockAnimationController, 157 mSmartSpaceTransitionController, 158 mSecureSettings, 159 mExecutor, 160 mResources 161 ); 162 163 when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); 164 when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors); 165 166 mSliceView = new View(getContext()); 167 when(mView.findViewById(R.id.keyguard_slice_view)).thenReturn(mSliceView); 168 when(mView.findViewById(R.id.keyguard_status_area)).thenReturn( 169 new LinearLayout(getContext())); 170 } 171 172 @Test testInit_viewAlreadyAttached()173 public void testInit_viewAlreadyAttached() { 174 mController.init(); 175 176 verifyAttachment(times(1)); 177 } 178 179 @Test testInit_viewNotYetAttached()180 public void testInit_viewNotYetAttached() { 181 ArgumentCaptor<View.OnAttachStateChangeListener> listenerArgumentCaptor = 182 ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); 183 184 when(mView.isAttachedToWindow()).thenReturn(false); 185 mController.init(); 186 verify(mView).addOnAttachStateChangeListener(listenerArgumentCaptor.capture()); 187 188 verifyAttachment(never()); 189 190 listenerArgumentCaptor.getValue().onViewAttachedToWindow(mView); 191 192 verifyAttachment(times(1)); 193 } 194 195 @Test testInitSubControllers()196 public void testInitSubControllers() { 197 mController.init(); 198 verify(mKeyguardSliceViewController).init(); 199 } 200 201 @Test testInit_viewDetached()202 public void testInit_viewDetached() { 203 ArgumentCaptor<View.OnAttachStateChangeListener> listenerArgumentCaptor = 204 ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); 205 mController.init(); 206 verify(mView).addOnAttachStateChangeListener(listenerArgumentCaptor.capture()); 207 208 verifyAttachment(times(1)); 209 210 listenerArgumentCaptor.getValue().onViewDetachedFromWindow(mView); 211 verify(mColorExtractor).removeOnColorsChangedListener( 212 any(ColorExtractor.OnColorsChangedListener.class)); 213 } 214 215 @Test testPluginPassesStatusBarState()216 public void testPluginPassesStatusBarState() { 217 ArgumentCaptor<ClockManager.ClockChangedListener> listenerArgumentCaptor = 218 ArgumentCaptor.forClass(ClockManager.ClockChangedListener.class); 219 220 mController.init(); 221 verify(mClockManager).addOnClockChangedListener(listenerArgumentCaptor.capture()); 222 223 listenerArgumentCaptor.getValue().onClockChanged(mClockPlugin); 224 verify(mView).setClockPlugin(mClockPlugin, StatusBarState.SHADE); 225 } 226 227 @Test testSmartspaceEnabledRemovesKeyguardStatusArea()228 public void testSmartspaceEnabledRemovesKeyguardStatusArea() { 229 when(mSmartspaceController.isEnabled()).thenReturn(true); 230 when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView); 231 mController.init(); 232 233 assertEquals(View.GONE, mSliceView.getVisibility()); 234 } 235 236 @Test testSmartspaceDisabledShowsKeyguardStatusArea()237 public void testSmartspaceDisabledShowsKeyguardStatusArea() { 238 when(mSmartspaceController.isEnabled()).thenReturn(false); 239 mController.init(); 240 241 assertEquals(View.VISIBLE, mSliceView.getVisibility()); 242 } 243 244 @Test testRefresh()245 public void testRefresh() { 246 mController.refresh(); 247 248 verify(mSmartspaceController).requestSmartspaceUpdate(); 249 } 250 251 @Test testChangeToDoubleLineClockSetsSmallClock()252 public void testChangeToDoubleLineClockSetsSmallClock() { 253 when(mSecureSettings.getInt(Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK, 1)) 254 .thenReturn(0); 255 ArgumentCaptor<ContentObserver> observerCaptor = 256 ArgumentCaptor.forClass(ContentObserver.class); 257 mController.init(); 258 verify(mSecureSettings).registerContentObserver(any(Uri.class), 259 anyBoolean(), observerCaptor.capture()); 260 ContentObserver observer = observerCaptor.getValue(); 261 mExecutor.runAllReady(); 262 263 // When a settings change has occurred to the small clock, make sure the view is adjusted 264 reset(mView); 265 observer.onChange(true); 266 mExecutor.runAllReady(); 267 verify(mView).switchToClock(KeyguardClockSwitch.SMALL); 268 } 269 verifyAttachment(VerificationMode times)270 private void verifyAttachment(VerificationMode times) { 271 verify(mClockManager, times).addOnClockChangedListener( 272 any(ClockManager.ClockChangedListener.class)); 273 verify(mColorExtractor, times).addOnColorsChangedListener( 274 any(ColorExtractor.OnColorsChangedListener.class)); 275 verify(mView, times).updateColors(mGradientColors); 276 } 277 } 278