Merge "Bouncer - User switcher for large screens"
This commit is contained in:
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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">
|
||||
|
||||
|
||||
@@ -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 -->
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -48,10 +48,6 @@ public abstract class KeyguardInputView extends LinearLayout {
|
||||
|
||||
abstract CharSequence getTitle();
|
||||
|
||||
void animateForIme(float interpolatedFraction, boolean appearingAnim) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean disallowInterceptTouch(MotionEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -156,8 +156,7 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView {
|
||||
setAlpha(0f);
|
||||
animate()
|
||||
.alpha(1f)
|
||||
.setDuration(500)
|
||||
.setStartDelay(300)
|
||||
.setDuration(300)
|
||||
.start();
|
||||
|
||||
setTranslationY(0f);
|
||||
@@ -219,15 +218,6 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView {
|
||||
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(
|
||||
|
||||
@@ -23,17 +23,23 @@ import static java.lang.Integer.max;
|
||||
|
||||
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.WindowInsetsAnimation;
|
||||
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 androidx.dynamicanimation.animation.SpringAnimation;
|
||||
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 @@ public class KeyguardSecurityContainer extends FrameLayout {
|
||||
@VisibleForTesting
|
||||
KeyguardSecurityViewFlipper mSecurityViewFlipper;
|
||||
private GlobalSettings mGlobalSettings;
|
||||
private FalsingCollector mFalsingCollector;
|
||||
private UserSwitcherController mUserSwitcherController;
|
||||
private AlertDialog mAlertDialog;
|
||||
private boolean mSwipeUpToRetry;
|
||||
|
||||
@@ -124,7 +140,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
|
||||
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 @@ public class KeyguardSecurityContainer extends FrameLayout {
|
||||
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 class KeyguardSecurityContainer extends FrameLayout {
|
||||
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 @@ public class KeyguardSecurityContainer extends FrameLayout {
|
||||
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 @@ public class KeyguardSecurityContainer extends FrameLayout {
|
||||
|
||||
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 @@ public class KeyguardSecurityContainer extends FrameLayout {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ public class KeyguardSecurityContainer extends FrameLayout {
|
||||
* 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 @@ public class KeyguardSecurityContainer extends FrameLayout {
|
||||
mAlertDialog = null;
|
||||
}
|
||||
mSecurityViewFlipper.setWindowInsetsAnimationCallback(null);
|
||||
mViewMode.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -428,7 +464,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
|
||||
}
|
||||
} else {
|
||||
if (!mIsDragging) {
|
||||
mModeLogic.handleTap(event);
|
||||
mViewMode.handleTap(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -453,8 +489,19 @@ public class KeyguardSecurityContainer extends FrameLayout {
|
||||
.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 class KeyguardSecurityContainer extends FrameLayout {
|
||||
public void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mSecurityViewFlipper = findViewById(R.id.view_flipper);
|
||||
|
||||
finishSetup();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -562,10 +607,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
|
||||
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 @@ public class KeyguardSecurityContainer extends FrameLayout {
|
||||
|
||||
// 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 @@ public class KeyguardSecurityContainer extends FrameLayout {
|
||||
/**
|
||||
* 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 @@ public class KeyguardSecurityContainer extends FrameLayout {
|
||||
/** 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 @@ public class KeyguardSecurityContainer extends FrameLayout {
|
||||
(FrameLayout.LayoutParams) mViewFlipper.getLayoutParams();
|
||||
lp.gravity = Gravity.CENTER_HORIZONTAL;
|
||||
mViewFlipper.setLayoutParams(lp);
|
||||
|
||||
mViewFlipper.setTranslationX(0);
|
||||
}
|
||||
}
|
||||
@@ -691,13 +754,171 @@ public class KeyguardSecurityContainer extends FrameLayout {
|
||||
* 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 @@ public class KeyguardSecurityContainer extends FrameLayout {
|
||||
* 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 @@ public class KeyguardSecurityContainer extends FrameLayout {
|
||||
|
||||
@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;
|
||||
|
||||
@@ -54,6 +54,7 @@ import com.android.systemui.classifier.FalsingCollector;
|
||||
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 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
|
||||
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 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
|
||||
KeyguardSecurityViewFlipperController securityViewFlipperController,
|
||||
ConfigurationController configurationController,
|
||||
FalsingCollector falsingCollector,
|
||||
UserSwitcherController userSwitcherController,
|
||||
GlobalSettings globalSettings) {
|
||||
super(view);
|
||||
mLockPatternUtils = lockPatternUtils;
|
||||
@@ -247,6 +250,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
|
||||
mConfigurationController = configurationController;
|
||||
mLastOrientation = getResources().getConfiguration().orientation;
|
||||
mFalsingCollector = falsingCollector;
|
||||
mUserSwitcherController = userSwitcherController;
|
||||
mGlobalSettings = globalSettings;
|
||||
}
|
||||
|
||||
@@ -343,14 +347,14 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
|
||||
|
||||
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 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
|
||||
}
|
||||
|
||||
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 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
|
||||
private final ConfigurationController mConfigurationController;
|
||||
private final FalsingCollector mFalsingCollector;
|
||||
private final GlobalSettings mGlobalSettings;
|
||||
private final UserSwitcherController mUserSwitcherController;
|
||||
|
||||
@Inject
|
||||
Factory(KeyguardSecurityContainer view,
|
||||
@@ -619,6 +625,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
|
||||
KeyguardSecurityViewFlipperController securityViewFlipperController,
|
||||
ConfigurationController configurationController,
|
||||
FalsingCollector falsingCollector,
|
||||
UserSwitcherController userSwitcherController,
|
||||
GlobalSettings globalSettings) {
|
||||
mView = view;
|
||||
mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory;
|
||||
@@ -632,6 +639,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
|
||||
mConfigurationController = configurationController;
|
||||
mFalsingCollector = falsingCollector;
|
||||
mGlobalSettings = globalSettings;
|
||||
mUserSwitcherController = userSwitcherController;
|
||||
}
|
||||
|
||||
public KeyguardSecurityContainerController create(
|
||||
@@ -640,7 +648,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
|
||||
mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
|
||||
mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
|
||||
mKeyguardStateController, securityCallback, mSecurityViewFlipperController,
|
||||
mConfigurationController, mFalsingCollector, mGlobalSettings);
|
||||
mConfigurationController, mFalsingCollector, mUserSwitcherController,
|
||||
mGlobalSettings);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -83,16 +83,6 @@ public class KeyguardSecurityViewFlipper extends ViewFlipper {
|
||||
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;
|
||||
|
||||
@@ -91,11 +91,6 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView {
|
||||
// noop.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startDisappearAnimation(Runnable finishRunnable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getTitle() {
|
||||
return getContext().getString(
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -836,6 +836,11 @@ public class UserSwitcherController implements Dumpable {
|
||||
mRootView = notificationShadeWindowView;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public KeyguardStateController getKeyguardStateController() {
|
||||
return mKeyguardStateController;
|
||||
}
|
||||
|
||||
public static abstract class BaseUserAdapter extends BaseAdapter {
|
||||
|
||||
final UserSwitcherController mController;
|
||||
@@ -843,7 +848,7 @@ public class UserSwitcherController implements Dumpable {
|
||||
|
||||
protected BaseUserAdapter(UserSwitcherController controller) {
|
||||
mController = controller;
|
||||
mKeyguardStateController = controller.mKeyguardStateController;
|
||||
mKeyguardStateController = controller.getKeyguardStateController();
|
||||
controller.addAdapter(new WeakReference<>(this));
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ import com.android.systemui.SysuiTestCase;
|
||||
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 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
|
||||
private FalsingCollector mFalsingCollector;
|
||||
@Mock
|
||||
private GlobalSettings mGlobalSettings;
|
||||
@Mock
|
||||
private UserSwitcherController mUserSwitcherController;
|
||||
private Configuration mConfiguration;
|
||||
|
||||
private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
|
||||
@@ -144,8 +147,8 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
|
||||
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 class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
|
||||
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 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
|
||||
.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 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
|
||||
.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 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
|
||||
.thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
|
||||
|
||||
mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password);
|
||||
verify(mView).initMode(MODE_DEFAULT, mGlobalSettings);
|
||||
verify(mView).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingCollector,
|
||||
mUserSwitcherController);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 android.widget.FrameLayout;
|
||||
|
||||
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 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
|
||||
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 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
|
||||
|
||||
@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 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
|
||||
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 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
|
||||
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 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
|
||||
|
||||
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 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user