Merge "[Bouncer] Make bouncer view a ConstraintLayout" into tm-qpr-dev

This commit is contained in:
Aaron Liu
2022-10-12 15:51:28 +00:00
committed by Android (Google) Code Review
6 changed files with 425 additions and 412 deletions

View File

@@ -24,7 +24,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
androidprv:layout_maxWidth="@dimen/keyguard_security_width"
androidprv:layout_maxHeight="@dimen/keyguard_security_height"
android:layout_gravity="center_horizontal|bottom"
android:gravity="bottom"
>

View File

@@ -15,6 +15,12 @@
*/
package com.android.keyguard;
import static androidx.constraintlayout.widget.ConstraintSet.BOTTOM;
import static androidx.constraintlayout.widget.ConstraintSet.END;
import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID;
import static androidx.constraintlayout.widget.ConstraintSet.START;
import static androidx.constraintlayout.widget.ConstraintSet.TOP;
import android.annotation.Nullable;
import android.app.admin.IKeyguardCallback;
import android.app.admin.IKeyguardClient;
@@ -30,7 +36,10 @@ import android.util.Log;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.ViewGroup;
import android.view.View;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
@@ -49,7 +58,7 @@ public class AdminSecondaryLockScreenController {
private static final int REMOTE_CONTENT_READY_TIMEOUT_MILLIS = 500;
private final KeyguardUpdateMonitor mUpdateMonitor;
private final Context mContext;
private final ViewGroup mParent;
private final ConstraintLayout mParent;
private AdminSecurityView mView;
private Handler mHandler;
private IKeyguardClient mClient;
@@ -156,6 +165,7 @@ public class AdminSecondaryLockScreenController {
mUpdateMonitor = updateMonitor;
mKeyguardCallback = callback;
mView = new AdminSecurityView(mContext, mSurfaceHolderCallback);
mView.setId(View.generateViewId());
}
/**
@@ -167,6 +177,15 @@ public class AdminSecondaryLockScreenController {
}
if (!mView.isAttachedToWindow()) {
mParent.addView(mView);
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(mParent);
constraintSet.connect(mView.getId(), TOP, PARENT_ID, TOP);
constraintSet.connect(mView.getId(), START, PARENT_ID, START);
constraintSet.connect(mView.getId(), END, PARENT_ID, END);
constraintSet.connect(mView.getId(), BOTTOM, PARENT_ID, BOTTOM);
constraintSet.constrainHeight(mView.getId(), ConstraintSet.MATCH_CONSTRAINT);
constraintSet.constrainWidth(mView.getId(), ConstraintSet.MATCH_CONSTRAINT);
constraintSet.applyTo(mParent);
}
}

View File

@@ -21,15 +21,23 @@ import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
import static androidx.constraintlayout.widget.ConstraintSet.BOTTOM;
import static androidx.constraintlayout.widget.ConstraintSet.CHAIN_SPREAD;
import static androidx.constraintlayout.widget.ConstraintSet.END;
import static androidx.constraintlayout.widget.ConstraintSet.LEFT;
import static androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT;
import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID;
import static androidx.constraintlayout.widget.ConstraintSet.RIGHT;
import static androidx.constraintlayout.widget.ConstraintSet.START;
import static androidx.constraintlayout.widget.ConstraintSet.TOP;
import static androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT;
import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY;
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.app.admin.DevicePolicyManager;
@@ -44,12 +52,12 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.UserManager;
import android.provider.Settings;
import android.transition.TransitionManager;
import android.util.AttributeSet;
import android.util.Log;
import android.util.MathUtils;
import android.util.TypedValue;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.VelocityTracker;
@@ -59,8 +67,6 @@ import android.view.ViewGroup;
import android.view.WindowInsets;
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;
@@ -68,8 +74,9 @@ import android.widget.TextView;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.SpringAnimation;
@@ -92,9 +99,9 @@ import com.android.systemui.util.settings.GlobalSettings;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
public class KeyguardSecurityContainer extends FrameLayout {
/** Determines how the bouncer is displayed to the user. */
public class KeyguardSecurityContainer extends ConstraintLayout {
static final int USER_TYPE_PRIMARY = 1;
static final int USER_TYPE_WORK_PROFILE = 2;
static final int USER_TYPE_SECONDARY_USER = 3;
@@ -125,15 +132,6 @@ public class KeyguardSecurityContainer extends FrameLayout {
// How much to scale the default slop by, to avoid accidental drags.
private static final float SLOP_SCALE = 4f;
private static final long IME_DISAPPEAR_DURATION_MS = 125;
// The duration of the animation to switch security sides.
private static final long SECURITY_SHIFT_ANIMATION_DURATION_MS = 500;
// How much of the switch sides animation should be dedicated to fading the security out. The
// remainder will fade it back in again.
private static final float SECURITY_SHIFT_ANIMATION_FADE_OUT_PROPORTION = 0.2f;
@VisibleForTesting
KeyguardSecurityViewFlipper mSecurityViewFlipper;
private GlobalSettings mGlobalSettings;
@@ -648,48 +646,9 @@ public class KeyguardSecurityContainer extends FrameLayout {
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
for (int i = 0; i < getChildCount(); i++) {
final View view = getChildAt(i);
if (view.getVisibility() != GONE) {
int updatedWidthMeasureSpec = mViewMode.getChildWidthMeasureSpec(widthMeasureSpec);
final LayoutParams lp = (LayoutParams) view.getLayoutParams();
// When using EXACTLY spec, measure will use the layout width if > 0. Set before
// measuring the child
lp.width = MeasureSpec.getSize(updatedWidthMeasureSpec);
measureChildWithMargins(view, updatedWidthMeasureSpec, 0,
heightMeasureSpec, 0);
maxWidth = Math.max(maxWidth,
view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight,
view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
childState = combineMeasuredStates(childState, view.getMeasuredState());
}
}
maxWidth += getPaddingLeft() + getPaddingRight();
maxHeight += getPaddingTop() + getPaddingBottom();
// Check against our minimum height and width
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
int width = right - left;
if (changed && mWidth != width) {
mWidth = width;
@@ -761,7 +720,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
* Enscapsulates the differences between bouncer modes for the container.
*/
interface ViewMode {
default void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
default void init(@NonNull ConstraintLayout v, @NonNull GlobalSettings globalSettings,
@NonNull KeyguardSecurityViewFlipper viewFlipper,
@NonNull FalsingManager falsingManager,
@NonNull UserSwitcherController userSwitcherController) {};
@@ -787,11 +746,6 @@ public class KeyguardSecurityContainer extends FrameLayout {
/** 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;
}
/** Called when we are setting a new ViewMode */
default void onDestroy() {};
}
@@ -801,13 +755,12 @@ public class KeyguardSecurityContainer extends FrameLayout {
* screen devices
*/
abstract static class SidedSecurityMode implements ViewMode {
@Nullable private ValueAnimator mRunningSecurityShiftAnimator;
private KeyguardSecurityViewFlipper mViewFlipper;
private ViewGroup mView;
private ConstraintLayout mView;
private GlobalSettings mGlobalSettings;
private int mDefaultSideSetting;
public void init(ViewGroup v, KeyguardSecurityViewFlipper viewFlipper,
public void init(ConstraintLayout v, KeyguardSecurityViewFlipper viewFlipper,
GlobalSettings globalSettings, boolean leftAlignedByDefault) {
mView = v;
mViewFlipper = viewFlipper;
@@ -850,127 +803,6 @@ public class KeyguardSecurityContainer extends FrameLayout {
protected abstract void updateSecurityViewLocation(boolean leftAlign, boolean animate);
protected void translateSecurityViewLocation(boolean leftAlign, boolean animate) {
translateSecurityViewLocation(leftAlign, animate, i -> {});
}
/**
* Moves the inner security view to the correct location with animation. This is triggered
* when the user double taps on the side of the screen that is not currently occupied by
* the security view.
*/
protected void translateSecurityViewLocation(boolean leftAlign, boolean animate,
Consumer<Float> securityAlphaListener) {
if (mRunningSecurityShiftAnimator != null) {
mRunningSecurityShiftAnimator.cancel();
mRunningSecurityShiftAnimator = null;
}
int targetTranslation = leftAlign
? 0 : mView.getMeasuredWidth() - mViewFlipper.getWidth();
if (animate) {
// This animation is a bit fun to implement. The bouncer needs to move, and fade
// in/out at the same time. The issue is, the bouncer should only move a short
// amount (120dp or so), but obviously needs to go from one side of the screen to
// the other. This needs a pretty custom animation.
//
// This works as follows. It uses a ValueAnimation to simply drive the animation
// progress. This animator is responsible for both the translation of the bouncer,
// and the current fade. It will fade the bouncer out while also moving it along the
// 120dp path. Once the bouncer is fully faded out though, it will "snap" the
// bouncer closer to its destination, then fade it back in again. The effect is that
// the bouncer will move from 0 -> X while fading out, then
// (destination - X) -> destination while fading back in again.
// TODO(b/208250221): Make this animation properly abortable.
Interpolator positionInterpolator = AnimationUtils.loadInterpolator(
mView.getContext(), android.R.interpolator.fast_out_extra_slow_in);
Interpolator fadeOutInterpolator = Interpolators.FAST_OUT_LINEAR_IN;
Interpolator fadeInInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
mRunningSecurityShiftAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
mRunningSecurityShiftAnimator.setDuration(SECURITY_SHIFT_ANIMATION_DURATION_MS);
mRunningSecurityShiftAnimator.setInterpolator(Interpolators.LINEAR);
int initialTranslation = (int) mViewFlipper.getTranslationX();
int totalTranslation = (int) mView.getResources().getDimension(
R.dimen.security_shift_animation_translation);
final boolean shouldRestoreLayerType = mViewFlipper.hasOverlappingRendering()
&& mViewFlipper.getLayerType() != View.LAYER_TYPE_HARDWARE;
if (shouldRestoreLayerType) {
mViewFlipper.setLayerType(View.LAYER_TYPE_HARDWARE, /* paint= */null);
}
float initialAlpha = mViewFlipper.getAlpha();
mRunningSecurityShiftAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mRunningSecurityShiftAnimator = null;
}
});
mRunningSecurityShiftAnimator.addUpdateListener(animation -> {
float switchPoint = SECURITY_SHIFT_ANIMATION_FADE_OUT_PROPORTION;
boolean isFadingOut = animation.getAnimatedFraction() < switchPoint;
int currentTranslation = (int) (positionInterpolator.getInterpolation(
animation.getAnimatedFraction()) * totalTranslation);
int translationRemaining = totalTranslation - currentTranslation;
// Flip the sign if we're going from right to left.
if (leftAlign) {
currentTranslation = -currentTranslation;
translationRemaining = -translationRemaining;
}
float opacity;
if (isFadingOut) {
// The bouncer fades out over the first X%.
float fadeOutFraction = MathUtils.constrainedMap(
/* rangeMin= */1.0f,
/* rangeMax= */0.0f,
/* valueMin= */0.0f,
/* valueMax= */switchPoint,
animation.getAnimatedFraction());
opacity = fadeOutInterpolator.getInterpolation(fadeOutFraction);
// When fading out, the alpha needs to start from the initial opacity of the
// view flipper, otherwise we get a weird bit of jank as it ramps back to
// 100%.
mViewFlipper.setAlpha(opacity * initialAlpha);
// Animate away from the source.
mViewFlipper.setTranslationX(initialTranslation + currentTranslation);
} else {
// And in again over the remaining (100-X)%.
float fadeInFraction = MathUtils.constrainedMap(
/* rangeMin= */0.0f,
/* rangeMax= */1.0f,
/* valueMin= */switchPoint,
/* valueMax= */1.0f,
animation.getAnimatedFraction());
opacity = fadeInInterpolator.getInterpolation(fadeInFraction);
mViewFlipper.setAlpha(opacity);
// Fading back in, animate towards the destination.
mViewFlipper.setTranslationX(targetTranslation - translationRemaining);
}
securityAlphaListener.accept(opacity);
if (animation.getAnimatedFraction() == 1.0f && shouldRestoreLayerType) {
mViewFlipper.setLayerType(View.LAYER_TYPE_NONE, /* paint= */null);
}
});
mRunningSecurityShiftAnimator.start();
} else {
mViewFlipper.setTranslationX(targetTranslation);
}
}
boolean isLeftAligned() {
return mGlobalSettings.getInt(Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
mDefaultSideSetting)
@@ -989,11 +821,11 @@ public class KeyguardSecurityContainer extends FrameLayout {
* Default bouncer is centered within the space
*/
static class DefaultViewMode implements ViewMode {
private ViewGroup mView;
private ConstraintLayout mView;
private KeyguardSecurityViewFlipper mViewFlipper;
@Override
public void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
public void init(@NonNull ConstraintLayout v, @NonNull GlobalSettings globalSettings,
@NonNull KeyguardSecurityViewFlipper viewFlipper,
@NonNull FalsingManager falsingManager,
@NonNull UserSwitcherController userSwitcherController) {
@@ -1005,11 +837,14 @@ public class KeyguardSecurityContainer extends FrameLayout {
}
private void updateSecurityViewGroup() {
FrameLayout.LayoutParams lp =
(FrameLayout.LayoutParams) mViewFlipper.getLayoutParams();
lp.gravity = Gravity.CENTER_HORIZONTAL;
mViewFlipper.setLayoutParams(lp);
mViewFlipper.setTranslationX(0);
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.connect(mViewFlipper.getId(), START, PARENT_ID, START);
constraintSet.connect(mViewFlipper.getId(), END, PARENT_ID, END);
constraintSet.connect(mViewFlipper.getId(), BOTTOM, PARENT_ID, BOTTOM);
constraintSet.connect(mViewFlipper.getId(), TOP, PARENT_ID, TOP);
constraintSet.constrainHeight(mViewFlipper.getId(), MATCH_CONSTRAINT);
constraintSet.constrainWidth(mViewFlipper.getId(), MATCH_CONSTRAINT);
constraintSet.applyTo(mView);
}
}
@@ -1018,7 +853,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
* a user switcher, in both portrait and landscape modes.
*/
static class UserSwitcherViewMode extends SidedSecurityMode {
private ViewGroup mView;
private ConstraintLayout mView;
private ViewGroup mUserSwitcherViewGroup;
private KeyguardSecurityViewFlipper mViewFlipper;
private TextView mUserSwitcher;
@@ -1029,11 +864,8 @@ public class KeyguardSecurityContainer extends FrameLayout {
private UserSwitcherController.UserSwitchCallback mUserSwitchCallback =
this::setupUserSwitcher;
private float mAnimationLastAlpha = 1f;
private boolean mAnimationWaitsToShift = true;
@Override
public void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
public void init(@NonNull ConstraintLayout v, @NonNull GlobalSettings globalSettings,
@NonNull KeyguardSecurityViewFlipper viewFlipper,
@NonNull FalsingManager falsingManager,
@NonNull UserSwitcherController userSwitcherController) {
@@ -1240,88 +1072,55 @@ public class KeyguardSecurityContainer extends FrameLayout {
});
}
/**
* 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.EXACTLY);
}
@Override
public void updateSecurityViewLocation() {
updateSecurityViewLocation(isLeftAligned(), /* animate= */false);
}
public void updateSecurityViewLocation(boolean leftAlign, boolean animate) {
setYTranslation();
setGravity();
setXTranslation(leftAlign, animate);
}
private void setXTranslation(boolean leftAlign, boolean animate) {
if (mResources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
mUserSwitcherViewGroup.setTranslationX(0);
mViewFlipper.setTranslationX(0);
} else {
int switcherTargetTranslation = leftAlign
? mView.getMeasuredWidth() - mViewFlipper.getWidth() : 0;
if (animate) {
mAnimationWaitsToShift = true;
mAnimationLastAlpha = 1f;
translateSecurityViewLocation(leftAlign, animate, securityAlpha -> {
// During the animation security view fades out - alpha goes from 1 to
// (almost) 0 - and then fades in - alpha grows back to 1.
// If new alpha is bigger than previous one it means we're at inflection
// point and alpha is zero or almost zero. That's when we want to do
// translation of user switcher, so that it's not visible to the user.
boolean fullyFadeOut = securityAlpha == 0.0f
|| securityAlpha > mAnimationLastAlpha;
if (fullyFadeOut && mAnimationWaitsToShift) {
mUserSwitcherViewGroup.setTranslationX(switcherTargetTranslation);
mAnimationWaitsToShift = false;
}
mUserSwitcherViewGroup.setAlpha(securityAlpha);
mAnimationLastAlpha = securityAlpha;
});
} else {
translateSecurityViewLocation(leftAlign, animate);
mUserSwitcherViewGroup.setTranslationX(switcherTargetTranslation);
}
if (animate) {
TransitionManager.beginDelayedTransition(mView,
new KeyguardSecurityViewTransition());
}
}
private void setGravity() {
if (mResources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
updateViewGravity(mUserSwitcherViewGroup, Gravity.CENTER_HORIZONTAL);
updateViewGravity(mViewFlipper, Gravity.CENTER_HORIZONTAL);
} else {
// horizontal gravity is the same because we translate these views anyway
updateViewGravity(mViewFlipper, Gravity.LEFT | Gravity.BOTTOM);
updateViewGravity(mUserSwitcherViewGroup, Gravity.LEFT | Gravity.CENTER_VERTICAL);
}
}
private void setYTranslation() {
int yTrans = mResources.getDimensionPixelSize(R.dimen.bouncer_user_switcher_y_trans);
if (mResources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
mUserSwitcherViewGroup.setTranslationY(yTrans);
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.connect(mUserSwitcherViewGroup.getId(), TOP, PARENT_ID, TOP, yTrans);
constraintSet.connect(mViewFlipper.getId(), TOP, PARENT_ID, TOP);
constraintSet.connect(mViewFlipper.getId(), BOTTOM, PARENT_ID, BOTTOM);
constraintSet.centerHorizontally(mViewFlipper.getId(), PARENT_ID);
constraintSet.centerHorizontally(mUserSwitcherViewGroup.getId(), PARENT_ID);
constraintSet.setVerticalChainStyle(mViewFlipper.getId(), CHAIN_SPREAD);
constraintSet.setVerticalChainStyle(mUserSwitcherViewGroup.getId(), CHAIN_SPREAD);
constraintSet.constrainHeight(mUserSwitcherViewGroup.getId(), WRAP_CONTENT);
constraintSet.constrainWidth(mUserSwitcherViewGroup.getId(), WRAP_CONTENT);
constraintSet.constrainHeight(mViewFlipper.getId(), MATCH_CONSTRAINT);
constraintSet.applyTo(mView);
} else {
// Attempt to reposition a bit higher to make up for this frame being a bit lower
// on the device
mUserSwitcherViewGroup.setTranslationY(-yTrans);
mViewFlipper.setTranslationY(0);
}
}
int leftElement = leftAlign ? mViewFlipper.getId() : mUserSwitcherViewGroup.getId();
int rightElement =
leftAlign ? mUserSwitcherViewGroup.getId() : mViewFlipper.getId();
private void updateViewGravity(View v, int gravity) {
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams();
lp.gravity = gravity;
v.setLayoutParams(lp);
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.connect(leftElement, LEFT, PARENT_ID, LEFT);
constraintSet.connect(leftElement, RIGHT, rightElement, LEFT);
constraintSet.connect(rightElement, LEFT, leftElement, RIGHT);
constraintSet.connect(rightElement, RIGHT, PARENT_ID, RIGHT);
constraintSet.connect(mUserSwitcherViewGroup.getId(), TOP, PARENT_ID, TOP);
constraintSet.connect(mUserSwitcherViewGroup.getId(), BOTTOM, PARENT_ID, BOTTOM,
yTrans);
constraintSet.connect(mViewFlipper.getId(), TOP, PARENT_ID, TOP);
constraintSet.connect(mViewFlipper.getId(), BOTTOM, PARENT_ID, BOTTOM);
constraintSet.setHorizontalChainStyle(mUserSwitcherViewGroup.getId(), CHAIN_SPREAD);
constraintSet.setHorizontalChainStyle(mViewFlipper.getId(), CHAIN_SPREAD);
constraintSet.constrainHeight(mUserSwitcherViewGroup.getId(),
MATCH_CONSTRAINT);
constraintSet.constrainWidth(mUserSwitcherViewGroup.getId(),
MATCH_CONSTRAINT);
constraintSet.constrainWidth(mViewFlipper.getId(), MATCH_CONSTRAINT);
constraintSet.constrainHeight(mViewFlipper.getId(), MATCH_CONSTRAINT);
constraintSet.applyTo(mView);
}
}
}
@@ -1330,11 +1129,11 @@ public class KeyguardSecurityContainer extends FrameLayout {
* between alternate sides of the display.
*/
static class OneHandedViewMode extends SidedSecurityMode {
private ViewGroup mView;
private ConstraintLayout mView;
private KeyguardSecurityViewFlipper mViewFlipper;
@Override
public void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
public void init(@NonNull ConstraintLayout v, @NonNull GlobalSettings globalSettings,
@NonNull KeyguardSecurityViewFlipper viewFlipper,
@NonNull FalsingManager falsingManager,
@NonNull UserSwitcherController userSwitcherController) {
@@ -1342,27 +1141,9 @@ public class KeyguardSecurityContainer extends FrameLayout {
mView = v;
mViewFlipper = viewFlipper;
updateSecurityViewGravity();
updateSecurityViewLocation(isLeftAligned(), /* animate= */false);
}
/**
* One-handed mode contains the child to half of the available space.
*/
@Override
public int getChildWidthMeasureSpec(int parentWidthMeasureSpec) {
return MeasureSpec.makeMeasureSpec(
MeasureSpec.getSize(parentWidthMeasureSpec) / 2,
MeasureSpec.EXACTLY);
}
private void updateSecurityViewGravity() {
FrameLayout.LayoutParams lp =
(FrameLayout.LayoutParams) mViewFlipper.getLayoutParams();
lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
mViewFlipper.setLayoutParams(lp);
}
/**
* Moves the bouncer to align with a tap (most likely in the shade), so the bouncer
* appears on the same side as a touch.
@@ -1380,7 +1161,20 @@ public class KeyguardSecurityContainer extends FrameLayout {
}
protected void updateSecurityViewLocation(boolean leftAlign, boolean animate) {
translateSecurityViewLocation(leftAlign, animate);
if (animate) {
TransitionManager.beginDelayedTransition(mView,
new KeyguardSecurityViewTransition());
}
ConstraintSet constraintSet = new ConstraintSet();
if (leftAlign) {
constraintSet.connect(mViewFlipper.getId(), LEFT, PARENT_ID, LEFT);
} else {
constraintSet.connect(mViewFlipper.getId(), RIGHT, PARENT_ID, RIGHT);
}
constraintSet.connect(mViewFlipper.getId(), TOP, PARENT_ID, TOP);
constraintSet.connect(mViewFlipper.getId(), BOTTOM, PARENT_ID, BOTTOM);
constraintSet.constrainPercentWidth(mViewFlipper.getId(), 0.5f);
constraintSet.applyTo(mView);
}
}
}

View File

@@ -0,0 +1,210 @@
/*
* Copyright (C) 2022 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.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.graphics.Rect
import android.transition.Transition
import android.transition.TransitionValues
import android.util.MathUtils
import android.view.View
import android.view.ViewGroup
import android.view.animation.AnimationUtils
import com.android.internal.R.interpolator.fast_out_extra_slow_in
import com.android.systemui.R
import com.android.systemui.animation.Interpolators
/** Animates constraint layout changes for the security view. */
class KeyguardSecurityViewTransition : Transition() {
companion object {
const val PROP_BOUNDS = "securityViewLocation:bounds"
// The duration of the animation to switch security sides.
const val SECURITY_SHIFT_ANIMATION_DURATION_MS = 500L
// How much of the switch sides animation should be dedicated to fading the security out.
// The remainder will fade it back in again.
const val SECURITY_SHIFT_ANIMATION_FADE_OUT_PROPORTION = 0.2f
}
private fun captureValues(values: TransitionValues) {
val boundsRect = Rect()
boundsRect.left = values.view.left
boundsRect.top = values.view.top
boundsRect.right = values.view.right
boundsRect.bottom = values.view.bottom
values.values[PROP_BOUNDS] = boundsRect
}
override fun getTransitionProperties(): Array<String>? {
return arrayOf(PROP_BOUNDS)
}
override fun captureEndValues(transitionValues: TransitionValues?) {
transitionValues?.let { captureValues(it) }
}
override fun captureStartValues(transitionValues: TransitionValues?) {
transitionValues?.let { captureValues(it) }
}
override fun createAnimator(
sceneRoot: ViewGroup?,
startValues: TransitionValues?,
endValues: TransitionValues?
): Animator? {
if (sceneRoot == null || startValues == null || endValues == null) {
return null
}
// This animation is a bit fun to implement. The bouncer needs to move, and fade
// in/out at the same time. The issue is, the bouncer should only move a short
// amount (120dp or so), but obviously needs to go from one side of the screen to
// the other. This needs a pretty custom animation.
//
// This works as follows. It uses a ValueAnimation to simply drive the animation
// progress. This animator is responsible for both the translation of the bouncer,
// and the current fade. It will fade the bouncer out while also moving it along the
// 120dp path. Once the bouncer is fully faded out though, it will "snap" the
// bouncer closer to its destination, then fade it back in again. The effect is that
// the bouncer will move from 0 -> X while fading out, then
// (destination - X) -> destination while fading back in again.
// TODO(b/208250221): Make this animation properly abortable.
val positionInterpolator =
AnimationUtils.loadInterpolator(sceneRoot.context, fast_out_extra_slow_in)
val fadeOutInterpolator = Interpolators.FAST_OUT_LINEAR_IN
val fadeInInterpolator = Interpolators.LINEAR_OUT_SLOW_IN
var runningSecurityShiftAnimator = ValueAnimator.ofFloat(0.0f, 1.0f)
runningSecurityShiftAnimator.duration = SECURITY_SHIFT_ANIMATION_DURATION_MS
runningSecurityShiftAnimator.interpolator = Interpolators.LINEAR
val startRect = startValues.values[PROP_BOUNDS] as Rect
val endRect = endValues.values[PROP_BOUNDS] as Rect
val v = startValues.view
val totalTranslation: Int =
sceneRoot.resources.getDimension(R.dimen.security_shift_animation_translation).toInt()
val shouldRestoreLayerType =
(v.hasOverlappingRendering() && v.layerType != View.LAYER_TYPE_HARDWARE)
if (shouldRestoreLayerType) {
v.setLayerType(View.LAYER_TYPE_HARDWARE, /* paint= */ null)
}
val initialAlpha: Float = v.alpha
runningSecurityShiftAnimator.addListener(
object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
runningSecurityShiftAnimator = null
}
}
)
var finishedFadingOutNonSecurityView = false
runningSecurityShiftAnimator.addUpdateListener { animation: ValueAnimator ->
val switchPoint = SECURITY_SHIFT_ANIMATION_FADE_OUT_PROPORTION
val isFadingOut = animation.animatedFraction < switchPoint
val opacity: Float
var currentTranslation =
(positionInterpolator.getInterpolation(animation.animatedFraction) *
totalTranslation)
.toInt()
var translationRemaining = totalTranslation - currentTranslation
val leftAlign = endRect.left < startRect.left
if (leftAlign) {
currentTranslation = -currentTranslation
translationRemaining = -translationRemaining
}
if (isFadingOut) {
// The bouncer fades out over the first X%.
val fadeOutFraction =
MathUtils.constrainedMap(
/* rangeMin= */ 1.0f,
/* rangeMax= */ 0.0f,
/* valueMin= */ 0.0f,
/* valueMax= */ switchPoint,
animation.animatedFraction
)
opacity = fadeOutInterpolator.getInterpolation(fadeOutFraction)
// When fading out, the alpha needs to start from the initial opacity of the
// view flipper, otherwise we get a weird bit of jank as it ramps back to
// 100%.
v.alpha = opacity * initialAlpha
if (v is KeyguardSecurityViewFlipper) {
v.setLeftTopRightBottom(
startRect.left + currentTranslation,
startRect.top,
startRect.right + currentTranslation,
startRect.bottom
)
}
} else {
// And in again over the remaining (100-X)%.
val fadeInFraction =
MathUtils.constrainedMap(
/* rangeMin= */ 0.0f,
/* rangeMax= */ 1.0f,
/* valueMin= */ switchPoint,
/* valueMax= */ 1.0f,
animation.animatedFraction
)
opacity = fadeInInterpolator.getInterpolation(fadeInFraction)
v.alpha = opacity
// Fading back in, animate towards the destination.
if (v is KeyguardSecurityViewFlipper) {
v.setLeftTopRightBottom(
endRect.left - translationRemaining,
endRect.top,
endRect.right - translationRemaining,
endRect.bottom
)
}
}
if (animation.animatedFraction == 1.0f && shouldRestoreLayerType) {
v.setLayerType(View.LAYER_TYPE_NONE, /* paint= */ null)
}
// For views that are not the security view flipper, we do not want to apply
// an x translation animation. Instead, we want to fade out, move to final position and
// then fade in.
if (v !is KeyguardSecurityViewFlipper) {
// Opacity goes close to 0 but does not fully get to 0.
if (opacity - 0.001f < 0f) {
v.setLeftTopRightBottom(
endRect.left,
endRect.top,
endRect.right,
endRect.bottom
)
finishedFadingOutNonSecurityView = true
} else if (!finishedFadingOutNonSecurityView) {
v.setLeftTopRightBottom(
startRect.left,
startRect.top,
startRect.right,
startRect.bottom
)
}
}
}
runningSecurityShiftAnimator.start()
return runningSecurityShiftAnimator
}
}

View File

@@ -41,6 +41,7 @@ import android.testing.TestableLooper.RunWithLooper;
import android.testing.ViewUtils;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceView;
import android.view.View;
import androidx.test.filters.SmallTest;
@@ -84,6 +85,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mKeyguardSecurityContainer = spy(new KeyguardSecurityContainer(mContext));
mKeyguardSecurityContainer.setId(View.generateViewId());
ViewUtils.attachView(mKeyguardSecurityContainer);
mTestableLooper = TestableLooper.get(this);

View File

@@ -21,10 +21,14 @@ import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.provider.Settings.Global.ONE_HANDED_KEYGUARD_SIDE;
import static android.provider.Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT;
import static android.provider.Settings.Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
import static androidx.constraintlayout.widget.ConstraintSet.CHAIN_SPREAD;
import static androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT;
import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID;
import static androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT;
import static com.android.keyguard.KeyguardSecurityContainer.MODE_DEFAULT;
import static com.android.keyguard.KeyguardSecurityContainer.MODE_ONE_HANDED;
@@ -32,9 +36,6 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -43,19 +44,17 @@ import android.content.res.Configuration;
import android.graphics.Insets;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.widget.FrameLayout;
import androidx.constraintlayout.widget.ConstraintSet;
import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.user.data.source.UserRecord;
import com.android.systemui.util.settings.GlobalSettings;
@@ -64,8 +63,6 @@ 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;
@@ -78,14 +75,11 @@ import java.util.ArrayList;
public class KeyguardSecurityContainerTest extends SysuiTestCase {
private static final int VIEW_WIDTH = 1600;
private int mScreenWidth;
private int mFakeMeasureSpec;
private static final int VIEW_HEIGHT = 900;
@Rule
public MockitoRule mRule = MockitoJUnit.rule();
@Mock
private KeyguardSecurityViewFlipper mSecurityViewFlipper;
@Mock
private GlobalSettings mGlobalSettings;
@@ -93,59 +87,32 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
private FalsingManager mFalsingManager;
@Mock
private UserSwitcherController mUserSwitcherController;
@Mock
private KeyguardStateController mKeyguardStateController;
@Captor
private ArgumentCaptor<FrameLayout.LayoutParams> mLayoutCaptor;
private KeyguardSecurityContainer mKeyguardSecurityContainer;
private FrameLayout.LayoutParams mSecurityViewFlipperLayoutParams;
@Before
public void setup() {
// Needed here, otherwise when mKeyguardSecurityContainer is created below, it'll cache
// the real references (rather than the TestableResources that this call creates).
mContext.ensureTestableResources();
mSecurityViewFlipperLayoutParams = new FrameLayout.LayoutParams(
MATCH_PARENT, MATCH_PARENT);
when(mSecurityViewFlipper.getLayoutParams()).thenReturn(mSecurityViewFlipperLayoutParams);
mSecurityViewFlipper = new KeyguardSecurityViewFlipper(getContext());
mSecurityViewFlipper.setId(View.generateViewId());
mKeyguardSecurityContainer = new KeyguardSecurityContainer(getContext());
mKeyguardSecurityContainer.setRight(VIEW_WIDTH);
mKeyguardSecurityContainer.setLeft(0);
mKeyguardSecurityContainer.setTop(0);
mKeyguardSecurityContainer.setBottom(VIEW_HEIGHT);
mKeyguardSecurityContainer.setId(View.generateViewId());
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.isKeyguardShowing()).thenReturn(true);
mScreenWidth = getUiDevice().getDisplayWidth();
mFakeMeasureSpec = View
.MeasureSpec.makeMeasureSpec(mScreenWidth, View.MeasureSpec.EXACTLY);
}
@Test
public void onMeasure_usesHalfWidthWithOneHandedModeEnabled() {
mKeyguardSecurityContainer.initMode(MODE_ONE_HANDED, mGlobalSettings, mFalsingManager,
mUserSwitcherController);
int halfWidthMeasureSpec =
View.MeasureSpec.makeMeasureSpec(mScreenWidth / 2, View.MeasureSpec.EXACTLY);
mKeyguardSecurityContainer.onMeasure(mFakeMeasureSpec, mFakeMeasureSpec);
verify(mSecurityViewFlipper).measure(halfWidthMeasureSpec, mFakeMeasureSpec);
}
@Test
public void onMeasure_usesFullWidthWithOneHandedModeDisabled() {
mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
mUserSwitcherController);
mKeyguardSecurityContainer.measure(mFakeMeasureSpec, mFakeMeasureSpec);
verify(mSecurityViewFlipper).measure(mFakeMeasureSpec, mFakeMeasureSpec);
}
@Test
public void onMeasure_respectsViewInsets() {
public void testOnApplyWindowInsets() {
int paddingBottom = getContext().getResources()
.getDimensionPixelSize(R.dimen.keyguard_security_view_bottom_margin);
int imeInsetAmount = paddingBottom + 1;
@@ -162,17 +129,12 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
.setInsetsIgnoringVisibility(systemBars(), systemBarInset)
.build();
// It's reduced by the max of the systembar and IME, so just subtract IME inset.
int expectedHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec(
mScreenWidth - imeInsetAmount, View.MeasureSpec.EXACTLY);
mKeyguardSecurityContainer.onApplyWindowInsets(insets);
mKeyguardSecurityContainer.measure(mFakeMeasureSpec, mFakeMeasureSpec);
verify(mSecurityViewFlipper).measure(mFakeMeasureSpec, expectedHeightMeasureSpec);
assertThat(mKeyguardSecurityContainer.getPaddingBottom()).isEqualTo(imeInsetAmount);
}
@Test
public void onMeasure_respectsViewInsets_largerSystembar() {
public void testOnApplyWindowInsets_largerSystembar() {
int imeInsetAmount = 0;
int paddingBottom = getContext().getResources()
.getDimensionPixelSize(R.dimen.keyguard_security_view_bottom_margin);
@@ -189,25 +151,22 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
.setInsetsIgnoringVisibility(systemBars(), systemBarInset)
.build();
int expectedHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec(
mScreenWidth - systemBarInsetAmount, View.MeasureSpec.EXACTLY);
mKeyguardSecurityContainer.onApplyWindowInsets(insets);
mKeyguardSecurityContainer.measure(mFakeMeasureSpec, mFakeMeasureSpec);
verify(mSecurityViewFlipper).measure(mFakeMeasureSpec, expectedHeightMeasureSpec);
assertThat(mKeyguardSecurityContainer.getPaddingBottom()).isEqualTo(systemBarInsetAmount);
}
private void setupForUpdateKeyguardPosition(boolean oneHandedMode) {
int mode = oneHandedMode ? MODE_ONE_HANDED : MODE_DEFAULT;
mKeyguardSecurityContainer.initMode(mode, mGlobalSettings, mFalsingManager,
@Test
public void testDefaultViewMode() {
mKeyguardSecurityContainer.initMode(MODE_ONE_HANDED, mGlobalSettings, mFalsingManager,
mUserSwitcherController);
mKeyguardSecurityContainer.measure(mFakeMeasureSpec, mFakeMeasureSpec);
mKeyguardSecurityContainer.layout(0, 0, mScreenWidth, mScreenWidth);
// Clear any interactions with the mock so we know the interactions definitely come from the
// below testing.
reset(mSecurityViewFlipper);
mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
mUserSwitcherController);
ConstraintSet.Constraint viewFlipperConstraint =
getViewConstraint(mSecurityViewFlipper.getId());
assertThat(viewFlipperConstraint.layout.topToTop).isEqualTo(PARENT_ID);
assertThat(viewFlipperConstraint.layout.startToStart).isEqualTo(PARENT_ID);
assertThat(viewFlipperConstraint.layout.endToEnd).isEqualTo(PARENT_ID);
assertThat(viewFlipperConstraint.layout.bottomToBottom).isEqualTo(PARENT_ID);
}
@Test
@@ -217,13 +176,22 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
mKeyguardSecurityContainer.getWidth() - 1f);
verify(mGlobalSettings).putInt(ONE_HANDED_KEYGUARD_SIDE, ONE_HANDED_KEYGUARD_SIDE_RIGHT);
assertSecurityTranslationX(
mKeyguardSecurityContainer.getWidth() - mSecurityViewFlipper.getWidth());
ConstraintSet.Constraint viewFlipperConstraint =
getViewConstraint(mSecurityViewFlipper.getId());
assertThat(viewFlipperConstraint.layout.widthPercent).isEqualTo(0.5f);
assertThat(viewFlipperConstraint.layout.topToTop).isEqualTo(PARENT_ID);
assertThat(viewFlipperConstraint.layout.rightToRight).isEqualTo(PARENT_ID);
assertThat(viewFlipperConstraint.layout.leftToLeft).isEqualTo(-1);
mKeyguardSecurityContainer.updatePositionByTouchX(1f);
verify(mGlobalSettings).putInt(ONE_HANDED_KEYGUARD_SIDE, ONE_HANDED_KEYGUARD_SIDE_LEFT);
verify(mSecurityViewFlipper).setTranslationX(0.0f);
viewFlipperConstraint =
getViewConstraint(mSecurityViewFlipper.getId());
assertThat(viewFlipperConstraint.layout.widthPercent).isEqualTo(0.5f);
assertThat(viewFlipperConstraint.layout.topToTop).isEqualTo(PARENT_ID);
assertThat(viewFlipperConstraint.layout.leftToLeft).isEqualTo(PARENT_ID);
assertThat(viewFlipperConstraint.layout.rightToRight).isEqualTo(-1);
}
@Test
@@ -232,10 +200,16 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
mKeyguardSecurityContainer.updatePositionByTouchX(
mKeyguardSecurityContainer.getWidth() - 1f);
verify(mSecurityViewFlipper, never()).setTranslationX(anyInt());
ConstraintSet.Constraint viewFlipperConstraint =
getViewConstraint(mSecurityViewFlipper.getId());
assertThat(viewFlipperConstraint.layout.rightToRight).isEqualTo(-1);
assertThat(viewFlipperConstraint.layout.leftToLeft).isEqualTo(-1);
mKeyguardSecurityContainer.updatePositionByTouchX(1f);
verify(mSecurityViewFlipper, never()).setTranslationX(anyInt());
viewFlipperConstraint =
getViewConstraint(mSecurityViewFlipper.getId());
assertThat(viewFlipperConstraint.layout.rightToRight).isEqualTo(-1);
assertThat(viewFlipperConstraint.layout.leftToLeft).isEqualTo(-1);
}
@Test
@@ -249,17 +223,31 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
setupUserSwitcher();
mKeyguardSecurityContainer.onConfigurationChanged(landscapeConfig);
// THEN views are oriented side by side
assertSecurityGravity(Gravity.LEFT | Gravity.BOTTOM);
assertUserSwitcherGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
assertSecurityTranslationX(
mKeyguardSecurityContainer.getWidth() - mSecurityViewFlipper.getWidth());
assertUserSwitcherTranslationX(0f);
ConstraintSet.Constraint viewFlipperConstraint =
getViewConstraint(mSecurityViewFlipper.getId());
ConstraintSet.Constraint userSwitcherConstraint =
getViewConstraint(R.id.keyguard_bouncer_user_switcher);
assertThat(viewFlipperConstraint.layout.rightToRight).isEqualTo(PARENT_ID);
assertThat(viewFlipperConstraint.layout.leftToRight).isEqualTo(
R.id.keyguard_bouncer_user_switcher);
assertThat(userSwitcherConstraint.layout.leftToLeft).isEqualTo(PARENT_ID);
assertThat(userSwitcherConstraint.layout.rightToLeft).isEqualTo(
mSecurityViewFlipper.getId());
assertThat(viewFlipperConstraint.layout.topToTop).isEqualTo(PARENT_ID);
assertThat(viewFlipperConstraint.layout.bottomToBottom).isEqualTo(PARENT_ID);
assertThat(userSwitcherConstraint.layout.topToTop).isEqualTo(PARENT_ID);
assertThat(userSwitcherConstraint.layout.bottomToBottom).isEqualTo(PARENT_ID);
assertThat(userSwitcherConstraint.layout.bottomMargin).isEqualTo(
getContext().getResources().getDimensionPixelSize(
R.dimen.bouncer_user_switcher_y_trans));
assertThat(viewFlipperConstraint.layout.horizontalChainStyle).isEqualTo(CHAIN_SPREAD);
assertThat(userSwitcherConstraint.layout.horizontalChainStyle).isEqualTo(CHAIN_SPREAD);
assertThat(viewFlipperConstraint.layout.mHeight).isEqualTo(MATCH_CONSTRAINT);
assertThat(userSwitcherConstraint.layout.mHeight).isEqualTo(MATCH_CONSTRAINT);
}
@Test
public void testUserSwitcherModeViewGravityPortrait() {
public void testUserSwitcherModeViewPositionPortrait() {
// GIVEN one user has been setup and in landscape
when(mUserSwitcherController.getUsers()).thenReturn(buildUserRecords(1));
Configuration portraitConfig = configuration(ORIENTATION_PORTRAIT);
@@ -267,15 +255,28 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
// WHEN UserSwitcherViewMode is initialized and config has changed
setupUserSwitcher();
reset(mSecurityViewFlipper);
when(mSecurityViewFlipper.getLayoutParams()).thenReturn(mSecurityViewFlipperLayoutParams);
mKeyguardSecurityContainer.onConfigurationChanged(portraitConfig);
// THEN views are both centered horizontally
assertSecurityGravity(Gravity.CENTER_HORIZONTAL);
assertUserSwitcherGravity(Gravity.CENTER_HORIZONTAL);
assertSecurityTranslationX(0);
assertUserSwitcherTranslationX(0);
ConstraintSet.Constraint viewFlipperConstraint =
getViewConstraint(mSecurityViewFlipper.getId());
ConstraintSet.Constraint userSwitcherConstraint =
getViewConstraint(R.id.keyguard_bouncer_user_switcher);
assertThat(viewFlipperConstraint.layout.topToTop).isEqualTo(PARENT_ID);
assertThat(viewFlipperConstraint.layout.bottomToBottom).isEqualTo(PARENT_ID);
assertThat(userSwitcherConstraint.layout.topToTop).isEqualTo(PARENT_ID);
assertThat(userSwitcherConstraint.layout.topMargin).isEqualTo(
getContext().getResources().getDimensionPixelSize(
R.dimen.bouncer_user_switcher_y_trans));
assertThat(viewFlipperConstraint.layout.leftToLeft).isEqualTo(PARENT_ID);
assertThat(viewFlipperConstraint.layout.rightToRight).isEqualTo(PARENT_ID);
assertThat(userSwitcherConstraint.layout.leftToLeft).isEqualTo(PARENT_ID);
assertThat(userSwitcherConstraint.layout.rightToRight).isEqualTo(PARENT_ID);
assertThat(viewFlipperConstraint.layout.verticalChainStyle).isEqualTo(CHAIN_SPREAD);
assertThat(userSwitcherConstraint.layout.verticalChainStyle).isEqualTo(CHAIN_SPREAD);
assertThat(viewFlipperConstraint.layout.mHeight).isEqualTo(MATCH_CONSTRAINT);
assertThat(userSwitcherConstraint.layout.mHeight).isEqualTo(WRAP_CONTENT);
assertThat(userSwitcherConstraint.layout.mWidth).isEqualTo(WRAP_CONTENT);
}
@Test
@@ -337,9 +338,9 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
when(mGlobalSettings.getInt(any(), anyInt())).thenReturn(ONE_HANDED_KEYGUARD_SIDE_LEFT);
mKeyguardSecurityContainer.onConfigurationChanged(new Configuration());
assertSecurityTranslationX(0);
assertUserSwitcherTranslationX(
mKeyguardSecurityContainer.getWidth() - mSecurityViewFlipper.getWidth());
ConstraintSet.Constraint viewFlipperConstraint = getViewConstraint(
mSecurityViewFlipper.getId());
assertThat(viewFlipperConstraint.layout.leftToLeft).isEqualTo(PARENT_ID);
}
private Configuration configuration(@Configuration.Orientation int orientation) {
@@ -348,28 +349,6 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
return config;
}
private void assertSecurityTranslationX(float translation) {
verify(mSecurityViewFlipper).setTranslationX(translation);
}
private void assertUserSwitcherTranslationX(float translation) {
ViewGroup userSwitcher = mKeyguardSecurityContainer.findViewById(
R.id.keyguard_bouncer_user_switcher);
assertThat(userSwitcher.getTranslationX()).isEqualTo(translation);
}
private void assertUserSwitcherGravity(@Gravity.GravityFlags int gravity) {
ViewGroup userSwitcher = mKeyguardSecurityContainer.findViewById(
R.id.keyguard_bouncer_user_switcher);
assertThat(((FrameLayout.LayoutParams) userSwitcher.getLayoutParams()).gravity)
.isEqualTo(gravity);
}
private void assertSecurityGravity(@Gravity.GravityFlags int gravity) {
verify(mSecurityViewFlipper, atLeastOnce()).setLayoutParams(mLayoutCaptor.capture());
assertThat(mLayoutCaptor.getValue().gravity).isEqualTo(gravity);
}
private void setViewWidth(int width) {
mKeyguardSecurityContainer.setRight(width);
mKeyguardSecurityContainer.setLeft(0);
@@ -399,9 +378,6 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
when(mGlobalSettings.getInt(any(), anyInt())).thenReturn(ONE_HANDED_KEYGUARD_SIDE_RIGHT);
mKeyguardSecurityContainer.initMode(KeyguardSecurityContainer.MODE_USER_SWITCHER,
mGlobalSettings, mFalsingManager, mUserSwitcherController);
// reset mSecurityViewFlipper so setup doesn't influence test verifications
reset(mSecurityViewFlipper);
when(mSecurityViewFlipper.getLayoutParams()).thenReturn(mSecurityViewFlipperLayoutParams);
}
private ArrayList<UserRecord> buildUserRecords(int count) {
@@ -415,4 +391,17 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
}
return users;
}
private void setupForUpdateKeyguardPosition(boolean oneHandedMode) {
int mode = oneHandedMode ? MODE_ONE_HANDED : MODE_DEFAULT;
mKeyguardSecurityContainer.initMode(mode, mGlobalSettings, mFalsingManager,
mUserSwitcherController);
}
/** Get the ConstraintLayout constraint of the view. */
private ConstraintSet.Constraint getViewConstraint(int viewId) {
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(mKeyguardSecurityContainer);
return constraintSet.getConstraint(viewId);
}
}