/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package com.android.keyguard; import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; import static com.android.keyguard.KeyguardClockSwitch.LARGE; import static com.android.keyguard.KeyguardClockSwitch.SMALL; import static com.google.common.truth.Truth.assertThat; import static junit.framework.TestCase.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.content.Context; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.TextView; import androidx.test.filters.SmallTest; import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.clocks.ClockController; import com.android.systemui.plugins.clocks.ClockFaceController; import com.android.systemui.res.R; import com.android.systemui.statusbar.StatusBarState; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) // Need to run on the main thread because KeyguardSliceView$Row init checks for // the main thread before acquiring a wake lock. This class is constructed when // the keyguard_clock_switch layout is inflated. @RunWithLooper(setAsMainLooper = true) public class KeyguardClockSwitchTest extends SysuiTestCase { @Mock ViewGroup mMockKeyguardSliceView; @Mock ClockController mClock; @Mock ClockFaceController mSmallClock; @Mock ClockFaceController mLargeClock; private FrameLayout mSmallClockFrame; private FrameLayout mLargeClockFrame; private KeyguardStatusAreaView mStatusArea; KeyguardClockSwitch mKeyguardClockSwitch; @Before public void setUp() { mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); MockitoAnnotations.initMocks(this); when(mMockKeyguardSliceView.getContext()).thenReturn(mContext); when(mMockKeyguardSliceView.findViewById(R.id.keyguard_status_area)) .thenReturn(mMockKeyguardSliceView); when(mClock.getSmallClock()).thenReturn(mSmallClock); when(mClock.getLargeClock()).thenReturn(mLargeClock); when(mSmallClock.getView()).thenReturn(new TextView(getContext())); when(mLargeClock.getView()).thenReturn(new TextView(getContext())); LayoutInflater layoutInflater = LayoutInflater.from(getContext()); layoutInflater.setPrivateFactory(new LayoutInflater.Factory2() { @Override public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { return onCreateView(name, context, attrs); } @Override public View onCreateView(String name, Context context, AttributeSet attrs) { if ("com.android.keyguard.KeyguardSliceView".equals(name)) { return mMockKeyguardSliceView; } return null; } }); mKeyguardClockSwitch = (KeyguardClockSwitch) layoutInflater.inflate(R.layout.keyguard_clock_switch, null); mSmallClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view); mLargeClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view_large); mStatusArea = mKeyguardClockSwitch.findViewById(R.id.keyguard_status_area); mKeyguardClockSwitch.mChildrenAreLaidOut = true; } @Test public void noPluginConnected_showNothing() { mKeyguardClockSwitch.setClock(null, StatusBarState.KEYGUARD); assertEquals(mLargeClockFrame.getChildCount(), 0); assertEquals(mSmallClockFrame.getChildCount(), 0); } @Test public void pluginConnectedThenDisconnected_showNothing() { mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD); assertEquals(mLargeClockFrame.getChildCount(), 1); assertEquals(mSmallClockFrame.getChildCount(), 1); mKeyguardClockSwitch.setClock(null, StatusBarState.KEYGUARD); assertEquals(mLargeClockFrame.getChildCount(), 0); assertEquals(mSmallClockFrame.getChildCount(), 0); } @Test public void onPluginConnected_showClock() { mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD); assertEquals(mClock.getSmallClock().getView().getParent(), mSmallClockFrame); assertEquals(mClock.getLargeClock().getView().getParent(), mLargeClockFrame); } @Test public void onPluginConnected_showSecondPluginClock() { // GIVEN a plugin has already connected ClockController otherClock = mock(ClockController.class); ClockFaceController smallClock = mock(ClockFaceController.class); ClockFaceController largeClock = mock(ClockFaceController.class); when(otherClock.getSmallClock()).thenReturn(smallClock); when(otherClock.getLargeClock()).thenReturn(largeClock); when(smallClock.getView()).thenReturn(new TextView(getContext())); when(largeClock.getView()).thenReturn(new TextView(getContext())); mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD); mKeyguardClockSwitch.setClock(otherClock, StatusBarState.KEYGUARD); // THEN only the view from the second plugin should be a child of KeyguardClockSwitch. assertThat(otherClock.getSmallClock().getView().getParent()).isEqualTo(mSmallClockFrame); assertThat(otherClock.getLargeClock().getView().getParent()).isEqualTo(mLargeClockFrame); assertThat(mClock.getSmallClock().getView().getParent()).isNull(); assertThat(mClock.getLargeClock().getView().getParent()).isNull(); } @Test public void onPluginDisconnected_secondOfTwoDisconnected() { // GIVEN two plugins are connected ClockController otherClock = mock(ClockController.class); ClockFaceController smallClock = mock(ClockFaceController.class); ClockFaceController largeClock = mock(ClockFaceController.class); when(otherClock.getSmallClock()).thenReturn(smallClock); when(otherClock.getLargeClock()).thenReturn(largeClock); when(smallClock.getView()).thenReturn(new TextView(getContext())); when(largeClock.getView()).thenReturn(new TextView(getContext())); mKeyguardClockSwitch.setClock(otherClock, StatusBarState.KEYGUARD); mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD); // WHEN the second plugin is disconnected mKeyguardClockSwitch.setClock(null, StatusBarState.KEYGUARD); // THEN nothing should be shown assertThat(otherClock.getSmallClock().getView().getParent()).isNull(); assertThat(otherClock.getLargeClock().getView().getParent()).isNull(); assertThat(mClock.getSmallClock().getView().getParent()).isNull(); assertThat(mClock.getLargeClock().getView().getParent()).isNull(); } @Test public void switchingToBigClockWithAnimation_makesSmallClockDisappear() { mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true); mKeyguardClockSwitch.mClockInAnim.end(); mKeyguardClockSwitch.mClockOutAnim.end(); mKeyguardClockSwitch.mStatusAreaAnim.end(); assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1); assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE); assertThat(mSmallClockFrame.getAlpha()).isEqualTo(0); assertThat(mSmallClockFrame.getVisibility()).isEqualTo(INVISIBLE); } @Test public void switchingToBigClockNoAnimation_makesSmallClockDisappear() { mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ false); assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1); assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE); assertThat(mSmallClockFrame.getAlpha()).isEqualTo(0); assertThat(mSmallClockFrame.getVisibility()).isEqualTo(INVISIBLE); } @Test public void switchingToSmallClockWithAnimation_makesBigClockDisappear() { mKeyguardClockSwitch.switchToClock(SMALL, /* animate */ true); mKeyguardClockSwitch.mClockInAnim.end(); mKeyguardClockSwitch.mClockOutAnim.end(); mKeyguardClockSwitch.mStatusAreaAnim.end(); assertThat(mSmallClockFrame.getAlpha()).isEqualTo(1); assertThat(mSmallClockFrame.getVisibility()).isEqualTo(VISIBLE); // only big clock is removed at switch assertThat(mLargeClockFrame.getParent()).isNull(); assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0); assertThat(mLargeClockFrame.getVisibility()).isEqualTo(INVISIBLE); } @Test public void switchingToSmallClockNoAnimation_makesBigClockDisappear() { mKeyguardClockSwitch.switchToClock(SMALL, false); assertThat(mSmallClockFrame.getAlpha()).isEqualTo(1); assertThat(mSmallClockFrame.getVisibility()).isEqualTo(VISIBLE); // only big clock is removed at switch assertThat(mLargeClockFrame.getParent()).isNull(); assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0); assertThat(mLargeClockFrame.getVisibility()).isEqualTo(INVISIBLE); } @Test public void switchingToSmallClockAnimation_resetsStatusArea() { mKeyguardClockSwitch.switchToClock(SMALL, true); mKeyguardClockSwitch.mClockInAnim.end(); mKeyguardClockSwitch.mClockOutAnim.end(); mKeyguardClockSwitch.mStatusAreaAnim.end(); assertThat(mStatusArea.getTranslationX()).isEqualTo(0); assertThat(mStatusArea.getTranslationY()).isEqualTo(0); assertThat(mStatusArea.getScaleX()).isEqualTo(1); assertThat(mStatusArea.getScaleY()).isEqualTo(1); } @Test public void switchingToSmallClockNoAnimation_resetsStatusArea() { mKeyguardClockSwitch.switchToClock(SMALL, false); assertThat(mStatusArea.getTranslationX()).isEqualTo(0); assertThat(mStatusArea.getTranslationY()).isEqualTo(0); assertThat(mStatusArea.getScaleX()).isEqualTo(1); assertThat(mStatusArea.getScaleY()).isEqualTo(1); } @Test public void switchingToBigClock_returnsTrueOnlyWhenItWasNotVisibleBefore() { assertThat(mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true)).isTrue(); assertThat(mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true)).isFalse(); } }