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