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 junit.framework.Assert.assertEquals; 20 21 import static org.mockito.ArgumentMatchers.any; 22 import static org.mockito.ArgumentMatchers.anyBoolean; 23 import static org.mockito.ArgumentMatchers.anyLong; 24 import static org.mockito.ArgumentMatchers.eq; 25 import static org.mockito.Mockito.mock; 26 import static org.mockito.Mockito.never; 27 import static org.mockito.Mockito.spy; 28 import static org.mockito.Mockito.times; 29 import static org.mockito.Mockito.verify; 30 import static org.mockito.Mockito.when; 31 32 import android.animation.AnimatorTestRule; 33 import android.platform.test.annotations.DisableFlags; 34 import android.testing.AndroidTestingRunner; 35 import android.testing.TestableLooper; 36 import android.view.View; 37 38 import androidx.test.filters.SmallTest; 39 40 import com.android.app.animation.Interpolators; 41 import com.android.systemui.Flags; 42 import com.android.systemui.animation.ViewHierarchyAnimator; 43 import com.android.systemui.plugins.clocks.ClockConfig; 44 import com.android.systemui.plugins.clocks.ClockController; 45 import com.android.systemui.res.R; 46 import com.android.systemui.statusbar.notification.AnimatableProperty; 47 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; 48 49 import org.junit.Rule; 50 import org.junit.Test; 51 import org.junit.runner.RunWith; 52 import org.mockito.ArgumentCaptor; 53 54 import java.lang.reflect.Field; 55 56 @SmallTest 57 @TestableLooper.RunWithLooper(setAsMainLooper = true) 58 @RunWith(AndroidTestingRunner.class) 59 public class KeyguardStatusViewControllerTest extends KeyguardStatusViewControllerBaseTest { 60 61 @Rule 62 public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(this); 63 64 @Test dozeTimeTick_updatesSlice()65 public void dozeTimeTick_updatesSlice() { 66 mController.dozeTimeTick(); 67 verify(mKeyguardSliceViewController).refresh(); 68 } 69 70 @Test dozeTimeTick_updatesClock()71 public void dozeTimeTick_updatesClock() { 72 mController.dozeTimeTick(); 73 verify(mKeyguardClockSwitchController).refresh(); 74 } 75 76 @Test setTranslationYExcludingMedia_forwardsCallToView()77 public void setTranslationYExcludingMedia_forwardsCallToView() { 78 float translationY = 123f; 79 80 mController.setTranslationY(translationY, /* excludeMedia= */true); 81 82 verify(mKeyguardStatusView).setChildrenTranslationY(translationY, /* excludeMedia= */true); 83 } 84 85 @Test 86 @DisableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) onLocaleListChangedNotifiesClockSwitchController()87 public void onLocaleListChangedNotifiesClockSwitchController() { 88 ArgumentCaptor<ConfigurationListener> configurationListenerArgumentCaptor = 89 ArgumentCaptor.forClass(ConfigurationListener.class); 90 91 mController.onViewAttached(); 92 verify(mConfigurationController).addCallback(configurationListenerArgumentCaptor.capture()); 93 94 configurationListenerArgumentCaptor.getValue().onLocaleListChanged(); 95 verify(mKeyguardClockSwitchController).onLocaleListChanged(); 96 } 97 98 @Test updatePosition_primaryClockAnimation()99 public void updatePosition_primaryClockAnimation() { 100 ClockController mockClock = mock(ClockController.class); 101 when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock); 102 when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", "", "", false, true, false)); 103 104 mController.updatePosition(10, 15, 20f, true); 105 106 verify(mControllerMock).setProperty(AnimatableProperty.Y, 15f, true); 107 verify(mKeyguardClockSwitchController).updatePosition( 108 10, 20f, KeyguardStatusViewController.CLOCK_ANIMATION_PROPERTIES, true); 109 verify(mControllerMock).setProperty(AnimatableProperty.SCALE_X, 1f, true); 110 verify(mControllerMock).setProperty(AnimatableProperty.SCALE_Y, 1f, true); 111 } 112 113 @Test updatePosition_alternateClockAnimation()114 public void updatePosition_alternateClockAnimation() { 115 ClockController mockClock = mock(ClockController.class); 116 when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock); 117 when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", "", "", true, true, false)); 118 119 mController.updatePosition(10, 15, 20f, true); 120 121 verify(mControllerMock).setProperty(AnimatableProperty.Y, 15f, true); 122 verify(mKeyguardClockSwitchController).updatePosition( 123 10, 1f, KeyguardStatusViewController.CLOCK_ANIMATION_PROPERTIES, true); 124 verify(mControllerMock).setProperty(AnimatableProperty.SCALE_X, 20f, true); 125 verify(mControllerMock).setProperty(AnimatableProperty.SCALE_Y, 20f, true); 126 } 127 128 @Test splitShadeEnabledPassedToClockSwitchController()129 public void splitShadeEnabledPassedToClockSwitchController() { 130 mController.setSplitShadeEnabled(true); 131 verify(mKeyguardClockSwitchController, times(1)).setSplitShadeEnabled(true); 132 verify(mKeyguardClockSwitchController, times(0)).setSplitShadeEnabled(false); 133 } 134 135 @Test splitShadeDisabledPassedToClockSwitchController()136 public void splitShadeDisabledPassedToClockSwitchController() { 137 mController.setSplitShadeEnabled(false); 138 verify(mKeyguardClockSwitchController, times(1)).setSplitShadeEnabled(false); 139 verify(mKeyguardClockSwitchController, times(0)).setSplitShadeEnabled(true); 140 } 141 142 @Test correctlyDump()143 public void correctlyDump() { 144 mController.onInit(); 145 verify(mDumpManager).registerDumpable(eq(mController.getInstanceName()), eq(mController)); 146 mController.onDestroy(); 147 verify(mDumpManager, times(1)).unregisterDumpable(eq(mController.getInstanceName())); 148 } 149 150 @Test onInit_addsOnLayoutChangeListenerToClockSwitch()151 public void onInit_addsOnLayoutChangeListenerToClockSwitch() { 152 when(mKeyguardStatusView.findViewById(R.id.status_view_media_container)).thenReturn( 153 mMediaHostContainer); 154 155 mController.onInit(); 156 157 ArgumentCaptor<View.OnLayoutChangeListener> captor = 158 ArgumentCaptor.forClass(View.OnLayoutChangeListener.class); 159 verify(mKeyguardClockSwitch).addOnLayoutChangeListener(captor.capture()); 160 } 161 162 @Test clockSwitchHeightChanged_animatesMediaHostContainer()163 public void clockSwitchHeightChanged_animatesMediaHostContainer() { 164 when(mKeyguardStatusView.findViewById(R.id.status_view_media_container)).thenReturn( 165 mMediaHostContainer); 166 167 mController.onInit(); 168 169 ArgumentCaptor<View.OnLayoutChangeListener> captor = 170 ArgumentCaptor.forClass(View.OnLayoutChangeListener.class); 171 verify(mKeyguardClockSwitch).addOnLayoutChangeListener(captor.capture()); 172 173 // Above here is the same as `onInit_addsOnLayoutChangeListenerToClockSwitch`. 174 // Below here is the actual test. 175 176 ViewHierarchyAnimator.Companion animator = ViewHierarchyAnimator.Companion; 177 ViewHierarchyAnimator.Companion spiedAnimator = spy(animator); 178 setCompanion(spiedAnimator); 179 180 View.OnLayoutChangeListener listener = captor.getValue(); 181 182 mController.setSplitShadeEnabled(true); 183 when(mKeyguardClockSwitch.getSplitShadeCentered()).thenReturn(false); 184 when(mKeyguardUpdateMonitor.isKeyguardVisible()).thenReturn(true); 185 when(mMediaHostContainer.getVisibility()).thenReturn(View.VISIBLE); 186 when(mMediaHostContainer.getHeight()).thenReturn(200); 187 188 when(mKeyguardClockSwitch.getHeight()).thenReturn(0); 189 listener.onLayoutChange(mKeyguardClockSwitch, /* left= */ 0, /* top= */ 0, /* right= */ 190 0, /* bottom= */ 0, /* oldLeft= */ 0, /* oldTop= */ 0, /* oldRight= */ 191 0, /* oldBottom = */ 200); 192 verify(spiedAnimator).animateNextUpdate(mMediaHostContainer, 193 Interpolators.STANDARD, /* duration= */ 500L, /* animateChildren= */ false); 194 195 // Resets ViewHierarchyAnimator.Companion to its original value 196 setCompanion(animator); 197 } 198 199 @Test clockSwitchHeightNotChanged_doesNotAnimateMediaOutputContainer()200 public void clockSwitchHeightNotChanged_doesNotAnimateMediaOutputContainer() { 201 when(mKeyguardStatusView.findViewById(R.id.status_view_media_container)).thenReturn( 202 mMediaHostContainer); 203 204 mController.onInit(); 205 206 ArgumentCaptor<View.OnLayoutChangeListener> captor = 207 ArgumentCaptor.forClass(View.OnLayoutChangeListener.class); 208 verify(mKeyguardClockSwitch).addOnLayoutChangeListener(captor.capture()); 209 210 // Above here is the same as `onInit_addsOnLayoutChangeListenerToClockSwitch`. 211 // Below here is the actual test. 212 213 ViewHierarchyAnimator.Companion animator = ViewHierarchyAnimator.Companion; 214 ViewHierarchyAnimator.Companion spiedAnimator = spy(animator); 215 setCompanion(spiedAnimator); 216 217 View.OnLayoutChangeListener listener = captor.getValue(); 218 219 mController.setSplitShadeEnabled(true); 220 when(mKeyguardClockSwitch.getSplitShadeCentered()).thenReturn(false); 221 when(mKeyguardUpdateMonitor.isKeyguardVisible()).thenReturn(true); 222 when(mMediaHostContainer.getVisibility()).thenReturn(View.VISIBLE); 223 when(mMediaHostContainer.getHeight()).thenReturn(200); 224 225 when(mKeyguardClockSwitch.getHeight()).thenReturn(200); 226 listener.onLayoutChange(mKeyguardClockSwitch, /* left= */ 0, /* top= */ 0, /* right= */ 227 0, /* bottom= */ 0, /* oldLeft= */ 0, /* oldTop= */ 0, /* oldRight= */ 228 0, /* oldBottom = */ 200); 229 verify(spiedAnimator, never()).animateNextUpdate(any(), any(), anyLong(), anyBoolean()); 230 231 // Resets ViewHierarchyAnimator.Companion to its original value 232 setCompanion(animator); 233 } 234 setCompanion(ViewHierarchyAnimator.Companion companion)235 private void setCompanion(ViewHierarchyAnimator.Companion companion) { 236 try { 237 Field field = ViewHierarchyAnimator.class.getDeclaredField("Companion"); 238 field.setAccessible(true); 239 field.set(null, companion); 240 } catch (Exception e) { 241 throw new RuntimeException(e); 242 } 243 } 244 245 @Test 246 @DisableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) statusAreaHeightChange_animatesHeightOutputChange()247 public void statusAreaHeightChange_animatesHeightOutputChange() { 248 // Init & Capture Layout Listener 249 mController.onInit(); 250 mController.onViewAttached(); 251 252 when(mDozeParameters.getAlwaysOn()).thenReturn(true); 253 ArgumentCaptor<View.OnLayoutChangeListener> captor = 254 ArgumentCaptor.forClass(View.OnLayoutChangeListener.class); 255 verify(mKeyguardStatusAreaView).addOnLayoutChangeListener(captor.capture()); 256 View.OnLayoutChangeListener listener = captor.getValue(); 257 258 // Setup and validate initial height 259 when(mKeyguardStatusView.getHeight()).thenReturn(200); 260 when(mKeyguardClockSwitchController.getNotificationIconAreaHeight()).thenReturn(10); 261 assertEquals(190, mController.getLockscreenHeight()); 262 263 // Trigger Change and validate value unchanged immediately 264 when(mKeyguardStatusAreaView.getHeight()).thenReturn(100); 265 when(mKeyguardStatusView.getHeight()).thenReturn(300); // Include child height 266 listener.onLayoutChange(mKeyguardStatusAreaView, 267 /* new layout */ 100, 300, 200, 400, 268 /* old layout */ 100, 300, 200, 300); 269 assertEquals(190, mController.getLockscreenHeight()); 270 271 // Complete animation, validate height increased 272 mAnimatorTestRule.advanceTimeBy(200); 273 assertEquals(290, mController.getLockscreenHeight()); 274 } 275 } 276