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