Merge "Improve IME hiding animation when uncloked by password" into sc-dev
This commit is contained in:
@@ -185,8 +185,8 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
|
||||
if (timeoutMs == 0) {
|
||||
mMessageAreaController.setMessage(mView.getWrongPasswordStringId());
|
||||
}
|
||||
mView.resetPasswordText(true /* animate */, false /* announce deletion if no match */);
|
||||
}
|
||||
mView.resetPasswordText(true /* animate */, !matched /* announce deletion if no match */);
|
||||
}
|
||||
|
||||
protected void verifyPasswordAndUnlock() {
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.android.internal.jank.InteractionJankMonitor;
|
||||
* A Base class for all Keyguard password/pattern/pin related inputs.
|
||||
*/
|
||||
public abstract class KeyguardInputView extends LinearLayout {
|
||||
private Runnable mOnFinishImeAnimationRunnable;
|
||||
|
||||
public KeyguardInputView(Context context) {
|
||||
super(context);
|
||||
@@ -47,7 +48,8 @@ public abstract class KeyguardInputView extends LinearLayout {
|
||||
|
||||
abstract CharSequence getTitle();
|
||||
|
||||
void animateForIme(float interpolatedFraction) {
|
||||
void animateForIme(float interpolatedFraction, boolean appearingAnim) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean disallowInterceptTouch(MotionEvent event) {
|
||||
@@ -85,4 +87,14 @@ public abstract class KeyguardInputView extends LinearLayout {
|
||||
};
|
||||
}
|
||||
|
||||
public void setOnFinishImeAnimationRunnable(Runnable onFinishImeAnimationRunnable) {
|
||||
mOnFinishImeAnimationRunnable = onFinishImeAnimationRunnable;
|
||||
}
|
||||
|
||||
public void runOnFinishImeAnimationRunnable() {
|
||||
if (mOnFinishImeAnimationRunnable != null) {
|
||||
mOnFinishImeAnimationRunnable.run();
|
||||
mOnFinishImeAnimationRunnable = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.keyguard;
|
||||
|
||||
import static android.view.WindowInsets.Type.ime;
|
||||
|
||||
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
|
||||
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE;
|
||||
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
|
||||
@@ -23,15 +25,25 @@ import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART;
|
||||
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
|
||||
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.WindowInsetsAnimationControlListener;
|
||||
import android.view.WindowInsetsAnimationController;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.internal.widget.LockscreenCredential;
|
||||
import com.android.internal.widget.TextViewInputDisabler;
|
||||
import com.android.systemui.Interpolators;
|
||||
import com.android.systemui.R;
|
||||
/**
|
||||
* Displays an alphanumeric (latin-1) key entry for the user to enter
|
||||
@@ -41,6 +53,8 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView {
|
||||
|
||||
private final int mDisappearYTranslation;
|
||||
|
||||
private static final long IME_DISAPPEAR_DURATION_MS = 125;
|
||||
|
||||
// A delay constant to be used in a workaround for the situation where InputMethodManagerService
|
||||
// is not switched to the new user yet.
|
||||
// TODO: Remove this by ensuring such a race condition never happens.
|
||||
@@ -150,9 +164,63 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void animateForIme(float interpolatedFraction) {
|
||||
public boolean startDisappearAnimation(Runnable finishRunnable) {
|
||||
getWindowInsetsController().controlWindowInsetsAnimation(ime(),
|
||||
100,
|
||||
Interpolators.LINEAR, null, new WindowInsetsAnimationControlListener() {
|
||||
|
||||
@Override
|
||||
public void onReady(@NonNull WindowInsetsAnimationController controller,
|
||||
int types) {
|
||||
ValueAnimator anim = ValueAnimator.ofFloat(1f, 0f);
|
||||
anim.addUpdateListener(animation -> {
|
||||
if (controller.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
Insets shownInsets = controller.getShownStateInsets();
|
||||
Insets insets = Insets.add(shownInsets, Insets.of(0, 0, 0,
|
||||
(int) (-shownInsets.bottom / 4
|
||||
* anim.getAnimatedFraction())));
|
||||
controller.setInsetsAndAlpha(insets,
|
||||
(float) animation.getAnimatedValue(),
|
||||
anim.getAnimatedFraction());
|
||||
});
|
||||
anim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
controller.finish(false);
|
||||
runOnFinishImeAnimationRunnable();
|
||||
finishRunnable.run();
|
||||
}
|
||||
});
|
||||
anim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
|
||||
anim.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinished(
|
||||
@NonNull WindowInsetsAnimationController controller) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelled(
|
||||
@Nullable WindowInsetsAnimationController controller) {
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void animateForIme(float interpolatedFraction, boolean appearingAnim) {
|
||||
animate().cancel();
|
||||
setAlpha(Math.max(interpolatedFraction, getAlpha()));
|
||||
setAlpha(appearingAnim
|
||||
? Math.max(interpolatedFraction, getAlpha())
|
||||
: 1 - interpolatedFraction);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -189,23 +189,22 @@ public class KeyguardPasswordViewController
|
||||
return;
|
||||
}
|
||||
if (wasDisabled) {
|
||||
mInputMethodManager.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
|
||||
showInput();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume(int reason) {
|
||||
super.onResume(reason);
|
||||
|
||||
mPasswordEntry.requestFocus();
|
||||
if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) {
|
||||
showInput();
|
||||
}
|
||||
}
|
||||
|
||||
private void showInput() {
|
||||
mPasswordEntry.post(() -> {
|
||||
if (mPasswordEntry.isFocused() && mView.isShown()) {
|
||||
mView.post(() -> {
|
||||
if (mView.isShown()) {
|
||||
mPasswordEntry.requestFocus();
|
||||
mInputMethodManager.showSoftInput(
|
||||
mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
|
||||
}
|
||||
@@ -214,7 +213,18 @@ public class KeyguardPasswordViewController
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
if (!mPasswordEntry.isVisibleToUser()) {
|
||||
// Reset all states directly and then hide IME when the screen turned off.
|
||||
super.onPause();
|
||||
} else {
|
||||
// In order not to break the IME hide animation by resetting states too early after
|
||||
// the password checked, make sure resetting states after the IME hiding animation
|
||||
// finished.
|
||||
mView.setOnFinishImeAnimationRunnable(() -> {
|
||||
mPasswordEntry.clearFocus();
|
||||
super.onPause();
|
||||
});
|
||||
}
|
||||
mInputMethodManager.hideSoftInputFromWindow(mView.getWindowToken(), 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,11 +23,9 @@ import static java.lang.Integer.max;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Rect;
|
||||
import android.provider.Settings;
|
||||
import android.util.AttributeSet;
|
||||
@@ -42,12 +40,9 @@ import android.view.ViewConfiguration;
|
||||
import android.view.ViewPropertyAnimator;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowInsetsAnimation;
|
||||
import android.view.WindowInsetsAnimationControlListener;
|
||||
import android.view.WindowInsetsAnimationController;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.dynamicanimation.animation.DynamicAnimation;
|
||||
@@ -130,6 +125,9 @@ public class KeyguardSecurityContainer extends FrameLayout {
|
||||
WindowInsetsAnimation.Bounds bounds) {
|
||||
if (!mDisappearAnimRunning) {
|
||||
beginJankInstrument(InteractionJankMonitor.CUJ_LOCKSCREEN_PASSWORD_APPEAR);
|
||||
} else {
|
||||
beginJankInstrument(
|
||||
InteractionJankMonitor.CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR);
|
||||
}
|
||||
mSecurityViewFlipper.getBoundsOnScreen(mFinalBounds);
|
||||
return bounds;
|
||||
@@ -138,25 +136,28 @@ public class KeyguardSecurityContainer extends FrameLayout {
|
||||
@Override
|
||||
public WindowInsets onProgress(WindowInsets windowInsets,
|
||||
List<WindowInsetsAnimation> list) {
|
||||
if (mDisappearAnimRunning) {
|
||||
mSecurityViewFlipper.setTranslationY(
|
||||
mInitialBounds.bottom - mFinalBounds.bottom);
|
||||
} else {
|
||||
int translationY = 0;
|
||||
float interpolatedFraction = 1f;
|
||||
for (WindowInsetsAnimation animation : list) {
|
||||
if ((animation.getTypeMask() & WindowInsets.Type.ime()) == 0) {
|
||||
continue;
|
||||
}
|
||||
interpolatedFraction = animation.getInterpolatedFraction();
|
||||
|
||||
final int paddingBottom = (int) MathUtils.lerp(
|
||||
mInitialBounds.bottom - mFinalBounds.bottom, 0,
|
||||
interpolatedFraction);
|
||||
translationY += paddingBottom;
|
||||
float start = mDisappearAnimRunning
|
||||
? -(mFinalBounds.bottom - mInitialBounds.bottom)
|
||||
: mInitialBounds.bottom - mFinalBounds.bottom;
|
||||
float end = mDisappearAnimRunning
|
||||
? -((mFinalBounds.bottom - mInitialBounds.bottom) * 0.75f)
|
||||
: 0f;
|
||||
int translationY = 0;
|
||||
float interpolatedFraction = 1f;
|
||||
for (WindowInsetsAnimation animation : list) {
|
||||
if ((animation.getTypeMask() & WindowInsets.Type.ime()) == 0) {
|
||||
continue;
|
||||
}
|
||||
mSecurityViewFlipper.animateForIme(translationY, interpolatedFraction);
|
||||
interpolatedFraction = animation.getInterpolatedFraction();
|
||||
|
||||
final int paddingBottom = (int) MathUtils.lerp(
|
||||
start, end,
|
||||
interpolatedFraction);
|
||||
translationY += paddingBottom;
|
||||
}
|
||||
mSecurityViewFlipper.animateForIme(translationY, interpolatedFraction,
|
||||
!mDisappearAnimRunning);
|
||||
|
||||
return windowInsets;
|
||||
}
|
||||
|
||||
@@ -164,7 +165,10 @@ public class KeyguardSecurityContainer extends FrameLayout {
|
||||
public void onEnd(WindowInsetsAnimation animation) {
|
||||
if (!mDisappearAnimRunning) {
|
||||
endJankInstrument(InteractionJankMonitor.CUJ_LOCKSCREEN_PASSWORD_APPEAR);
|
||||
mSecurityViewFlipper.animateForIme(0, /* interpolatedFraction */ 1f);
|
||||
mSecurityViewFlipper.animateForIme(0, /* interpolatedFraction */ 1f,
|
||||
true /* appearingAnim */);
|
||||
} else {
|
||||
endJankInstrument(InteractionJankMonitor.CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -522,63 +526,6 @@ public class KeyguardSecurityContainer extends FrameLayout {
|
||||
|
||||
public void startDisappearAnimation(SecurityMode securitySelection) {
|
||||
mDisappearAnimRunning = true;
|
||||
if (securitySelection == SecurityMode.Password) {
|
||||
mSecurityViewFlipper.getWindowInsetsController().controlWindowInsetsAnimation(ime(),
|
||||
IME_DISAPPEAR_DURATION_MS,
|
||||
Interpolators.LINEAR, null, new WindowInsetsAnimationControlListener() {
|
||||
|
||||
|
||||
@Override
|
||||
public void onReady(@NonNull WindowInsetsAnimationController controller,
|
||||
int types) {
|
||||
ValueAnimator anim = ValueAnimator.ofFloat(1f, 0f);
|
||||
anim.addUpdateListener(animation -> {
|
||||
if (controller.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
Insets shownInsets = controller.getShownStateInsets();
|
||||
Insets insets = Insets.add(shownInsets, Insets.of(0, 0, 0,
|
||||
(int) (-shownInsets.bottom / 4
|
||||
* anim.getAnimatedFraction())));
|
||||
controller.setInsetsAndAlpha(insets,
|
||||
(float) animation.getAnimatedValue(),
|
||||
anim.getAnimatedFraction());
|
||||
});
|
||||
anim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
beginJankInstrument(
|
||||
InteractionJankMonitor
|
||||
.CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
endJankInstrument(
|
||||
InteractionJankMonitor
|
||||
.CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR);
|
||||
controller.finish(false);
|
||||
}
|
||||
});
|
||||
anim.setDuration(IME_DISAPPEAR_DURATION_MS);
|
||||
anim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
|
||||
anim.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinished(
|
||||
@NonNull WindowInsetsAnimationController controller) {
|
||||
mDisappearAnimRunning = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelled(
|
||||
@Nullable WindowInsetsAnimationController controller) {
|
||||
cancelJankInstrument(
|
||||
InteractionJankMonitor.CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void beginJankInstrument(int cuj) {
|
||||
|
||||
@@ -87,10 +87,10 @@ public class KeyguardSecurityViewFlipper extends ViewFlipper {
|
||||
* Translate the entire view, and optionally inform the wrapped view of the progress
|
||||
* so it can animate with the parent.
|
||||
*/
|
||||
public void animateForIme(int translationY, float interpolatedFraction) {
|
||||
public void animateForIme(int translationY, float interpolatedFraction, boolean appearingAnim) {
|
||||
super.setTranslationY(translationY);
|
||||
KeyguardInputView v = getSecurityView();
|
||||
if (v != null) v.animateForIme(interpolatedFraction);
|
||||
if (v != null) v.animateForIme(interpolatedFraction, appearingAnim);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,13 +16,19 @@
|
||||
|
||||
package com.android.keyguard;
|
||||
|
||||
import static android.view.WindowInsets.Type.ime;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.view.WindowInsetsController;
|
||||
@@ -33,6 +39,7 @@ import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.internal.logging.UiEventLogger;
|
||||
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.FalsingManagerFake;
|
||||
import com.android.systemui.plugins.FalsingManager;
|
||||
@@ -84,18 +91,34 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
|
||||
@Mock
|
||||
private KeyguardSecurityViewFlipperController mKeyguardSecurityViewFlipperController;
|
||||
@Mock
|
||||
private KeyguardMessageAreaController.Factory mKeyguardMessageAreaControllerFactory;
|
||||
@Mock
|
||||
private KeyguardMessageArea mKeyguardMessageArea;
|
||||
@Mock
|
||||
private ConfigurationController mConfigurationController;
|
||||
@Mock
|
||||
private KeyguardViewController mKeyguardViewController;
|
||||
private FalsingManager mFalsingManager = new FalsingManagerFake();
|
||||
|
||||
private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
|
||||
private KeyguardPasswordViewController mKeyguardPasswordViewController;
|
||||
private KeyguardPasswordView mKeyguardPasswordView;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
when(mAdminSecondaryLockScreenControllerFactory.create(any(KeyguardSecurityCallback.class)))
|
||||
.thenReturn(mAdminSecondaryLockScreenController);
|
||||
when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
|
||||
mKeyguardPasswordView = spy(new KeyguardPasswordView(getContext()));
|
||||
when(mKeyguardPasswordView.getRootView()).thenReturn(mSecurityViewFlipper);
|
||||
when(mKeyguardPasswordView.findViewById(R.id.keyguard_message_area))
|
||||
.thenReturn(mKeyguardMessageArea);
|
||||
when(mKeyguardPasswordView.getWindowInsetsController()).thenReturn(mWindowInsetsController);
|
||||
mKeyguardPasswordViewController = new KeyguardPasswordViewController(
|
||||
(KeyguardPasswordView) mKeyguardPasswordView, mKeyguardUpdateMonitor,
|
||||
SecurityMode.Password, mLockPatternUtils, null,
|
||||
mKeyguardMessageAreaControllerFactory, null, null, null, mock(Resources.class),
|
||||
null);
|
||||
|
||||
mKeyguardSecurityContainerController = new KeyguardSecurityContainerController.Factory(
|
||||
mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
|
||||
@@ -125,14 +148,13 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
|
||||
public void startDisappearAnimation_animatesKeyboard() {
|
||||
when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
|
||||
SecurityMode.Password);
|
||||
when(mInputViewController.getSecurityMode()).thenReturn(
|
||||
SecurityMode.Password);
|
||||
when(mKeyguardSecurityViewFlipperController.getSecurityView(
|
||||
eq(SecurityMode.Password), any(KeyguardSecurityCallback.class)))
|
||||
.thenReturn(mInputViewController);
|
||||
mKeyguardSecurityContainerController.showPrimarySecurityScreen(false /* turningOff */);
|
||||
.thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
|
||||
mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password);
|
||||
|
||||
mKeyguardSecurityContainerController.startDisappearAnimation(null);
|
||||
verify(mInputViewController).startDisappearAnimation(eq(null));
|
||||
verify(mWindowInsetsController).controlWindowInsetsAnimation(
|
||||
eq(ime()), anyLong(), any(), any(), any());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,6 @@ package com.android.keyguard;
|
||||
import static android.view.WindowInsets.Type.ime;
|
||||
import static android.view.WindowInsets.Type.systemBars;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -89,13 +86,6 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startDisappearAnimation_animatesKeyboard() {
|
||||
mKeyguardSecurityContainer.startDisappearAnimation(SecurityMode.Password);
|
||||
verify(mWindowInsetsController).controlWindowInsetsAnimation(eq(ime()), anyLong(), any(),
|
||||
any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onMeasure_usesFullWidthWithoutOneHandedMode() {
|
||||
setUpKeyguard(
|
||||
|
||||
Reference in New Issue
Block a user