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