[Keyguard Bouncer] Refine entry animation.

Animate bouncer message area and add decelerate curves to y translation.

Test: Manual on device and add a unit test
Bug: 239532368
Change-Id: Id039530a9a3cbd917469f9dc19c10c82d94cd018
This commit is contained in:
Aaron Liu
2022-09-09 12:04:14 -07:00
parent 1c02d77cd7
commit 8a6d11abb5
5 changed files with 131 additions and 5 deletions

View File

@@ -84,6 +84,10 @@
<!-- The translation for disappearing security views after having solved them. -->
<dimen name="disappear_y_translation">-32dp</dimen>
<!-- Dimens for animation for the Bouncer PIN view -->
<dimen name="pin_view_trans_y_entry">120dp</dimen>
<dimen name="pin_view_trans_y_entry_offset">10dp</dimen>
<!-- Spacing around each button used for PIN view -->
<dimen name="num_pad_key_width">72dp</dimen>
<dimen name="num_pad_entry_row_margin_bottom">12dp</dimen>

View File

@@ -48,6 +48,9 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
private ConstraintLayout mContainer;
private int mDisappearYTranslation;
private View[][] mViews;
private int mYTrans;
private int mYTransOffset;
private View mBouncerMessageView;
@DevicePostureInt private int mLastDevicePosture = DEVICE_POSTURE_UNKNOWN;
public KeyguardPINView(Context context) {
@@ -67,6 +70,8 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
mContext, android.R.interpolator.fast_out_linear_in));
mDisappearYTranslation = getResources().getDimensionPixelSize(
R.dimen.disappear_y_translation);
mYTrans = getResources().getDimensionPixelSize(R.dimen.pin_view_trans_y_entry);
mYTransOffset = getResources().getDimensionPixelSize(R.dimen.pin_view_trans_y_entry_offset);
}
@Override
@@ -138,6 +143,7 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
super.onFinishInflate();
mContainer = findViewById(R.id.pin_container);
mBouncerMessageView = findViewById(R.id.bouncer_message_area);
mViews = new View[][]{
new View[]{
findViewById(R.id.row0), null, null
@@ -206,6 +212,12 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
/** Animate subviews according to expansion or time. */
private void animate(float progress) {
Interpolator standardDecelerate = Interpolators.STANDARD_DECELERATE;
Interpolator legacyDecelerate = Interpolators.LEGACY_DECELERATE;
mBouncerMessageView.setTranslationY(
mYTrans - mYTrans * standardDecelerate.getInterpolation(progress));
for (int i = 0; i < mViews.length; i++) {
View[] row = mViews[i];
for (View view : row) {
@@ -213,14 +225,15 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
continue;
}
float scaledProgress = MathUtils.constrain(
float scaledProgress = legacyDecelerate.getInterpolation(MathUtils.constrain(
(progress - 0.075f * i) / (1f - 0.075f * mViews.length),
0f,
1f
);
));
view.setAlpha(scaledProgress);
Interpolator interpolator = Interpolators.STANDARD_ACCELERATE;
view.setTranslationY(40 - (40 * interpolator.getInterpolation(scaledProgress)));
int yDistance = mYTrans + mYTransOffset * i;
view.setTranslationY(
yDistance - (yDistance * standardDecelerate.getInterpolation(progress)));
if (view instanceof NumPadAnimationListener) {
((NumPadAnimationListener) view).setProgress(scaledProgress);
}

View File

@@ -81,6 +81,12 @@ public class KeyguardPinViewController
mMessageAreaController.setMessage("");
}
@Override
public void startAppearAnimation() {
mMessageAreaController.setMessageIfEmpty(R.string.keyguard_enter_your_pin);
super.startAppearAnimation();
}
@Override
public boolean startDisappearAnimation(Runnable finishRunnable) {
return mView.startDisappearAnimation(

View File

@@ -86,7 +86,7 @@ class NumPadAnimator {
public void setProgress(float progress) {
mBackground.setCornerRadius(mEndRadius + (mStartRadius - mEndRadius) * progress);
int height = (int) (mHeight * 0.8f + mHeight * 0.2 * progress);
int height = (int) (mHeight * 0.7f + mHeight * 0.3 * progress);
int difference = mHeight - height;
mBackground.setBounds(0, difference / 2, mHeight, mHeight - difference / 2);
}

View File

@@ -0,0 +1,103 @@
/*
* 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.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.KeyguardSecurityModel.SecurityMode
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.statusbar.policy.DevicePostureController
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.any
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class KeyguardPinViewControllerTest : SysuiTestCase() {
@Mock private lateinit var keyguardPinView: KeyguardPINView
@Mock private lateinit var keyguardMessageArea: BouncerKeyguardMessageArea
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var securityMode: SecurityMode
@Mock private lateinit var lockPatternUtils: LockPatternUtils
@Mock private lateinit var mKeyguardSecurityCallback: KeyguardSecurityCallback
@Mock
private lateinit var keyguardMessageAreaControllerFactory: KeyguardMessageAreaController.Factory
@Mock
private lateinit var keyguardMessageAreaController:
KeyguardMessageAreaController<BouncerKeyguardMessageArea>
@Mock private lateinit var mLatencyTracker: LatencyTracker
@Mock private lateinit var liftToActivateListener: LiftToActivateListener
@Mock private val mEmergencyButtonController: EmergencyButtonController? = null
private val falsingCollector: FalsingCollector = FalsingCollectorFake()
@Mock lateinit var postureController: DevicePostureController
lateinit var pinViewController: KeyguardPinViewController
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
Mockito.`when`(keyguardPinView.requireViewById<View>(R.id.bouncer_message_area))
.thenReturn(keyguardMessageArea)
Mockito.`when`(
keyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea::class.java))
)
.thenReturn(keyguardMessageAreaController)
pinViewController =
KeyguardPinViewController(
keyguardPinView,
keyguardUpdateMonitor,
securityMode,
lockPatternUtils,
mKeyguardSecurityCallback,
keyguardMessageAreaControllerFactory,
mLatencyTracker,
liftToActivateListener,
mEmergencyButtonController,
falsingCollector,
postureController
)
}
@Test
fun startAppearAnimation() {
pinViewController.startAppearAnimation()
verify(keyguardMessageAreaController).setMessageIfEmpty(R.string.keyguard_enter_your_pin)
}
}