blob: f2ac0c7a77369422a3d52ffc00dcf06465a97f18 [file] [log] [blame]
/*
* Copyright (C) 2017 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.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.provider.Settings.Global.ONE_HANDED_KEYGUARD_SIDE;
import static android.provider.Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT;
import static android.provider.Settings.Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
import static com.android.keyguard.KeyguardSecurityContainer.MODE_DEFAULT;
import static com.android.keyguard.KeyguardSecurityContainer.MODE_ONE_HANDED;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.graphics.Insets;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.widget.FrameLayout;
import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.policy.UserSwitcherController.UserRecord;
import com.android.systemui.util.settings.GlobalSettings;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper()
public class KeyguardSecurityContainerTest extends SysuiTestCase {
private static final int VIEW_WIDTH = 1600;
private int mScreenWidth;
private int mFakeMeasureSpec;
@Rule
public MockitoRule mRule = MockitoJUnit.rule();
@Mock
private KeyguardSecurityViewFlipper mSecurityViewFlipper;
@Mock
private GlobalSettings mGlobalSettings;
@Mock
private FalsingManager mFalsingManager;
@Mock
private UserSwitcherController mUserSwitcherController;
@Mock
private KeyguardStateController mKeyguardStateController;
@Captor
private ArgumentCaptor<FrameLayout.LayoutParams> mLayoutCaptor;
private KeyguardSecurityContainer mKeyguardSecurityContainer;
private FrameLayout.LayoutParams mSecurityViewFlipperLayoutParams;
@Before
public void setup() {
// Needed here, otherwise when mKeyguardSecurityContainer is created below, it'll cache
// the real references (rather than the TestableResources that this call creates).
mContext.ensureTestableResources();
mSecurityViewFlipperLayoutParams = new FrameLayout.LayoutParams(
MATCH_PARENT, MATCH_PARENT);
when(mSecurityViewFlipper.getLayoutParams()).thenReturn(mSecurityViewFlipperLayoutParams);
mKeyguardSecurityContainer = new KeyguardSecurityContainer(getContext());
mKeyguardSecurityContainer.mSecurityViewFlipper = mSecurityViewFlipper;
mKeyguardSecurityContainer.addView(mSecurityViewFlipper, new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
when(mUserSwitcherController.getCurrentUserName()).thenReturn("Test User");
when(mUserSwitcherController.getKeyguardStateController())
.thenReturn(mKeyguardStateController);
when(mKeyguardStateController.isShowing()).thenReturn(true);
mScreenWidth = getUiDevice().getDisplayWidth();
mFakeMeasureSpec = View
.MeasureSpec.makeMeasureSpec(mScreenWidth, View.MeasureSpec.EXACTLY);
}
@Test
public void onMeasure_usesHalfWidthWithOneHandedModeEnabled() {
mKeyguardSecurityContainer.initMode(MODE_ONE_HANDED, mGlobalSettings, mFalsingManager,
mUserSwitcherController);
int halfWidthMeasureSpec =
View.MeasureSpec.makeMeasureSpec(mScreenWidth / 2, View.MeasureSpec.EXACTLY);
mKeyguardSecurityContainer.onMeasure(mFakeMeasureSpec, mFakeMeasureSpec);
verify(mSecurityViewFlipper).measure(halfWidthMeasureSpec, mFakeMeasureSpec);
}
@Test
public void onMeasure_usesFullWidthWithOneHandedModeDisabled() {
mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
mUserSwitcherController);
mKeyguardSecurityContainer.measure(mFakeMeasureSpec, mFakeMeasureSpec);
verify(mSecurityViewFlipper).measure(mFakeMeasureSpec, mFakeMeasureSpec);
}
@Test
public void onMeasure_respectsViewInsets() {
int paddingBottom = getContext().getResources()
.getDimensionPixelSize(R.dimen.keyguard_security_view_bottom_margin);
int imeInsetAmount = paddingBottom + 1;
int systemBarInsetAmount = 0;
mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
mUserSwitcherController);
Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount);
Insets systemBarInset = Insets.of(0, 0, 0, systemBarInsetAmount);
WindowInsets insets = new WindowInsets.Builder()
.setInsets(ime(), imeInset)
.setInsetsIgnoringVisibility(systemBars(), systemBarInset)
.build();
// It's reduced by the max of the systembar and IME, so just subtract IME inset.
int expectedHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec(
mScreenWidth - imeInsetAmount, View.MeasureSpec.EXACTLY);
mKeyguardSecurityContainer.onApplyWindowInsets(insets);
mKeyguardSecurityContainer.measure(mFakeMeasureSpec, mFakeMeasureSpec);
verify(mSecurityViewFlipper).measure(mFakeMeasureSpec, expectedHeightMeasureSpec);
}
@Test
public void onMeasure_respectsViewInsets_largerSystembar() {
int imeInsetAmount = 0;
int paddingBottom = getContext().getResources()
.getDimensionPixelSize(R.dimen.keyguard_security_view_bottom_margin);
int systemBarInsetAmount = paddingBottom + 1;
mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
mUserSwitcherController);
Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount);
Insets systemBarInset = Insets.of(0, 0, 0, systemBarInsetAmount);
WindowInsets insets = new WindowInsets.Builder()
.setInsets(ime(), imeInset)
.setInsetsIgnoringVisibility(systemBars(), systemBarInset)
.build();
int expectedHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec(
mScreenWidth - systemBarInsetAmount, View.MeasureSpec.EXACTLY);
mKeyguardSecurityContainer.onApplyWindowInsets(insets);
mKeyguardSecurityContainer.measure(mFakeMeasureSpec, mFakeMeasureSpec);
verify(mSecurityViewFlipper).measure(mFakeMeasureSpec, expectedHeightMeasureSpec);
}
private void setupForUpdateKeyguardPosition(boolean oneHandedMode) {
int mode = oneHandedMode ? MODE_ONE_HANDED : MODE_DEFAULT;
mKeyguardSecurityContainer.initMode(mode, mGlobalSettings, mFalsingManager,
mUserSwitcherController);
mKeyguardSecurityContainer.measure(mFakeMeasureSpec, mFakeMeasureSpec);
mKeyguardSecurityContainer.layout(0, 0, mScreenWidth, mScreenWidth);
// Clear any interactions with the mock so we know the interactions definitely come from the
// below testing.
reset(mSecurityViewFlipper);
}
@Test
public void updatePosition_movesKeyguard() {
setupForUpdateKeyguardPosition(/* oneHandedMode= */ true);
mKeyguardSecurityContainer.updatePositionByTouchX(
mKeyguardSecurityContainer.getWidth() - 1f);
verify(mGlobalSettings).putInt(ONE_HANDED_KEYGUARD_SIDE, ONE_HANDED_KEYGUARD_SIDE_RIGHT);
assertSecurityTranslationX(
mKeyguardSecurityContainer.getWidth() - mSecurityViewFlipper.getWidth());
mKeyguardSecurityContainer.updatePositionByTouchX(1f);
verify(mGlobalSettings).putInt(ONE_HANDED_KEYGUARD_SIDE, ONE_HANDED_KEYGUARD_SIDE_LEFT);
verify(mSecurityViewFlipper).setTranslationX(0.0f);
}
@Test
public void updatePosition_doesntMoveTwoHandedKeyguard() {
setupForUpdateKeyguardPosition(/* oneHandedMode= */ false);
mKeyguardSecurityContainer.updatePositionByTouchX(
mKeyguardSecurityContainer.getWidth() - 1f);
verify(mSecurityViewFlipper, never()).setTranslationX(anyInt());
mKeyguardSecurityContainer.updatePositionByTouchX(1f);
verify(mSecurityViewFlipper, never()).setTranslationX(anyInt());
}
@Test
public void testUserSwitcherModeViewPositionLandscape() {
// GIVEN one user has been setup and in landscape
when(mUserSwitcherController.getUsers()).thenReturn(buildUserRecords(1));
Configuration landscapeConfig = configuration(ORIENTATION_LANDSCAPE);
when(getContext().getResources().getConfiguration()).thenReturn(landscapeConfig);
// WHEN UserSwitcherViewMode is initialized and config has changed
setupUserSwitcher();
mKeyguardSecurityContainer.onConfigurationChanged(landscapeConfig);
// THEN views are oriented side by side
assertSecurityGravity(Gravity.LEFT | Gravity.BOTTOM);
assertUserSwitcherGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
assertSecurityTranslationX(
mKeyguardSecurityContainer.getWidth() - mSecurityViewFlipper.getWidth());
assertUserSwitcherTranslationX(0f);
}
@Test
public void testUserSwitcherModeViewGravityPortrait() {
// GIVEN one user has been setup and in landscape
when(mUserSwitcherController.getUsers()).thenReturn(buildUserRecords(1));
Configuration portraitConfig = configuration(ORIENTATION_PORTRAIT);
when(getContext().getResources().getConfiguration()).thenReturn(portraitConfig);
// WHEN UserSwitcherViewMode is initialized and config has changed
setupUserSwitcher();
reset(mSecurityViewFlipper);
when(mSecurityViewFlipper.getLayoutParams()).thenReturn(mSecurityViewFlipperLayoutParams);
mKeyguardSecurityContainer.onConfigurationChanged(portraitConfig);
// THEN views are both centered horizontally
assertSecurityGravity(Gravity.CENTER_HORIZONTAL);
assertUserSwitcherGravity(Gravity.CENTER_HORIZONTAL);
assertSecurityTranslationX(0);
assertUserSwitcherTranslationX(0);
}
@Test
public void testLessThanTwoUsersDoesNotAllowDropDown() {
// GIVEN one user has been setup
when(mUserSwitcherController.getUsers()).thenReturn(buildUserRecords(1));
// WHEN UserSwitcherViewMode is initialized
setupUserSwitcher();
// THEN the UserSwitcher anchor should not be clickable
ViewGroup anchor = mKeyguardSecurityContainer.findViewById(R.id.user_switcher_anchor);
assertThat(anchor.isClickable()).isFalse();
}
@Test
public void testTwoOrMoreUsersDoesAllowDropDown() {
// GIVEN one user has been setup
ArrayList<UserRecord> records = buildUserRecords(2);
when(mUserSwitcherController.getCurrentUserRecord()).thenReturn(records.get(0));
when(mUserSwitcherController.getUsers()).thenReturn(records);
// WHEN UserSwitcherViewMode is initialized
setupUserSwitcher();
// THEN the UserSwitcher anchor should not be clickable
ViewGroup anchor = mKeyguardSecurityContainer.findViewById(R.id.user_switcher_anchor);
assertThat(anchor.isClickable()).isTrue();
}
@Test
public void testTouchesAreRecognizedAsBeingOnTheOtherSideOfSecurity() {
setupUserSwitcher();
setViewWidth(VIEW_WIDTH);
// security is on the right side by default
assertThat(mKeyguardSecurityContainer.isTouchOnTheOtherSideOfSecurity(
touchEventLeftSide())).isTrue();
assertThat(mKeyguardSecurityContainer.isTouchOnTheOtherSideOfSecurity(
touchEventRightSide())).isFalse();
// move security to the left side
when(mGlobalSettings.getInt(any(), anyInt())).thenReturn(ONE_HANDED_KEYGUARD_SIDE_LEFT);
mKeyguardSecurityContainer.onConfigurationChanged(new Configuration());
assertThat(mKeyguardSecurityContainer.isTouchOnTheOtherSideOfSecurity(
touchEventLeftSide())).isFalse();
assertThat(mKeyguardSecurityContainer.isTouchOnTheOtherSideOfSecurity(
touchEventRightSide())).isTrue();
}
@Test
public void testSecuritySwitchesSidesInLandscapeUserSwitcherMode() {
when(getContext().getResources().getConfiguration())
.thenReturn(configuration(ORIENTATION_LANDSCAPE));
setupUserSwitcher();
// switch sides
when(mGlobalSettings.getInt(any(), anyInt())).thenReturn(ONE_HANDED_KEYGUARD_SIDE_LEFT);
mKeyguardSecurityContainer.onConfigurationChanged(new Configuration());
assertSecurityTranslationX(0);
assertUserSwitcherTranslationX(
mKeyguardSecurityContainer.getWidth() - mSecurityViewFlipper.getWidth());
}
private Configuration configuration(@Configuration.Orientation int orientation) {
Configuration config = new Configuration();
config.orientation = orientation;
return config;
}
private void assertSecurityTranslationX(float translation) {
verify(mSecurityViewFlipper).setTranslationX(translation);
}
private void assertUserSwitcherTranslationX(float translation) {
ViewGroup userSwitcher = mKeyguardSecurityContainer.findViewById(
R.id.keyguard_bouncer_user_switcher);
assertThat(userSwitcher.getTranslationX()).isEqualTo(translation);
}
private void assertUserSwitcherGravity(@Gravity.GravityFlags int gravity) {
ViewGroup userSwitcher = mKeyguardSecurityContainer.findViewById(
R.id.keyguard_bouncer_user_switcher);
assertThat(((FrameLayout.LayoutParams) userSwitcher.getLayoutParams()).gravity)
.isEqualTo(gravity);
}
private void assertSecurityGravity(@Gravity.GravityFlags int gravity) {
verify(mSecurityViewFlipper, atLeastOnce()).setLayoutParams(mLayoutCaptor.capture());
assertThat(mLayoutCaptor.getValue().gravity).isEqualTo(gravity);
}
private void setViewWidth(int width) {
mKeyguardSecurityContainer.setRight(width);
mKeyguardSecurityContainer.setLeft(0);
}
private MotionEvent touchEventLeftSide() {
return MotionEvent.obtain(
/* downTime= */0,
/* eventTime= */0,
MotionEvent.ACTION_DOWN,
/* x= */VIEW_WIDTH / 3f,
/* y= */0,
/* metaState= */0);
}
private MotionEvent touchEventRightSide() {
return MotionEvent.obtain(
/* downTime= */0,
/* eventTime= */0,
MotionEvent.ACTION_DOWN,
/* x= */(VIEW_WIDTH / 3f) * 2,
/* y= */0,
/* metaState= */0);
}
private void setupUserSwitcher() {
when(mGlobalSettings.getInt(any(), anyInt())).thenReturn(ONE_HANDED_KEYGUARD_SIDE_RIGHT);
mKeyguardSecurityContainer.initMode(KeyguardSecurityContainer.MODE_USER_SWITCHER,
mGlobalSettings, mFalsingManager, mUserSwitcherController);
// reset mSecurityViewFlipper so setup doesn't influence test verifications
reset(mSecurityViewFlipper);
when(mSecurityViewFlipper.getLayoutParams()).thenReturn(mSecurityViewFlipperLayoutParams);
}
private ArrayList<UserRecord> buildUserRecords(int count) {
ArrayList<UserRecord> users = new ArrayList<>();
for (int i = 0; i < count; ++i) {
UserInfo info = new UserInfo(i /* id */, "Name: " + i, null /* iconPath */,
0 /* flags */);
users.add(new UserRecord(info, null, false /* isGuest */, false /* isCurrent */,
false /* isAddUser */, false /* isRestricted */, true /* isSwitchToEnabled */,
false /* isAddSupervisedUser */));
}
return users;
}
}