Loading packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml +0 −1 Original line number Diff line number Diff line Loading @@ -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" > Loading packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java +21 −2 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -156,6 +165,7 @@ public class AdminSecondaryLockScreenController { mUpdateMonitor = updateMonitor; mKeyguardCallback = callback; mView = new AdminSecurityView(mContext, mSurfaceHolderCallback); mView.setId(View.generateViewId()); } /** Loading @@ -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); } } Loading packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +85 −291 File changed.Preview size limit exceeded, changes collapsed. Show changes packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt 0 → 100644 +210 −0 Original line number Diff line number Diff line /* * 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 } } packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java +2 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading Loading
packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml +0 −1 Original line number Diff line number Diff line Loading @@ -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" > Loading
packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java +21 −2 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -156,6 +165,7 @@ public class AdminSecondaryLockScreenController { mUpdateMonitor = updateMonitor; mKeyguardCallback = callback; mView = new AdminSecurityView(mContext, mSurfaceHolderCallback); mView.setId(View.generateViewId()); } /** Loading @@ -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); } } Loading
packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +85 −291 File changed.Preview size limit exceeded, changes collapsed. Show changes
packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt 0 → 100644 +210 −0 Original line number Diff line number Diff line /* * 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 } }
packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java +2 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading