diff options
| author | 2022-10-12 16:50:07 +0000 | |
|---|---|---|
| committer | 2022-10-12 16:50:07 +0000 | |
| commit | 3510d130bb35538f0525f77eb5c91468514159fd (patch) | |
| tree | 0967913adc0b76ea93f5fafd600f0d49fe16382e | |
| parent | ec073d1e832cb32a9f049d0a0a7941b3a3a68a43 (diff) | |
| parent | cd59981ea62ef60bd5bcac765d4e167a68ac266d (diff) | |
Merge "[Bouncer] Make bouncer view a ConstraintLayout" into tm-qpr-dev am: 06ab363b77 am: cd59981ea6
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20028956
Change-Id: If48a65e38c1406f220f7f1193ea0a087292e9246
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
6 files changed, 425 insertions, 412 deletions
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml index 5486adbd2b8e..6ec65cebf840 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml @@ -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" > diff --git a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java index 00f1c0108d0b..207f3440d38b 100644 --- a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java +++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java @@ -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); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index 353c3698dce4..c34db1532d6c 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -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; @@ -649,47 +647,8 @@ 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,89 +1072,56 @@ 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); - } - } - - } - - 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); + if (animate) { + TransitionManager.beginDelayedTransition(mView, + new KeyguardSecurityViewTransition()); } - } - - 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(); + + 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); } } - - private void updateViewGravity(View v, int gravity) { - FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams(); - lp.gravity = gravity; - v.setLayoutParams(lp); - } } /** @@ -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,28 +1141,10 @@ 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); } } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt new file mode 100644 index 000000000000..9eb2c118abac --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt @@ -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 + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java index 80385e69cfa4..f5cd0ca7ab3b 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java @@ -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); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java index c1036e356cfa..52f8825c724b 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java @@ -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); + } } |