Merge "Bouncer - User switcher for large screens"
diff --git a/packages/SystemUI/res-keyguard/drawable/keyguard_user_switcher_header_bg.xml b/packages/SystemUI/res-keyguard/drawable/keyguard_user_switcher_header_bg.xml
new file mode 100644
index 0000000..177f695
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/keyguard_user_switcher_header_bg.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:paddingMode="stack"
+ android:paddingStart="44dp"
+ android:paddingEnd="44dp"
+ android:paddingLeft="0dp"
+ android:paddingRight="0dp">
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="?androidprv:attr/colorSurface" />
+ <corners android:radius="@dimen/keyguard_user_switcher_corner" />
+ </shape>
+ </item>
+ <item
+ android:drawable="@drawable/ic_ksh_key_down"
+ android:gravity="end|center_vertical"
+ android:width="32dp"
+ android:height="32dp"
+ android:end="12dp" />
+</layer-list>
diff --git a/packages/SystemUI/res-keyguard/drawable/keyguard_user_switcher_popup_bg.xml b/packages/SystemUI/res-keyguard/drawable/keyguard_user_switcher_popup_bg.xml
new file mode 100644
index 0000000..96a2d15
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/keyguard_user_switcher_popup_bg.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 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
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <solid android:color="?androidprv:attr/colorSurface" />
+ <corners android:radius="@dimen/keyguard_user_switcher_popup_corner" />
+</shape>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
new file mode 100644
index 0000000..a2b8bf6
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2021, 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.
+*/
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_bouncer_user_switcher"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:paddingTop="12dp"
+ android:importantForAccessibility="yes"> <!-- Needed because TYPE_WINDOW_STATE_CHANGED is sent
+ from this view when bouncer is shown -->
+
+ <ImageView
+ android:id="@+id/user_icon"
+ android:layout_width="@dimen/keyguard_user_switcher_icon_size"
+ android:layout_height="@dimen/keyguard_user_switcher_icon_size" />
+
+ <!-- need to keep this outer view in order to have a correctly sized anchor
+ for the dropdown menu, as well as dropdown background in the right place -->
+ <LinearLayout
+ android:id="@+id/user_switcher_anchor"
+ android:orientation="horizontal"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_marginTop="32dp"
+ android:minHeight="48dp">
+ <TextView
+ style="@style/Keyguard.UserSwitcher.Spinner.Header"
+ android:clickable="false"
+ android:id="@+id/user_switcher_header"
+ android:layout_width="@dimen/keyguard_user_switcher_width"
+ android:layout_height="wrap_content" />
+ </LinearLayout>>
+
+</LinearLayout>
+
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher_item.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher_item.xml
new file mode 100644
index 0000000..b08e1ff
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher_item.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ Copyright (C) 2021 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.
+ -->
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/Keyguard.UserSwitcher.Spinner.Item"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start"
+ android:paddingStart="@dimen/control_menu_horizontal_padding"
+ android:paddingEnd="@dimen/control_menu_horizontal_padding"
+ android:textDirection="locale"/>
+
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
index a946318..94566c7 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -49,8 +49,6 @@
android:paddingBottom="@dimen/num_pad_entry_row_margin_bottom"
androidprv:layout_constraintEnd_toEndOf="parent"
androidprv:layout_constraintStart_toStartOf="parent"
-
- androidprv:layout_constraintTop_toTopOf="parent"
androidprv:layout_constraintBottom_toTopOf="@id/key1"
androidprv:layout_constraintVertical_bias="0.0">
diff --git a/packages/SystemUI/res-keyguard/values-sw720dp/bools.xml b/packages/SystemUI/res-keyguard/values-sw720dp/bools.xml
index 4daa648..54bb1fc 100644
--- a/packages/SystemUI/res-keyguard/values-sw720dp/bools.xml
+++ b/packages/SystemUI/res-keyguard/values-sw720dp/bools.xml
@@ -18,7 +18,7 @@
<resources>
<!-- Allows PIN/Pattern to be drawn on one side of a display, and for the user to
switch sides -->
- <bool name="can_use_one_handed_bouncer">true</bool>
+ <bool name="can_use_one_handed_bouncer">false</bool>
<!-- Will display the bouncer on one side of the display, and the current user icon and
user switcher on the other side -->
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 89dd741..9533040 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -106,4 +106,13 @@
opacity is zero), but this controls how much motion will actually be applied to it while
animating. Larger values will cause it to move "faster" while fading out/in. -->
<dimen name="one_handed_bouncer_move_animation_translation">120dp</dimen>
+
+
+ <dimen name="keyguard_user_switcher_header_text_size">24sp</dimen>
+ <dimen name="keyguard_user_switcher_item_text_size">18sp</dimen>
+ <dimen name="keyguard_user_switcher_width">300dp</dimen>
+ <dimen name="keyguard_user_switcher_icon_size">250dp</dimen>
+ <dimen name="keyguard_user_switcher_corner">32dp</dimen>
+ <dimen name="keyguard_user_switcher_popup_corner">24dp</dimen>
+ <dimen name="keyguard_user_switcher_item_padding_vertical">15dp</dimen>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index b0bdc72..a7b2b47 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -139,4 +139,23 @@
<style name="TextAppearance.Keyguard.BottomArea.Button">
<item name="android:shadowRadius">0</item>
</style>
+
+ <style name="Keyguard.UserSwitcher.Spinner" parent="@android:style/Widget.DeviceDefault.TextView">
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+ <item name="android:singleLine">true</item>
+ <item name="android:ellipsize">end</item>
+ <item name="android:paddingTop">@dimen/keyguard_user_switcher_item_padding_vertical</item>
+ <item name="android:paddingBottom">@dimen/keyguard_user_switcher_item_padding_vertical</item>
+ </style>
+
+ <style name="Keyguard.UserSwitcher.Spinner.Header">
+ <item name="android:background">@drawable/keyguard_user_switcher_header_bg</item>
+ <item name="android:textSize">@dimen/keyguard_user_switcher_header_text_size</item>
+ </style>
+
+ <style name="Keyguard.UserSwitcher.Spinner.Item">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textSize">@dimen/keyguard_user_switcher_item_text_size</item>
+ </style>
</resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
index 40190c1..7eae729 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
@@ -48,10 +48,6 @@
abstract CharSequence getTitle();
- void animateForIme(float interpolatedFraction, boolean appearingAnim) {
- return;
- }
-
boolean disallowInterceptTouch(MotionEvent event) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 3a3d308..bc366ab 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -156,8 +156,7 @@
setAlpha(0f);
animate()
.alpha(1f)
- .setDuration(500)
- .setStartDelay(300)
+ .setDuration(300)
.start();
setTranslationY(0f);
@@ -219,15 +218,6 @@
return true;
}
-
- @Override
- public void animateForIme(float interpolatedFraction, boolean appearingAnim) {
- animate().cancel();
- setAlpha(appearingAnim
- ? Math.max(interpolatedFraction, getAlpha())
- : 1 - interpolatedFraction);
- }
-
@Override
public CharSequence getTitle() {
return getResources().getString(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 172c7f6..95567ec 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -23,17 +23,23 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
import android.util.MathUtils;
import android.util.TypedValue;
import android.view.Gravity;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
@@ -44,7 +50,10 @@
import android.view.WindowManager;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
+import android.widget.AdapterView;
import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
@@ -56,12 +65,17 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
+import com.android.internal.util.UserIcons;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
+import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.shared.system.SysUiStatsLog;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.policy.UserSwitcherController.BaseUserAdapter;
+import com.android.systemui.statusbar.policy.UserSwitcherController.UserRecord;
import com.android.systemui.util.settings.GlobalSettings;
import java.util.ArrayList;
@@ -110,6 +124,8 @@
@VisibleForTesting
KeyguardSecurityViewFlipper mSecurityViewFlipper;
private GlobalSettings mGlobalSettings;
+ private FalsingCollector mFalsingCollector;
+ private UserSwitcherController mUserSwitcherController;
private AlertDialog mAlertDialog;
private boolean mSwipeUpToRetry;
@@ -124,7 +140,7 @@
private float mStartTouchY = -1;
private boolean mDisappearAnimRunning;
private SwipeListener mSwipeListener;
- private ModeLogic mModeLogic = new DefaultModeLogic();
+ private ViewMode mViewMode = new DefaultViewMode();
private @Mode int mCurrentMode = MODE_DEFAULT;
private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
@@ -173,8 +189,11 @@
interpolatedFraction);
translationY += paddingBottom;
}
- mSecurityViewFlipper.animateForIme(translationY, interpolatedFraction,
- !mDisappearAnimRunning);
+
+ float alpha = mDisappearAnimRunning
+ ? 1 - interpolatedFraction
+ : Math.max(interpolatedFraction, getAlpha());
+ updateChildren(translationY, alpha);
return windowInsets;
}
@@ -183,12 +202,19 @@
public void onEnd(WindowInsetsAnimation animation) {
if (!mDisappearAnimRunning) {
endJankInstrument(InteractionJankMonitor.CUJ_LOCKSCREEN_PASSWORD_APPEAR);
- mSecurityViewFlipper.animateForIme(0, /* interpolatedFraction */ 1f,
- true /* appearingAnim */);
+ updateChildren(0 /* translationY */, 1f /* alpha */);
} else {
endJankInstrument(InteractionJankMonitor.CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR);
}
}
+
+ private void updateChildren(int translationY, float alpha) {
+ for (int i = 0; i < KeyguardSecurityContainer.this.getChildCount(); ++i) {
+ View child = KeyguardSecurityContainer.this.getChildAt(i);
+ child.setTranslationY(translationY);
+ child.setAlpha(alpha);
+ }
+ }
};
// Used to notify the container when something interesting happens.
@@ -270,9 +296,12 @@
void onResume(SecurityMode securityMode, boolean faceAuthEnabled) {
mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback);
updateBiometricRetry(securityMode, faceAuthEnabled);
+
+ setupViewMode();
}
- void initMode(@Mode int mode, GlobalSettings globalSettings) {
+ void initMode(@Mode int mode, GlobalSettings globalSettings, FalsingCollector falsingCollector,
+ UserSwitcherController userSwitcherController) {
if (mCurrentMode == mode) return;
Log.i(TAG, "Switching mode from " + modeToString(mCurrentMode) + " to "
+ modeToString(mode));
@@ -280,16 +309,18 @@
switch (mode) {
case MODE_ONE_HANDED:
- mModeLogic = new OneHandedModeLogic();
+ mViewMode = new OneHandedViewMode();
break;
case MODE_USER_SWITCHER:
- mModeLogic = new UserSwitcherModeLogic();
+ mViewMode = new UserSwitcherViewMode();
break;
default:
- mModeLogic = new DefaultModeLogic();
+ mViewMode = new DefaultViewMode();
}
mGlobalSettings = globalSettings;
- finishSetup();
+ mFalsingCollector = falsingCollector;
+ mUserSwitcherController = userSwitcherController;
+ setupViewMode();
}
private String modeToString(@Mode int mode) {
@@ -305,10 +336,14 @@
}
}
- private void finishSetup() {
- if (mSecurityViewFlipper == null || mGlobalSettings == null) return;
+ private void setupViewMode() {
+ if (mSecurityViewFlipper == null || mGlobalSettings == null
+ || mFalsingCollector == null || mUserSwitcherController == null) {
+ return;
+ }
- mModeLogic.init(this, mGlobalSettings, mSecurityViewFlipper);
+ mViewMode.init(this, mGlobalSettings, mSecurityViewFlipper, mFalsingCollector,
+ mUserSwitcherController);
}
@Mode int getMode() {
@@ -321,13 +356,13 @@
* that the user last interacted with.
*/
void updatePositionByTouchX(float x) {
- mModeLogic.updatePositionByTouchX(x);
+ mViewMode.updatePositionByTouchX(x);
}
/** Returns whether the inner SecurityViewFlipper is left-aligned when in one-handed mode. */
public boolean isOneHandedModeLeftAligned() {
return mCurrentMode == MODE_ONE_HANDED
- && ((OneHandedModeLogic) mModeLogic).isLeftAligned();
+ && ((OneHandedViewMode) mViewMode).isLeftAligned();
}
public void onPause() {
@@ -336,6 +371,7 @@
mAlertDialog = null;
}
mSecurityViewFlipper.setWindowInsetsAnimationCallback(null);
+ mViewMode.reset();
}
@Override
@@ -428,7 +464,7 @@
}
} else {
if (!mIsDragging) {
- mModeLogic.handleTap(event);
+ mViewMode.handleTap(event);
}
}
}
@@ -453,8 +489,19 @@
.animateToFinalPosition(0);
}
+ /**
+ * Runs after a succsssful authentication only
+ */
public void startDisappearAnimation(SecurityMode securitySelection) {
mDisappearAnimRunning = true;
+ mViewMode.startDisappearAnimation(securitySelection);
+ }
+
+ /**
+ * This will run when the bouncer shows in all cases except when the user drags the bouncer up.
+ */
+ public void startAppearAnimation(SecurityMode securityMode) {
+ mViewMode.startAppearAnimation(securityMode);
}
private void beginJankInstrument(int cuj) {
@@ -490,8 +537,6 @@
public void onFinishInflate() {
super.onFinishInflate();
mSecurityViewFlipper = findViewById(R.id.view_flipper);
-
- finishSetup();
}
@Override
@@ -562,10 +607,7 @@
for (int i = 0; i < getChildCount(); i++) {
final View view = getChildAt(i);
if (view.getVisibility() != GONE) {
- int updatedWidthMeasureSpec = widthMeasureSpec;
- if (view == mSecurityViewFlipper) {
- updatedWidthMeasureSpec = mModeLogic.getChildWidthMeasureSpec(widthMeasureSpec);
- }
+ int updatedWidthMeasureSpec = mViewMode.getChildWidthMeasureSpec(widthMeasureSpec);
measureChildWithMargins(view, updatedWidthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams lp = (LayoutParams) view.getLayoutParams();
@@ -595,7 +637,13 @@
// After a layout pass, we need to re-place the inner bouncer, as our bounds may have
// changed.
- mModeLogic.updateSecurityViewLocation();
+ mViewMode.updateSecurityViewLocation();
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration config) {
+ super.onConfigurationChanged(config);
+ mViewMode.updateSecurityViewLocation();
}
void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
@@ -643,10 +691,12 @@
/**
* Enscapsulates the differences between bouncer modes for the container.
*/
- private interface ModeLogic {
+ interface ViewMode {
- default void init(ViewGroup v, GlobalSettings globalSettings,
- KeyguardSecurityViewFlipper viewFlipper) {};
+ default void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
+ @NonNull KeyguardSecurityViewFlipper viewFlipper,
+ @NonNull FalsingCollector falsingCollector,
+ @NonNull UserSwitcherController userSwitcherController) {};
/** Reinitialize the location */
default void updateSecurityViewLocation() {};
@@ -657,19 +707,33 @@
/** A tap on the container, outside of the ViewFlipper */
default void handleTap(MotionEvent event) {};
+ /** Called when the view needs to reset or hides */
+ default void reset() {};
+
+ /** On a successful auth, optionally handle how the view disappears */
+ default void startDisappearAnimation(SecurityMode securityMode) {};
+
+ /** On notif tap, this animation will run */
+ default void startAppearAnimation(SecurityMode securityMode) {};
+
/** Override to alter the width measure spec to perhaps limit the ViewFlipper size */
default int getChildWidthMeasureSpec(int parentWidthMeasureSpec) {
return parentWidthMeasureSpec;
}
}
- private static class DefaultModeLogic implements ModeLogic {
+ /**
+ * Default bouncer is centered within the space
+ */
+ static class DefaultViewMode implements ViewMode {
private ViewGroup mView;
private KeyguardSecurityViewFlipper mViewFlipper;
@Override
- public void init(ViewGroup v, GlobalSettings globalSettings,
- KeyguardSecurityViewFlipper viewFlipper) {
+ public void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
+ @NonNull KeyguardSecurityViewFlipper viewFlipper,
+ @NonNull FalsingCollector falsingCollector,
+ @NonNull UserSwitcherController userSwitcherController) {
mView = v;
mViewFlipper = viewFlipper;
@@ -682,7 +746,6 @@
(FrameLayout.LayoutParams) mViewFlipper.getLayoutParams();
lp.gravity = Gravity.CENTER_HORIZONTAL;
mViewFlipper.setLayoutParams(lp);
-
mViewFlipper.setTranslationX(0);
}
}
@@ -691,13 +754,171 @@
* User switcher mode will display both the current user icon as well as
* a user switcher, in both portrait and landscape modes.
*/
- private static class UserSwitcherModeLogic implements ModeLogic {
+ static class UserSwitcherViewMode implements ViewMode {
private ViewGroup mView;
+ private ViewGroup mUserSwitcherViewGroup;
+ private KeyguardSecurityViewFlipper mViewFlipper;
+ private ImageView mUserIconView;
+ private TextView mUserSwitcher;
+ private FalsingCollector mFalsingCollector;
+ private UserSwitcherController mUserSwitcherController;
+ private KeyguardUserSwitcherPopupMenu mPopup;
@Override
- public void init(ViewGroup v, GlobalSettings globalSettings,
- KeyguardSecurityViewFlipper viewFlipper) {
+ public void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
+ @NonNull KeyguardSecurityViewFlipper viewFlipper,
+ @NonNull FalsingCollector falsingCollector,
+ @NonNull UserSwitcherController userSwitcherController) {
mView = v;
+ mViewFlipper = viewFlipper;
+ mFalsingCollector = falsingCollector;
+ mUserSwitcherController = userSwitcherController;
+
+ if (mUserSwitcherViewGroup == null) {
+ LayoutInflater.from(v.getContext()).inflate(
+ R.layout.keyguard_bouncer_user_switcher,
+ mView,
+ true);
+ mUserSwitcherViewGroup = mView.findViewById(R.id.keyguard_bouncer_user_switcher);
+ }
+
+ mUserIconView = mView.findViewById(R.id.user_icon);
+ Drawable icon = UserIcons.getDefaultUserIcon(v.getContext().getResources(), 0, false);
+ mUserIconView.setImageDrawable(icon);
+
+ updateSecurityViewLocation();
+
+ mUserSwitcher = mView.findViewById(R.id.user_switcher_header);
+ setupUserSwitcher();
+ }
+
+ @Override
+ public void reset() {
+ if (mPopup != null) {
+ mPopup.dismiss();
+ mPopup = null;
+ }
+ }
+
+ @Override
+ public void startAppearAnimation(SecurityMode securityMode) {
+ // IME insets animations handle alpha and translation
+ if (securityMode == SecurityMode.Password) {
+ return;
+ }
+
+ mUserSwitcherViewGroup.setAlpha(0f);
+ ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(mUserSwitcherViewGroup, View.ALPHA,
+ 1f);
+ alphaAnim.setInterpolator(Interpolators.ALPHA_IN);
+ alphaAnim.setDuration(500);
+ alphaAnim.start();
+ }
+
+ @Override
+ public void startDisappearAnimation(SecurityMode securityMode) {
+ // IME insets animations handle alpha and translation
+ if (securityMode == SecurityMode.Password) {
+ return;
+ }
+
+ int yTranslation = mView.getContext().getResources().getDimensionPixelSize(
+ R.dimen.disappear_y_translation);
+
+ AnimatorSet anims = new AnimatorSet();
+ ObjectAnimator yAnim = ObjectAnimator.ofFloat(mView, View.TRANSLATION_Y, yTranslation);
+ ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(mView, View.ALPHA, 0f);
+
+ anims.setInterpolator(Interpolators.STANDARD_ACCELERATE);
+ anims.playTogether(alphaAnim, yAnim);
+ anims.start();
+ }
+
+ private void setupUserSwitcher() {
+ String currentUserName = mUserSwitcherController.getCurrentUserName();
+ mUserSwitcher.setText(currentUserName);
+
+ ViewGroup anchor = mView.findViewById(R.id.user_switcher_anchor);
+ BaseUserAdapter adapter = new BaseUserAdapter(mUserSwitcherController) {
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ UserRecord item = getItem(position);
+ TextView view = (TextView) convertView;
+ if (view == null) {
+ view = (TextView) LayoutInflater.from(parent.getContext()).inflate(
+ R.layout.keyguard_bouncer_user_switcher_item,
+ parent,
+ false);
+ }
+ view.setText(getName(parent.getContext(), item));
+ return view;
+ }
+ };
+
+ if (adapter.getCount() < 2) {
+ // The drop down arrow is at index 1
+ ((LayerDrawable) mUserSwitcher.getBackground()).getDrawable(1).setAlpha(0);
+ anchor.setClickable(false);
+ return;
+ } else {
+ ((LayerDrawable) mUserSwitcher.getBackground()).getDrawable(1).setAlpha(255);
+ }
+
+ anchor.setClickable(true);
+ anchor.setOnTouchListener((v, ev) -> {
+ if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mFalsingCollector.avoidGesture();
+ mPopup = new KeyguardUserSwitcherPopupMenu(v.getContext(),
+ mFalsingCollector);
+ mPopup.setAnchorView(anchor);
+ mPopup.setAdapter(adapter);
+ mPopup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ public void onItemClick(AdapterView parent, View view, int pos,
+ long id) {
+ mFalsingCollector.avoidGesture();
+
+ // - 1 to account for the header view
+ UserRecord user = adapter.getItem(pos - 1);
+ if (!user.isCurrent) {
+ adapter.onUserListItemClicked(user);
+ }
+ mPopup.dismiss();
+ mPopup = null;
+ }
+ });
+ mPopup.show();
+ }
+ return true;
+ });
+ }
+
+ /**
+ * Each view will get half the width. Yes, it would be easier to use something other than
+ * FrameLayout but it was too disruptive to downstream projects to change.
+ */
+ @Override
+ public int getChildWidthMeasureSpec(int parentWidthMeasureSpec) {
+ return MeasureSpec.makeMeasureSpec(
+ MeasureSpec.getSize(parentWidthMeasureSpec) / 2,
+ MeasureSpec.getMode(parentWidthMeasureSpec));
+ }
+
+ @Override
+ public void updateSecurityViewLocation() {
+ if (mView.getContext().getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_PORTRAIT) {
+ updateViewGravity(mViewFlipper, Gravity.CENTER_HORIZONTAL);
+ updateViewGravity(mUserSwitcherViewGroup, Gravity.CENTER_HORIZONTAL);
+ } else {
+ updateViewGravity(mViewFlipper, Gravity.RIGHT | Gravity.BOTTOM);
+ updateViewGravity(mUserSwitcherViewGroup, Gravity.LEFT | Gravity.TOP);
+ }
+ }
+
+ private void updateViewGravity(View v, int gravity) {
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams();
+ lp.gravity = gravity;
+ v.setLayoutParams(lp);
}
}
@@ -705,7 +926,7 @@
* Logic to enabled one-handed bouncer mode. Supports animating the bouncer
* between alternate sides of the display.
*/
- private static class OneHandedModeLogic implements ModeLogic {
+ static class OneHandedViewMode implements ViewMode {
@Nullable private ValueAnimator mRunningOneHandedAnimator;
private ViewGroup mView;
private KeyguardSecurityViewFlipper mViewFlipper;
@@ -713,7 +934,9 @@
@Override
public void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
- @NonNull KeyguardSecurityViewFlipper viewFlipper) {
+ @NonNull KeyguardSecurityViewFlipper viewFlipper,
+ @NonNull FalsingCollector falsingCollector,
+ @NonNull UserSwitcherController userSwitcherController) {
mView = v;
mViewFlipper = viewFlipper;
mGlobalSettings = globalSettings;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 4035229..6b73a32 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -54,6 +54,7 @@
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.settings.GlobalSettings;
@@ -78,6 +79,7 @@
private final SecurityCallback mSecurityCallback;
private final ConfigurationController mConfigurationController;
private final FalsingCollector mFalsingCollector;
+ private final UserSwitcherController mUserSwitcherController;
private final GlobalSettings mGlobalSettings;
private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
@@ -232,6 +234,7 @@
KeyguardSecurityViewFlipperController securityViewFlipperController,
ConfigurationController configurationController,
FalsingCollector falsingCollector,
+ UserSwitcherController userSwitcherController,
GlobalSettings globalSettings) {
super(view);
mLockPatternUtils = lockPatternUtils;
@@ -247,6 +250,7 @@
mConfigurationController = configurationController;
mLastOrientation = getResources().getConfiguration().orientation;
mFalsingCollector = falsingCollector;
+ mUserSwitcherController = userSwitcherController;
mGlobalSettings = globalSettings;
}
@@ -343,14 +347,14 @@
public void startAppearAnimation() {
if (mCurrentSecurityMode != SecurityMode.None) {
+ mView.startAppearAnimation(mCurrentSecurityMode);
getCurrentSecurityController().startAppearAnimation();
}
}
public boolean startDisappearAnimation(Runnable onFinishRunnable) {
- mView.startDisappearAnimation(getCurrentSecurityMode());
-
if (mCurrentSecurityMode != SecurityMode.None) {
+ mView.startDisappearAnimation(mCurrentSecurityMode);
return getCurrentSecurityController().startDisappearAnimation(onFinishRunnable);
}
@@ -506,15 +510,16 @@
}
private void configureMode() {
- // One-handed mode and user-switcher are currently mutually exclusive, and enforced here
+ boolean useSimSecurity = mCurrentSecurityMode == SecurityMode.SimPin
+ || mCurrentSecurityMode == SecurityMode.SimPuk;
int mode = KeyguardSecurityContainer.MODE_DEFAULT;
- if (canDisplayUserSwitcher()) {
+ if (canDisplayUserSwitcher() && !useSimSecurity) {
mode = KeyguardSecurityContainer.MODE_USER_SWITCHER;
} else if (canUseOneHandedBouncer()) {
mode = KeyguardSecurityContainer.MODE_ONE_HANDED;
}
- mView.initMode(mode, mGlobalSettings);
+ mView.initMode(mode, mGlobalSettings, mFalsingCollector, mUserSwitcherController);
}
public void reportFailedUnlockAttempt(int userId, int timeoutMs) {
@@ -605,6 +610,7 @@
private final ConfigurationController mConfigurationController;
private final FalsingCollector mFalsingCollector;
private final GlobalSettings mGlobalSettings;
+ private final UserSwitcherController mUserSwitcherController;
@Inject
Factory(KeyguardSecurityContainer view,
@@ -619,6 +625,7 @@
KeyguardSecurityViewFlipperController securityViewFlipperController,
ConfigurationController configurationController,
FalsingCollector falsingCollector,
+ UserSwitcherController userSwitcherController,
GlobalSettings globalSettings) {
mView = view;
mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory;
@@ -632,6 +639,7 @@
mConfigurationController = configurationController;
mFalsingCollector = falsingCollector;
mGlobalSettings = globalSettings;
+ mUserSwitcherController = userSwitcherController;
}
public KeyguardSecurityContainerController create(
@@ -640,7 +648,8 @@
mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
mKeyguardStateController, securityCallback, mSecurityViewFlipperController,
- mConfigurationController, mFalsingCollector, mGlobalSettings);
+ mConfigurationController, mFalsingCollector, mUserSwitcherController,
+ mGlobalSettings);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
index e01e17d..4d2391a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
@@ -83,16 +83,6 @@
return "";
}
- /**
- * Translate the entire view, and optionally inform the wrapped view of the progress
- * so it can animate with the parent.
- */
- public void animateForIme(int translationY, float interpolatedFraction, boolean appearingAnim) {
- super.setTranslationY(translationY);
- KeyguardInputView v = getSecurityView();
- if (v != null) v.animateForIme(interpolatedFraction, appearingAnim);
- }
-
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
index 0d72c93..03b647b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
@@ -92,11 +92,6 @@
}
@Override
- public boolean startDisappearAnimation(Runnable finishRunnable) {
- return false;
- }
-
- @Override
public CharSequence getTitle() {
return getContext().getString(
com.android.internal.R.string.keyguard_accessibility_sim_puk_unlock);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java
new file mode 100644
index 0000000..ca31b40d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2021 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 android.annotation.NonNull;
+import android.content.Context;
+import android.content.res.Resources;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.ListPopupWindow;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingCollector;
+
+/**
+ * Custom user-switcher for use on the bouncer.
+ */
+public class KeyguardUserSwitcherPopupMenu extends ListPopupWindow {
+ private Context mContext;
+ private FalsingCollector mFalsingCollector;
+ private int mLastHeight = -1;
+ private View.OnLayoutChangeListener mLayoutListener = (v, l, t, r, b, ol, ot, or, ob) -> {
+ int height = -v.getMeasuredHeight() + getAnchorView().getHeight();
+ if (height != mLastHeight) {
+ mLastHeight = height;
+ setVerticalOffset(height);
+ KeyguardUserSwitcherPopupMenu.super.show();
+ }
+ };
+
+ public KeyguardUserSwitcherPopupMenu(@NonNull Context context,
+ @NonNull FalsingCollector falsingCollector) {
+ super(context);
+ mContext = context;
+ mFalsingCollector = falsingCollector;
+ Resources res = mContext.getResources();
+ setBackgroundDrawable(
+ res.getDrawable(R.drawable.keyguard_user_switcher_popup_bg, context.getTheme()));
+ setModal(true);
+ setOverlapAnchor(true);
+ }
+
+ /**
+ * Show the dialog.
+ */
+ @Override
+ public void show() {
+ // need to call show() first in order to construct the listView
+ super.show();
+ ListView listView = getListView();
+
+ // This will force the popupwindow to show upward instead of drop down
+ listView.addOnLayoutChangeListener(mLayoutListener);
+
+ TextView header = (TextView) LayoutInflater.from(mContext).inflate(
+ R.layout.keyguard_bouncer_user_switcher_item, listView, false);
+ header.setText(mContext.getResources().getString(
+ R.string.accessibility_multi_user_switch_switcher));
+ listView.addHeaderView(header);
+
+ listView.setOnTouchListener((v, ev) -> {
+ if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mFalsingCollector.avoidGesture();
+ }
+ return false;
+ });
+ }
+
+ @Override
+ public void dismiss() {
+ getListView().removeOnLayoutChangeListener(mLayoutListener);
+ super.dismiss();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index dc8dc99..79ee746 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -836,6 +836,11 @@
mRootView = notificationShadeWindowView;
}
+ @VisibleForTesting
+ public KeyguardStateController getKeyguardStateController() {
+ return mKeyguardStateController;
+ }
+
public static abstract class BaseUserAdapter extends BaseAdapter {
final UserSwitcherController mController;
@@ -843,7 +848,7 @@
protected BaseUserAdapter(UserSwitcherController controller) {
mController = controller;
- mKeyguardStateController = controller.mKeyguardStateController;
+ mKeyguardStateController = controller.getKeyguardStateController();
controller.addAdapter(new WeakReference<>(this));
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index 030464a..98ce138 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -50,6 +50,7 @@
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.util.settings.GlobalSettings;
import org.junit.Before;
@@ -111,6 +112,8 @@
private FalsingCollector mFalsingCollector;
@Mock
private GlobalSettings mGlobalSettings;
+ @Mock
+ private UserSwitcherController mUserSwitcherController;
private Configuration mConfiguration;
private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
@@ -144,8 +147,8 @@
mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
mKeyguardStateController, mKeyguardSecurityViewFlipperController,
- mConfigurationController, mFalsingCollector, mGlobalSettings)
- .create(mSecurityCallback);
+ mConfigurationController, mFalsingCollector, mUserSwitcherController,
+ mGlobalSettings).create(mSecurityCallback);
}
@Test
@@ -182,13 +185,15 @@
public void onResourcesUpdate_callsThroughOnRotationChange() {
// Rotation is the same, shouldn't cause an update
mKeyguardSecurityContainerController.updateResources();
- verify(mView, never()).initMode(MODE_DEFAULT, mGlobalSettings);
+ verify(mView, never()).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingCollector,
+ mUserSwitcherController);
// Update rotation. Should trigger update
mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
mKeyguardSecurityContainerController.updateResources();
- verify(mView).initMode(MODE_DEFAULT, mGlobalSettings);
+ verify(mView).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingCollector,
+ mUserSwitcherController);
}
private void touchDownLeftSide() {
@@ -245,7 +250,8 @@
.thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
- verify(mView).initMode(MODE_DEFAULT, mGlobalSettings);
+ verify(mView).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingCollector,
+ mUserSwitcherController);
}
@Test
@@ -256,7 +262,8 @@
.thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
- verify(mView).initMode(MODE_ONE_HANDED, mGlobalSettings);
+ verify(mView).initMode(MODE_ONE_HANDED, mGlobalSettings, mFalsingCollector,
+ mUserSwitcherController);
}
@Test
@@ -267,6 +274,7 @@
.thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password);
- verify(mView).initMode(MODE_DEFAULT, mGlobalSettings);
+ verify(mView).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingCollector,
+ mUserSwitcherController);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index c751081..ea7940a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -16,21 +16,27 @@
package com.android.keyguard;
+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.anyInt;
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.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
@@ -39,17 +45,26 @@
import androidx.test.filters.SmallTest;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingCollector;
+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()
@@ -67,28 +82,43 @@
private KeyguardSecurityViewFlipper mSecurityViewFlipper;
@Mock
private GlobalSettings mGlobalSettings;
+ @Mock
+ private FalsingCollector mFalsingCollector;
+ @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();
- FrameLayout.LayoutParams securityViewFlipperLayoutParams = new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+ mSecurityViewFlipperLayoutParams = new FrameLayout.LayoutParams(
+ MATCH_PARENT, MATCH_PARENT);
when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
- when(mSecurityViewFlipper.getLayoutParams()).thenReturn(securityViewFlipperLayoutParams);
+ 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);
}
@Test
public void onMeasure_usesHalfWidthWithOneHandedModeEnabled() {
- mKeyguardSecurityContainer.initMode(MODE_ONE_HANDED, mGlobalSettings);
+ mKeyguardSecurityContainer.initMode(MODE_ONE_HANDED, mGlobalSettings, mFalsingCollector,
+ mUserSwitcherController);
int halfWidthMeasureSpec =
View.MeasureSpec.makeMeasureSpec(SCREEN_WIDTH / 2, View.MeasureSpec.EXACTLY);
@@ -99,7 +129,8 @@
@Test
public void onMeasure_usesFullWidthWithOneHandedModeDisabled() {
- mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings);
+ mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingCollector,
+ mUserSwitcherController);
mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
@@ -110,7 +141,8 @@
int imeInsetAmount = 100;
int systemBarInsetAmount = 10;
- mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings);
+ mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingCollector,
+ mUserSwitcherController);
Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount);
Insets systemBarInset = Insets.of(0, 0, 0, systemBarInsetAmount);
@@ -134,7 +166,8 @@
int imeInsetAmount = 0;
int systemBarInsetAmount = 10;
- mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings);
+ mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingCollector,
+ mUserSwitcherController);
Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount);
Insets systemBarInset = Insets.of(0, 0, 0, systemBarInsetAmount);
@@ -154,7 +187,8 @@
private void setupForUpdateKeyguardPosition(boolean oneHandedMode) {
int mode = oneHandedMode ? MODE_ONE_HANDED : MODE_DEFAULT;
- mKeyguardSecurityContainer.initMode(mode, mGlobalSettings);
+ mKeyguardSecurityContainer.initMode(mode, mGlobalSettings, mFalsingCollector,
+ mUserSwitcherController);
mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
mKeyguardSecurityContainer.layout(0, 0, SCREEN_WIDTH, SCREEN_WIDTH);
@@ -189,4 +223,92 @@
mKeyguardSecurityContainer.updatePositionByTouchX(1f);
verify(mSecurityViewFlipper, never()).setTranslationX(anyInt());
}
+
+ @Test
+ public void testUserSwitcherModeViewGravityLandscape() {
+ // GIVEN one user has been setup and in landscape
+ when(mUserSwitcherController.getUsers()).thenReturn(buildUserRecords(1));
+ Configuration config = new Configuration();
+ config.orientation = Configuration.ORIENTATION_LANDSCAPE;
+ when(getContext().getResources().getConfiguration()).thenReturn(config);
+
+ // WHEN UserSwitcherViewMode is initialized and config has changed
+ setupUserSwitcher();
+ reset(mSecurityViewFlipper);
+ when(mSecurityViewFlipper.getLayoutParams()).thenReturn(mSecurityViewFlipperLayoutParams);
+ mKeyguardSecurityContainer.onConfigurationChanged(config);
+
+ // THEN views are oriented side by side
+ verify(mSecurityViewFlipper).setLayoutParams(mLayoutCaptor.capture());
+ assertThat(mLayoutCaptor.getValue().gravity).isEqualTo(Gravity.RIGHT | Gravity.BOTTOM);
+ ViewGroup userSwitcher = mKeyguardSecurityContainer.findViewById(
+ R.id.keyguard_bouncer_user_switcher);
+ assertThat(((FrameLayout.LayoutParams) userSwitcher.getLayoutParams()).gravity)
+ .isEqualTo(Gravity.LEFT | Gravity.TOP);
+ }
+
+ @Test
+ public void testUserSwitcherModeViewGravityPortrait() {
+ // GIVEN one user has been setup and in landscape
+ when(mUserSwitcherController.getUsers()).thenReturn(buildUserRecords(1));
+ Configuration config = new Configuration();
+ config.orientation = Configuration.ORIENTATION_PORTRAIT;
+ when(getContext().getResources().getConfiguration()).thenReturn(config);
+
+ // WHEN UserSwitcherViewMode is initialized and config has changed
+ setupUserSwitcher();
+ reset(mSecurityViewFlipper);
+ when(mSecurityViewFlipper.getLayoutParams()).thenReturn(mSecurityViewFlipperLayoutParams);
+ mKeyguardSecurityContainer.onConfigurationChanged(config);
+
+ // THEN views are both centered horizontally
+ verify(mSecurityViewFlipper).setLayoutParams(mLayoutCaptor.capture());
+ assertThat(mLayoutCaptor.getValue().gravity).isEqualTo(Gravity.CENTER_HORIZONTAL);
+ ViewGroup userSwitcher = mKeyguardSecurityContainer.findViewById(
+ R.id.keyguard_bouncer_user_switcher);
+ assertThat(((FrameLayout.LayoutParams) userSwitcher.getLayoutParams()).gravity)
+ .isEqualTo(Gravity.CENTER_HORIZONTAL);
+ }
+
+ @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
+ when(mUserSwitcherController.getUsers()).thenReturn(buildUserRecords(2));
+
+ // 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();
+ }
+
+ private void setupUserSwitcher() {
+ mKeyguardSecurityContainer.initMode(KeyguardSecurityContainer.MODE_USER_SWITCHER,
+ mGlobalSettings, mFalsingCollector, mUserSwitcherController);
+ }
+
+ 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 */));
+ }
+ return users;
+ }
}