Merge "[Bouncer] Make bouncer view a ConstraintLayout" into tm-qpr-dev
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
index 5486adb..6ec65ce 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 00f1c01..207f344 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.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 @@
     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 @@
         mUpdateMonitor = updateMonitor;
         mKeyguardCallback = callback;
         mView = new AdminSecurityView(mContext, mSurfaceHolderCallback);
+        mView.setId(View.generateViewId());
     }
 
     /**
@@ -167,6 +177,15 @@
         }
         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 353c369..c34db15 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.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.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.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 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 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 @@
     // 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 @@
     }
 
     @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 @@
      * 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 @@
         /** 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 @@
      * 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 @@
 
         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 @@
      * 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 @@
         }
 
         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 @@
      * 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 @@
         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 @@
             });
         }
 
-        /**
-         * 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 @@
      * 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 @@
             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 @@
         }
 
         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 0000000..9eb2c11
--- /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 80385e6..f5cd0ca 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.ViewUtils;
 import android.view.SurfaceControlViewHost;
 import android.view.SurfaceView;
+import android.view.View;
 
 import androidx.test.filters.SmallTest;
 
@@ -84,6 +85,7 @@
         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 c1036e3..52f8825 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.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 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.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.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 @@
 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 @@
     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 @@
                 .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 @@
                 .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 @@
                 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 @@
 
         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 @@
         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 @@
 
         // 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 @@
         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 @@
         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 @@
         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 @@
         }
         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);
+    }
 }