From 7156bc795d294826b31a24d5df0a95b4c94b0b66 Mon Sep 17 00:00:00 2001 From: Lucas Dupin Date: Fri, 3 May 2019 19:37:39 -0700 Subject: [PATCH] Allow users to swipe up on bouncer to retry auth This will be enabled whenever face authentication is available. It's not possible to swipe on the pattern area. Test: manual (with pin, pattern and password) Fixes: 128036528 Change-Id: I8a4a9f7243db90be63df7d4e18125a1de3a591d9 --- .../res-keyguard/layout/keyguard_bouncer.xml | 3 +- .../android/keyguard/KeyguardPatternView.java | 7 + .../keyguard/KeyguardSecurityContainer.java | 140 +++++++++++++++++- .../keyguard/KeyguardSecurityView.java | 11 ++ .../keyguard/KeyguardUpdateMonitor.java | 20 ++- 5 files changed, 172 insertions(+), 9 deletions(-) diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml index 8c80e78733efb..79868093fb127 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml @@ -19,8 +19,7 @@ android:layout_height="match_parent" android:background="@android:color/transparent" android:clipChildren="false" - android:clipToPadding="false" - android:fitsSystemWindows="true"> + android:clipToPadding="false"> touchSlop) { + mIsDragging = true; + return true; + } + break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + mIsDragging = false; + break; + } + return false; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + final int action = event.getActionMasked(); + switch (action) { + case MotionEvent.ACTION_MOVE: + mVelocityTracker.addMovement(event); + int pointerIndex = event.findPointerIndex(mActivePointerId); + float y = event.getY(pointerIndex); + if (mLastTouchY != -1) { + float dy = y - mLastTouchY; + setTranslationY(getTranslationY() + dy * TOUCH_Y_MULTIPLIER); + } + mLastTouchY = y; + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mActivePointerId = -1; + mLastTouchY = -1; + mIsDragging = false; + startSpringAnimation(mVelocityTracker.getYVelocity()); + break; + case MotionEvent.ACTION_POINTER_UP: + int index = event.getActionIndex(); + int pointerId = event.getPointerId(index); + if (pointerId == mActivePointerId) { + // This was our active pointer going up. Choose a new + // active pointer and adjust accordingly. + final int newPointerIndex = index == 0 ? 1 : 0; + mLastTouchY = event.getY(newPointerIndex); + mActivePointerId = event.getPointerId(newPointerIndex); + } + break; + } + if (action == MotionEvent.ACTION_UP) { + if (-getTranslationY() > TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + MIN_DRAG_SIZE, getResources().getDisplayMetrics())) { + mUpdateMonitor.requestFaceAuth(); + } + } + return true; + } + + private void startSpringAnimation(float startVelocity) { + mSpringAnimation + .setStartVelocity(startVelocity) + .animateToFinalPosition(0); + } + public void startAppearAnimation() { if (mCurrentSecuritySelection != SecurityMode.None) { getSecurityView(mCurrentSecuritySelection).startAppearAnimation(); @@ -145,6 +256,18 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe return false; } + /** + * Enables/disables swipe up to retry on the bouncer. + */ + private void updateBiometricRetry() { + SecurityMode securityMode = getSecurityMode(); + int userId = KeyguardUpdateMonitor.getCurrentUser(); + mSwipeUpToRetry = mUpdateMonitor.isUnlockWithFacePossible(userId) + && securityMode != SecurityMode.SimPin + && securityMode != SecurityMode.SimPuk + && securityMode != SecurityMode.None; + } + public CharSequence getTitle() { return mSecurityViewFlipper.getTitle(); } @@ -195,6 +318,20 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils); } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + updatePaddings(); + } + + private void updatePaddings() { + int bottomPadding = getRootWindowInsets().getSystemWindowInsets().bottom; + if (getPaddingBottom() == bottomPadding) { + return; + } + setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), bottomPadding); + } + private void showDialog(String title, String message) { if (mAlertDialog != null) { mAlertDialog.dismiss(); @@ -467,7 +604,6 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe } public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { - KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext); if (success) { StatsLog.write(StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED, StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java index 272b3bdf56a40..e10819473dea0 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java @@ -16,6 +16,7 @@ package com.android.keyguard; import android.content.res.ColorStateList; +import android.view.MotionEvent; import com.android.internal.widget.LockPatternUtils; @@ -137,4 +138,14 @@ public interface KeyguardSecurityView { * @return The View's title. */ CharSequence getTitle(); + + /** + * If the parent should not be allowed to intercept touch events. + * @param event A touch event. + * @return {@code true} if touch should be passed forward. + * @see android.view.ViewGroup#requestDisallowInterceptTouchEvent(boolean) + */ + default boolean disallowInterceptTouch(MotionEvent event) { + return false; + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 83b98b0f8bb1a..dd6ccb2b3a88b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -1686,7 +1686,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { && mFpm.getEnrolledFingerprints(userId).size() > 0; } - private boolean isUnlockWithFacePossible(int userId) { + /** + * If face hardware is available and user has enrolled. Not considering encryption or + * lockdown state. + */ + public boolean isUnlockWithFacePossible(int userId) { return mFaceManager != null && mFaceManager.isHardwareDetected() && !isFaceDisabled(userId) && mFaceManager.hasEnrolledTemplates(userId); @@ -2273,6 +2277,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { return isSimPinSecure(); } + /** + * If any SIM cards are currently secure. + * @see #isSimPinSecure(State) + */ public boolean isSimPinSecure() { // True if any SIM is pin secure for (SubscriptionInfo info : getSubscriptionInfo(false /* forceReload */)) { @@ -2338,11 +2346,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { return changed; } + /** + * If the {@code state} is currently requiring a SIM PIN, PUK, or is disabled. + */ public static boolean isSimPinSecure(IccCardConstants.State state) { - final IccCardConstants.State simState = state; - return (simState == IccCardConstants.State.PIN_REQUIRED - || simState == IccCardConstants.State.PUK_REQUIRED - || simState == IccCardConstants.State.PERM_DISABLED); + return (state == IccCardConstants.State.PIN_REQUIRED + || state == IccCardConstants.State.PUK_REQUIRED + || state == IccCardConstants.State.PERM_DISABLED); } public DisplayClientState getCachedDisplayClientState() {