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.google.common.truth.Truth.assertThat; 23 24 import static org.mockito.Mockito.doReturn; 25 import static org.mockito.Mockito.mock; 26 import static org.mockito.Mockito.never; 27 import static org.mockito.Mockito.verify; 28 import static org.mockito.Mockito.when; 29 30 import android.graphics.Color; 31 import android.graphics.Paint.Style; 32 import android.test.suitebuilder.annotation.SmallTest; 33 import android.testing.AndroidTestingRunner; 34 import android.testing.TestableLooper.RunWithLooper; 35 import android.text.TextPaint; 36 import android.view.LayoutInflater; 37 import android.view.View; 38 import android.widget.FrameLayout; 39 import android.widget.TextClock; 40 41 import com.android.keyguard.clock.ClockManager; 42 import com.android.systemui.SystemUIFactory; 43 import com.android.systemui.SysuiTestCase; 44 import com.android.systemui.plugins.ClockPlugin; 45 import com.android.systemui.plugins.statusbar.StatusBarStateController; 46 import com.android.systemui.statusbar.StatusBarState; 47 import com.android.systemui.util.InjectionInflationController; 48 49 import org.junit.Before; 50 import org.junit.Test; 51 import org.junit.runner.RunWith; 52 import org.mockito.InjectMocks; 53 import org.mockito.Mock; 54 import org.mockito.MockitoAnnotations; 55 56 @SmallTest 57 @RunWith(AndroidTestingRunner.class) 58 // Need to run on the main thread because KeyguardSliceView$Row init checks for 59 // the main thread before acquiring a wake lock. This class is constructed when 60 // the keyguard_clcok_switch layout is inflated. 61 @RunWithLooper(setAsMainLooper = true) 62 public class KeyguardClockSwitchTest extends SysuiTestCase { 63 private FrameLayout mClockContainer; 64 private FrameLayout mBigClockContainer; 65 private TextClock mBigClock; 66 private StatusBarStateController.StateListener mStateListener; 67 68 @Mock 69 TextClock mClockView; 70 @InjectMocks 71 KeyguardClockSwitch mKeyguardClockSwitch; 72 73 @Before 74 public void setUp() { 75 InjectionInflationController inflationController = new InjectionInflationController( 76 SystemUIFactory.getInstance().getRootComponent()); 77 LayoutInflater layoutInflater = inflationController 78 .injectable(LayoutInflater.from(getContext())); 79 mKeyguardClockSwitch = 80 (KeyguardClockSwitch) layoutInflater.inflate(R.layout.keyguard_clock_switch, null); 81 mClockContainer = mKeyguardClockSwitch.findViewById(R.id.clock_view); 82 mBigClockContainer = new FrameLayout(getContext()); 83 mBigClock = new TextClock(getContext()); 84 MockitoAnnotations.initMocks(this); 85 when(mClockView.getPaint()).thenReturn(mock(TextPaint.class)); 86 mStateListener = mKeyguardClockSwitch.getStateListener(); 87 } 88 89 @Test 90 public void onPluginConnected_showPluginClock() { 91 ClockPlugin plugin = mock(ClockPlugin.class); 92 TextClock pluginView = new TextClock(getContext()); 93 when(plugin.getView()).thenReturn(pluginView); 94 95 mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin); 96 97 verify(mClockView).setVisibility(GONE); 98 assertThat(plugin.getView().getParent()).isEqualTo(mClockContainer); 99 } 100 101 @Test 102 public void onPluginConnected_showPluginBigClock() { 103 // GIVEN that the container for the big clock has visibility GONE 104 mBigClockContainer.setVisibility(GONE); 105 mKeyguardClockSwitch.setBigClockContainer(mBigClockContainer); 106 // AND the plugin returns a view for the big clock 107 ClockPlugin plugin = mock(ClockPlugin.class); 108 when(plugin.getBigClockView()).thenReturn(mBigClock); 109 // AND in the keyguard state 110 mStateListener.onStateChanged(StatusBarState.KEYGUARD); 111 // WHEN the plugin is connected 112 mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin); 113 // THEN the big clock container is visible and it is the parent of the 114 // big clock view. 115 assertThat(mBigClockContainer.getVisibility()).isEqualTo(View.VISIBLE); 116 assertThat(mBigClock.getParent()).isEqualTo(mBigClockContainer); 117 } 118 119 @Test 120 public void onPluginConnected_nullView() { 121 ClockPlugin plugin = mock(ClockPlugin.class); 122 mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin); 123 verify(mClockView, never()).setVisibility(GONE); 124 } 125 126 @Test 127 public void onPluginConnected_showSecondPluginClock() { 128 // GIVEN a plugin has already connected 129 ClockPlugin plugin1 = mock(ClockPlugin.class); 130 when(plugin1.getView()).thenReturn(new TextClock(getContext())); 131 mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin1); 132 // WHEN a second plugin is connected 133 ClockPlugin plugin2 = mock(ClockPlugin.class); 134 when(plugin2.getView()).thenReturn(new TextClock(getContext())); 135 mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin2); 136 // THEN only the view from the second plugin should be a child of KeyguardClockSwitch. 137 assertThat(plugin2.getView().getParent()).isEqualTo(mClockContainer); 138 assertThat(plugin1.getView().getParent()).isNull(); 139 } 140 141 @Test 142 public void onPluginConnected_darkAmountInitialized() { 143 // GIVEN that the dark amount has already been set 144 mKeyguardClockSwitch.setDarkAmount(0.5f); 145 // WHEN a plugin is connected 146 ClockPlugin plugin = mock(ClockPlugin.class); 147 mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin); 148 // THEN dark amount should be initalized on the plugin. 149 verify(plugin).setDarkAmount(0.5f); 150 } 151 152 @Test 153 public void onPluginDisconnected_showDefaultClock() { 154 ClockPlugin plugin = mock(ClockPlugin.class); 155 TextClock pluginView = new TextClock(getContext()); 156 when(plugin.getView()).thenReturn(pluginView); 157 mClockView.setVisibility(GONE); 158 159 mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin); 160 mKeyguardClockSwitch.getClockChangedListener().onClockChanged(null); 161 162 verify(mClockView).setVisibility(VISIBLE); 163 assertThat(plugin.getView().getParent()).isNull(); 164 } 165 166 @Test 167 public void onPluginDisconnected_hidePluginBigClock() { 168 // GIVEN that the big clock container is visible 169 FrameLayout bigClockContainer = new FrameLayout(getContext()); 170 bigClockContainer.setVisibility(VISIBLE); 171 mKeyguardClockSwitch.setBigClockContainer(bigClockContainer); 172 // AND the plugin returns a view for the big clock 173 ClockPlugin plugin = mock(ClockPlugin.class); 174 TextClock pluginView = new TextClock(getContext()); 175 when(plugin.getBigClockView()).thenReturn(pluginView); 176 // AND in the keyguard state 177 mStateListener.onStateChanged(StatusBarState.KEYGUARD); 178 // WHEN the plugin is connected and then disconnected 179 mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin); 180 mKeyguardClockSwitch.getClockChangedListener().onClockChanged(null); 181 // THEN the big lock container is GONE and the big clock view doesn't have 182 // a parent. 183 assertThat(bigClockContainer.getVisibility()).isEqualTo(GONE); 184 assertThat(pluginView.getParent()).isNull(); 185 } 186 187 @Test 188 public void onPluginDisconnected_nullView() { 189 ClockPlugin plugin = mock(ClockPlugin.class); 190 mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin); 191 mKeyguardClockSwitch.getClockChangedListener().onClockChanged(null); 192 verify(mClockView, never()).setVisibility(GONE); 193 } 194 195 @Test 196 public void onPluginDisconnected_secondOfTwoDisconnected() { 197 // GIVEN two plugins are connected 198 ClockPlugin plugin1 = mock(ClockPlugin.class); 199 when(plugin1.getView()).thenReturn(new TextClock(getContext())); 200 ClockManager.ClockChangedListener listener = mKeyguardClockSwitch.getClockChangedListener(); 201 listener.onClockChanged(plugin1); 202 ClockPlugin plugin2 = mock(ClockPlugin.class); 203 when(plugin2.getView()).thenReturn(new TextClock(getContext())); 204 listener.onClockChanged(plugin2); 205 // WHEN the second plugin is disconnected 206 listener.onClockChanged(null); 207 // THEN the default clock should be shown. 208 verify(mClockView).setVisibility(VISIBLE); 209 assertThat(plugin1.getView().getParent()).isNull(); 210 assertThat(plugin2.getView().getParent()).isNull(); 211 } 212 213 @Test 214 public void onPluginDisconnected_onDestroyView() { 215 // GIVEN a plugin is connected 216 ClockPlugin clockPlugin = mock(ClockPlugin.class); 217 when(clockPlugin.getView()).thenReturn(new TextClock(getContext())); 218 ClockManager.ClockChangedListener listener = mKeyguardClockSwitch.getClockChangedListener(); 219 listener.onClockChanged(clockPlugin); 220 // WHEN the plugin is disconnected 221 listener.onClockChanged(null); 222 // THEN onDestroyView is called on the plugin 223 verify(clockPlugin).onDestroyView(); 224 } 225 226 @Test 227 public void setTextColor_defaultClockSetTextColor() { 228 mKeyguardClockSwitch.setTextColor(Color.YELLOW); 229 230 verify(mClockView).setTextColor(Color.YELLOW); 231 } 232 233 @Test 234 public void setTextColor_pluginClockSetTextColor() { 235 ClockPlugin plugin = mock(ClockPlugin.class); 236 TextClock pluginView = new TextClock(getContext()); 237 when(plugin.getView()).thenReturn(pluginView); 238 mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin); 239 240 mKeyguardClockSwitch.setTextColor(Color.WHITE); 241 242 verify(plugin).setTextColor(Color.WHITE); 243 } 244 245 @Test 246 public void setStyle_defaultClockSetStyle() { 247 TextPaint paint = mock(TextPaint.class); 248 Style style = mock(Style.class); 249 doReturn(paint).when(mClockView).getPaint(); 250 251 mKeyguardClockSwitch.setStyle(style); 252 253 verify(paint).setStyle(style); 254 } 255 256 @Test 257 public void setStyle_pluginClockSetStyle() { 258 ClockPlugin plugin = mock(ClockPlugin.class); 259 TextClock pluginView = new TextClock(getContext()); 260 when(plugin.getView()).thenReturn(pluginView); 261 Style style = mock(Style.class); 262 mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin); 263 264 mKeyguardClockSwitch.setStyle(style); 265 266 verify(plugin).setStyle(style); 267 } 268 269 @Test 270 public void onStateChanged_GoneInShade() { 271 // GIVEN that the big clock container is visible 272 mBigClockContainer.setVisibility(View.VISIBLE); 273 mKeyguardClockSwitch.setBigClockContainer(mBigClockContainer); 274 // WHEN transitioned to SHADE state 275 mStateListener.onStateChanged(StatusBarState.SHADE); 276 // THEN the container is gone. 277 assertThat(mBigClockContainer.getVisibility()).isEqualTo(View.GONE); 278 } 279 280 @Test 281 public void onStateChanged_VisibleInKeyguard() { 282 // GIVEN that the big clock container is gone 283 mBigClockContainer.setVisibility(View.GONE); 284 mKeyguardClockSwitch.setBigClockContainer(mBigClockContainer); 285 // AND GIVEN that a plugin is active. 286 ClockPlugin plugin = mock(ClockPlugin.class); 287 when(plugin.getBigClockView()).thenReturn(mBigClock); 288 mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin); 289 // WHEN transitioned to KEYGUARD state 290 mStateListener.onStateChanged(StatusBarState.KEYGUARD); 291 // THEN the container is visible. 292 assertThat(mBigClockContainer.getVisibility()).isEqualTo(View.VISIBLE); 293 } 294 295 @Test 296 public void setBigClockContainer_visible() { 297 // GIVEN that the big clock container is visible 298 mBigClockContainer.setVisibility(View.VISIBLE); 299 // AND GIVEN that a plugin is active. 300 ClockPlugin plugin = mock(ClockPlugin.class); 301 when(plugin.getBigClockView()).thenReturn(mBigClock); 302 mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin); 303 // AND in the keyguard state 304 mStateListener.onStateChanged(StatusBarState.KEYGUARD); 305 // WHEN the container is associated with the clock switch 306 mKeyguardClockSwitch.setBigClockContainer(mBigClockContainer); 307 // THEN the container remains visible. 308 assertThat(mBigClockContainer.getVisibility()).isEqualTo(View.VISIBLE); 309 } 310 311 @Test 312 public void setBigClockContainer_gone() { 313 // GIVEN that the big clock container is gone 314 mBigClockContainer.setVisibility(View.GONE); 315 // AND GIVEN that a plugin is active. 316 ClockPlugin plugin = mock(ClockPlugin.class); 317 when(plugin.getBigClockView()).thenReturn(mBigClock); 318 mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin); 319 // AND in the keyguard state 320 mStateListener.onStateChanged(StatusBarState.KEYGUARD); 321 // WHEN the container is associated with the clock switch 322 mKeyguardClockSwitch.setBigClockContainer(mBigClockContainer); 323 // THEN the container is made visible. 324 assertThat(mBigClockContainer.getVisibility()).isEqualTo(View.VISIBLE); 325 } 326 } 327