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.GONE; 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 org.mockito.Mockito.mock; 28 import static org.mockito.Mockito.verify; 29 import static org.mockito.Mockito.when; 30 31 import android.content.Context; 32 import android.graphics.Color; 33 import android.graphics.Paint.Style; 34 import android.test.suitebuilder.annotation.SmallTest; 35 import android.testing.AndroidTestingRunner; 36 import android.testing.TestableLooper.RunWithLooper; 37 import android.util.AttributeSet; 38 import android.view.LayoutInflater; 39 import android.view.View; 40 import android.widget.FrameLayout; 41 import android.widget.TextClock; 42 43 import com.android.systemui.R; 44 import com.android.systemui.SysuiTestCase; 45 import com.android.systemui.plugins.ClockPlugin; 46 import com.android.systemui.statusbar.StatusBarState; 47 48 import org.junit.Before; 49 import org.junit.Test; 50 import org.junit.runner.RunWith; 51 import org.mockito.MockitoAnnotations; 52 53 @SmallTest 54 @RunWith(AndroidTestingRunner.class) 55 // Need to run on the main thread because KeyguardSliceView$Row init checks for 56 // the main thread before acquiring a wake lock. This class is constructed when 57 // the keyguard_clcok_switch layout is inflated. 58 @RunWithLooper(setAsMainLooper = true) 59 public class KeyguardClockSwitchTest extends SysuiTestCase { 60 private FrameLayout mClockFrame; 61 private FrameLayout mLargeClockFrame; 62 private TextClock mBigClock; 63 64 private AnimatableClockView mClockView; 65 private AnimatableClockView mLargeClockView; 66 View mMockKeyguardSliceView; 67 KeyguardClockSwitch mKeyguardClockSwitch; 68 69 @Before setUp()70 public void setUp() { 71 mMockKeyguardSliceView = mock(KeyguardSliceView.class); 72 when(mMockKeyguardSliceView.getContext()).thenReturn(mContext); 73 when(mMockKeyguardSliceView.findViewById(R.id.keyguard_status_area)) 74 .thenReturn(mMockKeyguardSliceView); 75 76 LayoutInflater layoutInflater = LayoutInflater.from(getContext()); 77 layoutInflater.setPrivateFactory(new LayoutInflater.Factory2() { 78 79 @Override 80 public View onCreateView(View parent, String name, Context context, 81 AttributeSet attrs) { 82 return onCreateView(name, context, attrs); 83 } 84 85 @Override 86 public View onCreateView(String name, Context context, AttributeSet attrs) { 87 if ("com.android.keyguard.KeyguardSliceView".equals(name)) { 88 return mMockKeyguardSliceView; 89 } 90 return null; 91 } 92 }); 93 mKeyguardClockSwitch = 94 (KeyguardClockSwitch) layoutInflater.inflate(R.layout.keyguard_clock_switch, null); 95 mClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view); 96 mClockView = mKeyguardClockSwitch.findViewById(R.id.animatable_clock_view); 97 mLargeClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view_large); 98 mLargeClockView = mKeyguardClockSwitch.findViewById(R.id.animatable_clock_view_large); 99 mBigClock = new TextClock(getContext()); 100 mKeyguardClockSwitch.mChildrenAreLaidOut = true; 101 MockitoAnnotations.initMocks(this); 102 } 103 104 @Test onPluginConnected_showPluginClock()105 public void onPluginConnected_showPluginClock() { 106 ClockPlugin plugin = mock(ClockPlugin.class); 107 TextClock pluginView = new TextClock(getContext()); 108 when(plugin.getView()).thenReturn(pluginView); 109 110 mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD); 111 112 assertThat(mClockView.getVisibility()).isEqualTo(GONE); 113 assertThat(plugin.getView().getParent()).isEqualTo(mClockFrame); 114 } 115 116 @Test onPluginConnected_showPluginBigClock()117 public void onPluginConnected_showPluginBigClock() { 118 // GIVEN the plugin returns a view for the big clock 119 ClockPlugin plugin = mock(ClockPlugin.class); 120 when(plugin.getBigClockView()).thenReturn(mBigClock); 121 // WHEN the plugin is connected 122 mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD); 123 // THEN the big clock container is visible and it is the parent of the 124 // big clock view. 125 assertThat(mLargeClockView.getVisibility()).isEqualTo(View.GONE); 126 assertThat(mBigClock.getParent()).isEqualTo(mLargeClockFrame); 127 } 128 129 @Test onPluginConnected_nullView()130 public void onPluginConnected_nullView() { 131 ClockPlugin plugin = mock(ClockPlugin.class); 132 mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD); 133 assertThat(mClockView.getVisibility()).isEqualTo(VISIBLE); 134 } 135 136 @Test onPluginConnected_showSecondPluginClock()137 public void onPluginConnected_showSecondPluginClock() { 138 // GIVEN a plugin has already connected 139 ClockPlugin plugin1 = mock(ClockPlugin.class); 140 when(plugin1.getView()).thenReturn(new TextClock(getContext())); 141 mKeyguardClockSwitch.setClockPlugin(plugin1, StatusBarState.KEYGUARD); 142 // WHEN a second plugin is connected 143 ClockPlugin plugin2 = mock(ClockPlugin.class); 144 when(plugin2.getView()).thenReturn(new TextClock(getContext())); 145 mKeyguardClockSwitch.setClockPlugin(plugin2, StatusBarState.KEYGUARD); 146 // THEN only the view from the second plugin should be a child of KeyguardClockSwitch. 147 assertThat(plugin2.getView().getParent()).isEqualTo(mClockFrame); 148 assertThat(plugin1.getView().getParent()).isNull(); 149 } 150 151 @Test onPluginConnected_darkAmountInitialized()152 public void onPluginConnected_darkAmountInitialized() { 153 // GIVEN that the dark amount has already been set 154 mKeyguardClockSwitch.setDarkAmount(0.5f); 155 // WHEN a plugin is connected 156 ClockPlugin plugin = mock(ClockPlugin.class); 157 mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD); 158 // THEN dark amount should be initalized on the plugin. 159 verify(plugin).setDarkAmount(0.5f); 160 } 161 162 @Test onPluginDisconnected_showDefaultClock()163 public void onPluginDisconnected_showDefaultClock() { 164 ClockPlugin plugin = mock(ClockPlugin.class); 165 TextClock pluginView = new TextClock(getContext()); 166 when(plugin.getView()).thenReturn(pluginView); 167 168 mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD); 169 assertThat(mClockView.getVisibility()).isEqualTo(GONE); 170 171 mKeyguardClockSwitch.setClockPlugin(null, StatusBarState.KEYGUARD); 172 assertThat(mClockView.getVisibility()).isEqualTo(VISIBLE); 173 174 assertThat(plugin.getView().getParent()).isNull(); 175 } 176 177 @Test onPluginDisconnected_hidePluginBigClock()178 public void onPluginDisconnected_hidePluginBigClock() { 179 // GIVEN the plugin returns a view for the big clock 180 ClockPlugin plugin = mock(ClockPlugin.class); 181 TextClock pluginView = new TextClock(getContext()); 182 when(plugin.getBigClockView()).thenReturn(pluginView); 183 // WHEN the plugin is connected and then disconnected 184 mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD); 185 mKeyguardClockSwitch.setClockPlugin(null, StatusBarState.KEYGUARD); 186 // THEN the big lock container is GONE and the big clock view doesn't have 187 // a parent. 188 assertThat(mLargeClockView.getVisibility()).isEqualTo(VISIBLE); 189 assertThat(pluginView.getParent()).isNull(); 190 } 191 192 @Test onPluginDisconnected_nullView()193 public void onPluginDisconnected_nullView() { 194 ClockPlugin plugin = mock(ClockPlugin.class); 195 mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD); 196 mKeyguardClockSwitch.setClockPlugin(null, StatusBarState.KEYGUARD); 197 assertThat(mClockView.getVisibility()).isEqualTo(VISIBLE); 198 } 199 200 @Test onPluginDisconnected_secondOfTwoDisconnected()201 public void onPluginDisconnected_secondOfTwoDisconnected() { 202 // GIVEN two plugins are connected 203 ClockPlugin plugin1 = mock(ClockPlugin.class); 204 when(plugin1.getView()).thenReturn(new TextClock(getContext())); 205 mKeyguardClockSwitch.setClockPlugin(plugin1, StatusBarState.KEYGUARD); 206 ClockPlugin plugin2 = mock(ClockPlugin.class); 207 when(plugin2.getView()).thenReturn(new TextClock(getContext())); 208 mKeyguardClockSwitch.setClockPlugin(plugin2, StatusBarState.KEYGUARD); 209 // WHEN the second plugin is disconnected 210 mKeyguardClockSwitch.setClockPlugin(null, StatusBarState.KEYGUARD); 211 // THEN the default clock should be shown. 212 assertThat(mClockView.getVisibility()).isEqualTo(VISIBLE); 213 assertThat(plugin1.getView().getParent()).isNull(); 214 assertThat(plugin2.getView().getParent()).isNull(); 215 } 216 217 @Test onPluginDisconnected_onDestroyView()218 public void onPluginDisconnected_onDestroyView() { 219 // GIVEN a plugin is connected 220 ClockPlugin clockPlugin = mock(ClockPlugin.class); 221 when(clockPlugin.getView()).thenReturn(new TextClock(getContext())); 222 mKeyguardClockSwitch.setClockPlugin(clockPlugin, StatusBarState.KEYGUARD); 223 // WHEN the plugin is disconnected 224 mKeyguardClockSwitch.setClockPlugin(null, StatusBarState.KEYGUARD); 225 // THEN onDestroyView is called on the plugin 226 verify(clockPlugin).onDestroyView(); 227 } 228 229 @Test setTextColor_pluginClockSetTextColor()230 public void setTextColor_pluginClockSetTextColor() { 231 ClockPlugin plugin = mock(ClockPlugin.class); 232 TextClock pluginView = new TextClock(getContext()); 233 when(plugin.getView()).thenReturn(pluginView); 234 mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD); 235 236 mKeyguardClockSwitch.setTextColor(Color.WHITE); 237 238 verify(plugin).setTextColor(Color.WHITE); 239 } 240 241 242 @Test setStyle_pluginClockSetStyle()243 public void setStyle_pluginClockSetStyle() { 244 ClockPlugin plugin = mock(ClockPlugin.class); 245 TextClock pluginView = new TextClock(getContext()); 246 when(plugin.getView()).thenReturn(pluginView); 247 Style style = mock(Style.class); 248 mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD); 249 250 mKeyguardClockSwitch.setStyle(style); 251 252 verify(plugin).setStyle(style); 253 } 254 255 @Test switchingToBigClock_makesSmallClockDisappear()256 public void switchingToBigClock_makesSmallClockDisappear() { 257 mKeyguardClockSwitch.switchToClock(LARGE); 258 259 mKeyguardClockSwitch.mClockInAnim.end(); 260 mKeyguardClockSwitch.mClockOutAnim.end(); 261 262 assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1); 263 assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE); 264 assertThat(mClockFrame.getAlpha()).isEqualTo(0); 265 } 266 267 @Test switchingToSmallClock_makesBigClockDisappear()268 public void switchingToSmallClock_makesBigClockDisappear() { 269 mKeyguardClockSwitch.switchToClock(SMALL); 270 271 mKeyguardClockSwitch.mClockInAnim.end(); 272 mKeyguardClockSwitch.mClockOutAnim.end(); 273 274 assertThat(mClockFrame.getAlpha()).isEqualTo(1); 275 assertThat(mClockFrame.getVisibility()).isEqualTo(VISIBLE); 276 // only big clock is removed at switch 277 assertThat(mLargeClockFrame.getParent()).isNull(); 278 assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0); 279 } 280 281 @Test switchingToBigClock_returnsTrueOnlyWhenItWasNotVisibleBefore()282 public void switchingToBigClock_returnsTrueOnlyWhenItWasNotVisibleBefore() { 283 assertThat(mKeyguardClockSwitch.switchToClock(LARGE)).isTrue(); 284 assertThat(mKeyguardClockSwitch.switchToClock(LARGE)).isFalse(); 285 } 286 } 287