Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 02896d1c authored by Lucas Dupin's avatar Lucas Dupin Committed by Android (Google) Code Review
Browse files

Merge "Allow users to swipe up on bouncer to retry auth" into qt-dev

parents 5ffba38d 7156bc79
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -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">

    <include
        style="@style/BouncerSecurityContainer"
+7 −0
Original line number Diff line number Diff line
@@ -231,6 +231,13 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
    public void showUsabilityHint() {
    }

    @Override
    public boolean disallowInterceptTouch(MotionEvent event) {
        mTempRect.set(mLockPatternView.getLeft(), mLockPatternView.getTop(),
                mLockPatternView.getRight(), mLockPatternView.getBottom());
        return mTempRect.contains((int) event.getX(), (int) event.getY());
    }

    /** TODO: hook this up */
    public void cleanUp() {
        if (DEBUG) Log.v(TAG, "Cleanup() called on " + this);
+138 −2
Original line number Diff line number Diff line
@@ -26,12 +26,18 @@ import android.util.AttributeSet;
import android.util.Log;
import android.util.Slog;
import android.util.StatsLog;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.widget.FrameLayout;

import androidx.annotation.VisibleForTesting;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.SpringAnimation;

import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -60,6 +66,11 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
    // Bouncer is dismissed due to sim card unlock code entered.
    private static final int BOUNCER_DISMISS_SIM = 4;

    // Make the view move slower than the finger, as if the spring were applying force.
    private static final float TOUCH_Y_MULTIPLIER = 0.25f;
    // How much you need to drag the bouncer to trigger an auth retry (in dps.)
    private static final float MIN_DRAG_SIZE = 10;

    private KeyguardSecurityModel mSecurityModel;
    private LockPatternUtils mLockPatternUtils;

@@ -70,10 +81,18 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
    private SecurityCallback mSecurityCallback;
    private AlertDialog mAlertDialog;
    private InjectionInflationController mInjectionInflationController;
    private boolean mSwipeUpToRetry;

    private final ViewConfiguration mViewConfiguration;
    private final SpringAnimation mSpringAnimation;
    private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
    private final KeyguardUpdateMonitor mUpdateMonitor;

    private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
    private float mLastTouchY = -1;
    private int mActivePointerId = -1;
    private boolean mIsDragging;
    private float mStartTouchY = -1;

    // Used to notify the container when something interesting happens.
    public interface SecurityCallback {
@@ -104,9 +123,10 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
        mSecurityModel = new KeyguardSecurityModel(context);
        mLockPatternUtils = new LockPatternUtils(context);
        mUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);

        mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y);
        mInjectionInflationController =  new InjectionInflationController(
            SystemUIFactory.getInstance().getRootComponent());
        mViewConfiguration = ViewConfiguration.get(context);
    }

    public void setSecurityCallback(SecurityCallback callback) {
@@ -118,6 +138,8 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
        if (mCurrentSecuritySelection != SecurityMode.None) {
            getSecurityView(mCurrentSecuritySelection).onResume(reason);
        }
        updateBiometricRetry();
        updatePaddings();
    }

    @Override
@@ -131,6 +153,95 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
        }
    }

    @Override
    public boolean shouldDelayChildPressedState() {
        return true;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                int pointerIndex = event.getActionIndex();
                mStartTouchY = event.getY(pointerIndex);
                mActivePointerId = event.getPointerId(pointerIndex);
                mVelocityTracker.clear();
                break;
            case MotionEvent.ACTION_MOVE:
                if (mIsDragging) {
                    return true;
                }
                if (!mSwipeUpToRetry) {
                    return false;
                }
                // Avoid dragging the pattern view
                if (mCurrentSecurityView.disallowInterceptTouch(event)) {
                    return false;
                }
                int index = event.findPointerIndex(mActivePointerId);
                int touchSlop = mViewConfiguration.getScaledTouchSlop();
                if (mCurrentSecurityView != null
                        && mStartTouchY - event.getY(index) > 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);
+11 −0
Original line number Diff line number Diff line
@@ -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;
    }
}
+15 −5
Original line number Diff line number Diff line
@@ -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() {