1 /* 2 * Copyright (C) 2018 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 android.view.View.INVISIBLE; 20 import static android.view.View.VISIBLE; 21 22 import static com.android.keyguard.KeyguardClockSwitch.LARGE; 23 import static com.android.keyguard.KeyguardClockSwitch.SMALL; 24 25 import static com.google.common.truth.Truth.assertThat; 26 27 import static junit.framework.TestCase.assertEquals; 28 29 import static org.mockito.Mockito.mock; 30 import static org.mockito.Mockito.when; 31 32 import android.content.Context; 33 import android.testing.AndroidTestingRunner; 34 import android.testing.TestableLooper.RunWithLooper; 35 import android.util.AttributeSet; 36 import android.view.LayoutInflater; 37 import android.view.View; 38 import android.view.ViewGroup; 39 import android.widget.FrameLayout; 40 import android.widget.TextView; 41 42 import androidx.test.filters.SmallTest; 43 44 import com.android.systemui.Flags; 45 import com.android.systemui.SysuiTestCase; 46 import com.android.systemui.plugins.clocks.ClockController; 47 import com.android.systemui.plugins.clocks.ClockFaceController; 48 import com.android.systemui.res.R; 49 import com.android.systemui.statusbar.StatusBarState; 50 51 import org.junit.Before; 52 import org.junit.Test; 53 import org.junit.runner.RunWith; 54 import org.mockito.Mock; 55 import org.mockito.MockitoAnnotations; 56 57 @SmallTest 58 @RunWith(AndroidTestingRunner.class) 59 // Need to run on the main thread because KeyguardSliceView$Row init checks for 60 // the main thread before acquiring a wake lock. This class is constructed when 61 // the keyguard_clock_switch layout is inflated. 62 @RunWithLooper(setAsMainLooper = true) 63 public class KeyguardClockSwitchTest extends SysuiTestCase { 64 @Mock 65 ViewGroup mMockKeyguardSliceView; 66 67 @Mock 68 ClockController mClock; 69 70 @Mock 71 ClockFaceController mSmallClock; 72 73 @Mock 74 ClockFaceController mLargeClock; 75 76 private FrameLayout mSmallClockFrame; 77 private FrameLayout mLargeClockFrame; 78 private KeyguardStatusAreaView mStatusArea; 79 80 KeyguardClockSwitch mKeyguardClockSwitch; 81 82 @Before setUp()83 public void setUp() { 84 mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); 85 86 MockitoAnnotations.initMocks(this); 87 when(mMockKeyguardSliceView.getContext()).thenReturn(mContext); 88 when(mMockKeyguardSliceView.findViewById(R.id.keyguard_status_area)) 89 .thenReturn(mMockKeyguardSliceView); 90 91 when(mClock.getSmallClock()).thenReturn(mSmallClock); 92 when(mClock.getLargeClock()).thenReturn(mLargeClock); 93 94 when(mSmallClock.getView()).thenReturn(new TextView(getContext())); 95 when(mLargeClock.getView()).thenReturn(new TextView(getContext())); 96 97 LayoutInflater layoutInflater = LayoutInflater.from(getContext()); 98 layoutInflater.setPrivateFactory(new LayoutInflater.Factory2() { 99 100 @Override 101 public View onCreateView(View parent, String name, Context context, 102 AttributeSet attrs) { 103 return onCreateView(name, context, attrs); 104 } 105 106 @Override 107 public View onCreateView(String name, Context context, AttributeSet attrs) { 108 if ("com.android.keyguard.KeyguardSliceView".equals(name)) { 109 return mMockKeyguardSliceView; 110 } 111 return null; 112 } 113 }); 114 mKeyguardClockSwitch = 115 (KeyguardClockSwitch) layoutInflater.inflate(R.layout.keyguard_clock_switch, null); 116 mSmallClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view); 117 mLargeClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view_large); 118 mStatusArea = mKeyguardClockSwitch.findViewById(R.id.keyguard_status_area); 119 mKeyguardClockSwitch.mChildrenAreLaidOut = true; 120 } 121 122 @Test noPluginConnected_showNothing()123 public void noPluginConnected_showNothing() { 124 mKeyguardClockSwitch.setClock(null, StatusBarState.KEYGUARD); 125 assertEquals(mLargeClockFrame.getChildCount(), 0); 126 assertEquals(mSmallClockFrame.getChildCount(), 0); 127 } 128 129 @Test pluginConnectedThenDisconnected_showNothing()130 public void pluginConnectedThenDisconnected_showNothing() { 131 mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD); 132 assertEquals(mLargeClockFrame.getChildCount(), 1); 133 assertEquals(mSmallClockFrame.getChildCount(), 1); 134 135 mKeyguardClockSwitch.setClock(null, StatusBarState.KEYGUARD); 136 assertEquals(mLargeClockFrame.getChildCount(), 0); 137 assertEquals(mSmallClockFrame.getChildCount(), 0); 138 } 139 140 @Test onPluginConnected_showClock()141 public void onPluginConnected_showClock() { 142 mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD); 143 144 assertEquals(mClock.getSmallClock().getView().getParent(), mSmallClockFrame); 145 assertEquals(mClock.getLargeClock().getView().getParent(), mLargeClockFrame); 146 } 147 148 @Test onPluginConnected_showSecondPluginClock()149 public void onPluginConnected_showSecondPluginClock() { 150 // GIVEN a plugin has already connected 151 ClockController otherClock = mock(ClockController.class); 152 ClockFaceController smallClock = mock(ClockFaceController.class); 153 ClockFaceController largeClock = mock(ClockFaceController.class); 154 when(otherClock.getSmallClock()).thenReturn(smallClock); 155 when(otherClock.getLargeClock()).thenReturn(largeClock); 156 when(smallClock.getView()).thenReturn(new TextView(getContext())); 157 when(largeClock.getView()).thenReturn(new TextView(getContext())); 158 mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD); 159 mKeyguardClockSwitch.setClock(otherClock, StatusBarState.KEYGUARD); 160 161 // THEN only the view from the second plugin should be a child of KeyguardClockSwitch. 162 assertThat(otherClock.getSmallClock().getView().getParent()).isEqualTo(mSmallClockFrame); 163 assertThat(otherClock.getLargeClock().getView().getParent()).isEqualTo(mLargeClockFrame); 164 assertThat(mClock.getSmallClock().getView().getParent()).isNull(); 165 assertThat(mClock.getLargeClock().getView().getParent()).isNull(); 166 } 167 168 @Test onPluginDisconnected_secondOfTwoDisconnected()169 public void onPluginDisconnected_secondOfTwoDisconnected() { 170 // GIVEN two plugins are connected 171 ClockController otherClock = mock(ClockController.class); 172 ClockFaceController smallClock = mock(ClockFaceController.class); 173 ClockFaceController largeClock = mock(ClockFaceController.class); 174 when(otherClock.getSmallClock()).thenReturn(smallClock); 175 when(otherClock.getLargeClock()).thenReturn(largeClock); 176 when(smallClock.getView()).thenReturn(new TextView(getContext())); 177 when(largeClock.getView()).thenReturn(new TextView(getContext())); 178 mKeyguardClockSwitch.setClock(otherClock, StatusBarState.KEYGUARD); 179 mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD); 180 // WHEN the second plugin is disconnected 181 mKeyguardClockSwitch.setClock(null, StatusBarState.KEYGUARD); 182 // THEN nothing should be shown 183 assertThat(otherClock.getSmallClock().getView().getParent()).isNull(); 184 assertThat(otherClock.getLargeClock().getView().getParent()).isNull(); 185 assertThat(mClock.getSmallClock().getView().getParent()).isNull(); 186 assertThat(mClock.getLargeClock().getView().getParent()).isNull(); 187 } 188 189 @Test switchingToBigClockWithAnimation_makesSmallClockDisappear()190 public void switchingToBigClockWithAnimation_makesSmallClockDisappear() { 191 mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true); 192 193 mKeyguardClockSwitch.mClockInAnim.end(); 194 mKeyguardClockSwitch.mClockOutAnim.end(); 195 mKeyguardClockSwitch.mStatusAreaAnim.end(); 196 197 assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1); 198 assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE); 199 assertThat(mSmallClockFrame.getAlpha()).isEqualTo(0); 200 assertThat(mSmallClockFrame.getVisibility()).isEqualTo(INVISIBLE); 201 } 202 203 @Test switchingToBigClockNoAnimation_makesSmallClockDisappear()204 public void switchingToBigClockNoAnimation_makesSmallClockDisappear() { 205 mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ false); 206 207 assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1); 208 assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE); 209 assertThat(mSmallClockFrame.getAlpha()).isEqualTo(0); 210 assertThat(mSmallClockFrame.getVisibility()).isEqualTo(INVISIBLE); 211 } 212 213 @Test switchingToSmallClockWithAnimation_makesBigClockDisappear()214 public void switchingToSmallClockWithAnimation_makesBigClockDisappear() { 215 mKeyguardClockSwitch.switchToClock(SMALL, /* animate */ true); 216 217 mKeyguardClockSwitch.mClockInAnim.end(); 218 mKeyguardClockSwitch.mClockOutAnim.end(); 219 mKeyguardClockSwitch.mStatusAreaAnim.end(); 220 221 assertThat(mSmallClockFrame.getAlpha()).isEqualTo(1); 222 assertThat(mSmallClockFrame.getVisibility()).isEqualTo(VISIBLE); 223 // only big clock is removed at switch 224 assertThat(mLargeClockFrame.getParent()).isNull(); 225 assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0); 226 assertThat(mLargeClockFrame.getVisibility()).isEqualTo(INVISIBLE); 227 } 228 229 @Test switchingToSmallClockNoAnimation_makesBigClockDisappear()230 public void switchingToSmallClockNoAnimation_makesBigClockDisappear() { 231 mKeyguardClockSwitch.switchToClock(SMALL, false); 232 233 assertThat(mSmallClockFrame.getAlpha()).isEqualTo(1); 234 assertThat(mSmallClockFrame.getVisibility()).isEqualTo(VISIBLE); 235 // only big clock is removed at switch 236 assertThat(mLargeClockFrame.getParent()).isNull(); 237 assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0); 238 assertThat(mLargeClockFrame.getVisibility()).isEqualTo(INVISIBLE); 239 } 240 241 @Test switchingToSmallClockAnimation_resetsStatusArea()242 public void switchingToSmallClockAnimation_resetsStatusArea() { 243 mKeyguardClockSwitch.switchToClock(SMALL, true); 244 245 mKeyguardClockSwitch.mClockInAnim.end(); 246 mKeyguardClockSwitch.mClockOutAnim.end(); 247 mKeyguardClockSwitch.mStatusAreaAnim.end(); 248 249 assertThat(mStatusArea.getTranslationX()).isEqualTo(0); 250 assertThat(mStatusArea.getTranslationY()).isEqualTo(0); 251 assertThat(mStatusArea.getScaleX()).isEqualTo(1); 252 assertThat(mStatusArea.getScaleY()).isEqualTo(1); 253 } 254 255 @Test switchingToSmallClockNoAnimation_resetsStatusArea()256 public void switchingToSmallClockNoAnimation_resetsStatusArea() { 257 mKeyguardClockSwitch.switchToClock(SMALL, false); 258 259 assertThat(mStatusArea.getTranslationX()).isEqualTo(0); 260 assertThat(mStatusArea.getTranslationY()).isEqualTo(0); 261 assertThat(mStatusArea.getScaleX()).isEqualTo(1); 262 assertThat(mStatusArea.getScaleY()).isEqualTo(1); 263 } 264 265 266 @Test switchingToBigClock_returnsTrueOnlyWhenItWasNotVisibleBefore()267 public void switchingToBigClock_returnsTrueOnlyWhenItWasNotVisibleBefore() { 268 assertThat(mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true)).isTrue(); 269 assertThat(mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true)).isFalse(); 270 } 271 } 272