Loading packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +117 −97 Original line number Diff line number Diff line Loading @@ -35,13 +35,14 @@ import android.hardware.biometrics.SensorLocationInternal; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.media.AudioAttributes; import android.os.Process; import android.os.VibrationEffect; import android.os.Vibrator; import android.util.DisplayMetrics; import android.util.Log; import android.util.MathUtils; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; Loading Loading @@ -83,6 +84,7 @@ import javax.inject.Inject; */ @StatusBarComponent.StatusBarScope public class LockIconViewController extends ViewController<LockIconView> implements Dumpable { private static final String TAG = "LockIconViewController"; private static final float sDefaultDensity = (float) DisplayMetrics.DENSITY_DEVICE_STABLE / (float) DisplayMetrics.DENSITY_DEFAULT; private static final int sLockIconRadiusPx = (int) (sDefaultDensity * 36); Loading @@ -91,6 +93,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) .build(); private static final long LONG_PRESS_TIMEOUT = 150L; // milliseconds @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @NonNull private final KeyguardViewController mKeyguardViewController; Loading @@ -112,6 +115,12 @@ public class LockIconViewController extends ViewController<LockIconView> impleme @Nullable private final Vibrator mVibrator; @Nullable private final AuthRippleController mAuthRippleController; // Tracks the velocity of a touch to help filter out the touches that move too fast. private VelocityTracker mVelocityTracker; // The ID of the pointer for which ACTION_DOWN has occurred. -1 means no pointer is active. private int mActivePointerId = -1; private VibrationEffect mTick; private boolean mIsDozing; private boolean mIsBouncerShowing; private boolean mRunningFPS; Loading @@ -122,6 +131,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme private boolean mUserUnlockedWithBiometric; private Runnable mCancelDelayedUpdateVisibilityRunnable; private Runnable mOnGestureDetectedRunnable; private Runnable mLongPressCancelRunnable; private boolean mUdfpsSupported; private float mHeightPixels; Loading Loading @@ -181,7 +191,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mView.setImageDrawable(mIcon); mUnlockedLabel = resources.getString(R.string.accessibility_unlock_button); mLockedLabel = resources.getString(R.string.accessibility_lock_icon); dumpManager.registerDumpable("LockIconViewController", this); dumpManager.registerDumpable(TAG, this); } @Override Loading Loading @@ -320,7 +330,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme getResources().getString(R.string.accessibility_enter_hint)); public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(v, info); if (isClickable()) { if (isActionable()) { if (mShowLockIcon) { info.addAction(mAccessibilityAuthenticateHint); } else if (mShowUnlockIcon) { Loading Loading @@ -475,7 +485,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme @Override public void onKeyguardVisibilityChanged(boolean showing) { // reset mIsBouncerShowing state in case it was preemptively set // onAffordanceClick // onLongPress mIsBouncerShowing = mKeyguardViewController.isBouncerShowing(); updateVisibility(); } Loading Loading @@ -569,74 +579,102 @@ public class LockIconViewController extends ViewController<LockIconView> impleme } }; private final GestureDetector mGestureDetector = new GestureDetector(new SimpleOnGestureListener() { public boolean onDown(MotionEvent e) { if (!isClickable()) { mDownDetected = false; /** * Handles the touch if it is within the lock icon view and {@link #isActionable()} is true. * Subsequently, will trigger {@link #onLongPress()} if a touch is continuously in the lock icon * area for {@link #LONG_PRESS_TIMEOUT} ms. * * Touch speed debouncing mimics logic from the velocity tracker in {@link UdfpsController}. */ public boolean onTouchEvent(MotionEvent event, Runnable onGestureDetectedRunnable) { if (!onInterceptTouchEvent(event)) { cancelTouches(); return false; } // intercept all following touches until we see MotionEvent.ACTION_CANCEL UP or // MotionEvent.ACTION_UP (see #onTouchEvent) mOnGestureDetectedRunnable = onGestureDetectedRunnable; switch(event.getActionMasked()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_HOVER_ENTER: if (mVibrator != null && !mDownDetected) { if (mTick == null) { mTick = UdfpsController.lowTick(getContext(), true, LONG_PRESS_TIMEOUT); } mVibrator.vibrate( Process.myUid(), getContext().getOpPackageName(), UdfpsController.EFFECT_CLICK, "lockIcon-onDown", mTick, "lock-icon-tick", VIBRATION_SONIFICATION_ATTRIBUTES); } mDownDetected = true; return true; } public void onLongPress(MotionEvent e) { if (!wasClickableOnDownEvent()) { return; // The pointer that causes ACTION_DOWN is always at index 0. // We need to persist its ID to track it during ACTION_MOVE that could include // data for many other pointers because of multi-touch support. mActivePointerId = event.getPointerId(0); if (mVelocityTracker == null) { // To simplify the lifecycle of the velocity tracker, make sure it's never null // after ACTION_DOWN, and always null after ACTION_CANCEL or ACTION_UP. mVelocityTracker = VelocityTracker.obtain(); } else { // ACTION_UP or ACTION_CANCEL is not guaranteed to be called before a new // ACTION_DOWN, in that case we should just reuse the old instance. mVelocityTracker.clear(); } mVelocityTracker.addMovement(event); if (onAffordanceClick() && mVibrator != null) { // only vibrate if the click went through and wasn't intercepted by falsing mVibrator.vibrate( Process.myUid(), getContext().getOpPackageName(), UdfpsController.EFFECT_CLICK, "lockIcon-onLongPress", VIBRATION_SONIFICATION_ATTRIBUTES); } mDownDetected = true; mLongPressCancelRunnable = mExecutor.executeDelayed( this::onLongPress, LONG_PRESS_TIMEOUT); break; case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_HOVER_MOVE: mVelocityTracker.addMovement(event); // Compute pointer velocity in pixels per second. mVelocityTracker.computeCurrentVelocity(1000); float velocity = UdfpsController.computePointerSpeed(mVelocityTracker, mActivePointerId); if (event.getClassification() != MotionEvent.CLASSIFICATION_DEEP_PRESS && UdfpsController.exceedsVelocityThreshold(velocity)) { Log.v(TAG, "lock icon long-press rescheduled due to " + "high pointer velocity=" + velocity); mLongPressCancelRunnable.run(); mLongPressCancelRunnable = mExecutor.executeDelayed( this::onLongPress, LONG_PRESS_TIMEOUT); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_HOVER_EXIT: cancelTouches(); break; } public boolean onSingleTapUp(MotionEvent e) { if (!wasClickableOnDownEvent()) { return false; } onAffordanceClick(); return true; } public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { if (!wasClickableOnDownEvent()) { /** * Intercepts the touch if the onDown event and current event are within this lock icon view's * bounds. */ public boolean onInterceptTouchEvent(MotionEvent event) { if (!inLockIconArea(event) || !isActionable()) { return false; } onAffordanceClick(); if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { return true; } private boolean wasClickableOnDownEvent() { return mDownDetected; } /** * Whether we tried to launch the affordance. * * If falsing intercepts the click, returns false. */ private boolean onAffordanceClick() { private void onLongPress() { cancelTouches(); if (mFalsingManager.isFalseTouch(LOCK_ICON)) { return false; Log.v(TAG, "lock icon long-press rejected by the falsing manager."); return; } // pre-emptively set to true to hide view Loading @@ -649,49 +687,31 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mOnGestureDetectedRunnable.run(); } mKeyguardViewController.showBouncer(/* scrim */ true); return true; } }); /** * Send touch events to this view and handles it if the touch is within this view and we are * in a 'clickable' state * @return whether to intercept the touch event */ public boolean onTouchEvent(MotionEvent event, Runnable onGestureDetectedRunnable) { if (onInterceptTouchEvent(event)) { mOnGestureDetectedRunnable = onGestureDetectedRunnable; mGestureDetector.onTouchEvent(event); return true; } private void cancelTouches() { mDownDetected = false; return false; if (mLongPressCancelRunnable != null) { mLongPressCancelRunnable.run(); } /** * Intercepts the touch if the onDown event and current event are within this lock icon view's * bounds. */ public boolean onInterceptTouchEvent(MotionEvent event) { if (!inLockIconArea(event) || !isClickable()) { return false; if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { return true; if (mVibrator != null) { mVibrator.cancel(); } return mDownDetected; } private boolean inLockIconArea(MotionEvent event) { return mSensorTouchLocation.contains((int) event.getX(), (int) event.getY()) && (mView.getVisibility() == View.VISIBLE || (mAodFp != null && mAodFp.getVisibility() == View.VISIBLE)); } private boolean isClickable() { private boolean isActionable() { return mUdfpsSupported || mShowUnlockIcon; } Loading packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +39 −17 Original line number Diff line number Diff line Loading @@ -103,6 +103,7 @@ import kotlin.Unit; public class UdfpsController implements DozeReceiver { private static final String TAG = "UdfpsController"; private static final long AOD_INTERRUPT_TIMEOUT_MILLIS = 1000; private static final long DEFAULT_VIBRATION_DURATION = 1000; // milliseconds // Minimum required delay between consecutive touch logs in milliseconds. private static final long MIN_TOUCH_LOG_INTERVAL = 50; Loading Loading @@ -164,8 +165,7 @@ public class UdfpsController implements DozeReceiver { private boolean mAttemptedToDismissKeyguard; private Set<Callback> mCallbacks = new HashSet<>(); // by default, use low tick private int mPrimitiveTick = VibrationEffect.Composition.PRIMITIVE_LOW_TICK; private static final int DEFAULT_TICK = VibrationEffect.Composition.PRIMITIVE_LOW_TICK; private final VibrationEffect mTick; @VisibleForTesting Loading Loading @@ -327,12 +327,23 @@ public class UdfpsController implements DozeReceiver { } } private static float computePointerSpeed(@NonNull VelocityTracker tracker, int pointerId) { /** * Calculate the pointer speed given a velocity tracker and the pointer id. * This assumes that the velocity tracker has already been passed all relevant motion events. */ public static float computePointerSpeed(@NonNull VelocityTracker tracker, int pointerId) { final float vx = tracker.getXVelocity(pointerId); final float vy = tracker.getYVelocity(pointerId); return (float) Math.sqrt(Math.pow(vx, 2.0) + Math.pow(vy, 2.0)); } /** * Whether the velocity exceeds the acceptable UDFPS debouncing threshold. */ public static boolean exceedsVelocityThreshold(float velocity) { return velocity > 750f; } private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Loading Loading @@ -467,7 +478,7 @@ public class UdfpsController implements DozeReceiver { final float v = computePointerSpeed(mVelocityTracker, mActivePointerId); final float minor = event.getTouchMinor(idx); final float major = event.getTouchMajor(idx); final boolean exceedsVelocityThreshold = v > 750f; final boolean exceedsVelocityThreshold = exceedsVelocityThreshold(v); final String touchInfo = String.format( "minor: %.1f, major: %.1f, v: %.1f, exceedsVelocityThreshold: %b", minor, major, v, exceedsVelocityThreshold); Loading Loading @@ -575,7 +586,7 @@ public class UdfpsController implements DozeReceiver { mConfigurationController = configurationController; mSystemClock = systemClock; mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; mTick = lowTick(); mTick = lowTick(context, false /* useShortRampup */, DEFAULT_VIBRATION_DURATION); mSensorProps = findFirstUdfps(); // At least one UDFPS sensor exists Loading Loading @@ -610,32 +621,43 @@ public class UdfpsController implements DozeReceiver { udfpsHapticsSimulator.setUdfpsController(this); } private VibrationEffect lowTick() { boolean useLowTickDefault = mContext.getResources() /** * Returns the continuous low tick effect that starts playing on the udfps finger-down event. */ public static VibrationEffect lowTick( Context context, boolean useShortRampUp, long duration ) { boolean useLowTickDefault = context.getResources() .getBoolean(R.bool.config_udfpsUseLowTick); int primitiveTick = DEFAULT_TICK; if (Settings.Global.getFloat( mContext.getContentResolver(), context.getContentResolver(), "tick-low", useLowTickDefault ? 1 : 0) == 0) { mPrimitiveTick = VibrationEffect.Composition.PRIMITIVE_TICK; primitiveTick = VibrationEffect.Composition.PRIMITIVE_TICK; } float tickIntensity = Settings.Global.getFloat( mContext.getContentResolver(), context.getContentResolver(), "tick-intensity", mContext.getResources().getFloat(R.dimen.config_udfpsTickIntensity)); context.getResources().getFloat(R.dimen.config_udfpsTickIntensity)); int tickDelay = Settings.Global.getInt( mContext.getContentResolver(), context.getContentResolver(), "tick-delay", mContext.getResources().getInteger(R.integer.config_udfpsTickDelay)); context.getResources().getInteger(R.integer.config_udfpsTickDelay)); VibrationEffect.Composition composition = VibrationEffect.startComposition(); composition.addPrimitive(mPrimitiveTick, tickIntensity, 0); int primitives = 1000 / tickDelay; composition.addPrimitive(primitiveTick, tickIntensity, 0); int primitives = (int) (duration / tickDelay); float[] rampUp = new float[]{.48f, .58f, .69f, .83f}; if (useShortRampUp) { rampUp = new float[]{.5f, .7f}; } for (int i = 0; i < rampUp.length; i++) { composition.addPrimitive(mPrimitiveTick, tickIntensity * rampUp[i], tickDelay); composition.addPrimitive(primitiveTick, tickIntensity * rampUp[i], tickDelay); } for (int i = rampUp.length; i < primitives; i++) { composition.addPrimitive(mPrimitiveTick, tickIntensity, tickDelay); composition.addPrimitive(primitiveTick, tickIntensity, tickDelay); } return composition.compose(); } Loading packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java +4 −1 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.systemui.classifier; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DIAGONAL_HORIZONTAL_ANGLE_RANGE; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DIAGONAL_VERTICAL_ANGLE_RANGE; import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE; import static com.android.systemui.classifier.Classifier.LOCK_ICON; import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE; import android.provider.DeviceConfig; Loading Loading @@ -71,7 +72,9 @@ class DiagonalClassifier extends FalsingClassifier { return Result.passed(0); } if (interactionType == LEFT_AFFORDANCE || interactionType == RIGHT_AFFORDANCE) { if (interactionType == LEFT_AFFORDANCE || interactionType == RIGHT_AFFORDANCE || interactionType == LOCK_ICON) { return Result.passed(0); } Loading Loading
packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +117 −97 Original line number Diff line number Diff line Loading @@ -35,13 +35,14 @@ import android.hardware.biometrics.SensorLocationInternal; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.media.AudioAttributes; import android.os.Process; import android.os.VibrationEffect; import android.os.Vibrator; import android.util.DisplayMetrics; import android.util.Log; import android.util.MathUtils; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; Loading Loading @@ -83,6 +84,7 @@ import javax.inject.Inject; */ @StatusBarComponent.StatusBarScope public class LockIconViewController extends ViewController<LockIconView> implements Dumpable { private static final String TAG = "LockIconViewController"; private static final float sDefaultDensity = (float) DisplayMetrics.DENSITY_DEVICE_STABLE / (float) DisplayMetrics.DENSITY_DEFAULT; private static final int sLockIconRadiusPx = (int) (sDefaultDensity * 36); Loading @@ -91,6 +93,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) .build(); private static final long LONG_PRESS_TIMEOUT = 150L; // milliseconds @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @NonNull private final KeyguardViewController mKeyguardViewController; Loading @@ -112,6 +115,12 @@ public class LockIconViewController extends ViewController<LockIconView> impleme @Nullable private final Vibrator mVibrator; @Nullable private final AuthRippleController mAuthRippleController; // Tracks the velocity of a touch to help filter out the touches that move too fast. private VelocityTracker mVelocityTracker; // The ID of the pointer for which ACTION_DOWN has occurred. -1 means no pointer is active. private int mActivePointerId = -1; private VibrationEffect mTick; private boolean mIsDozing; private boolean mIsBouncerShowing; private boolean mRunningFPS; Loading @@ -122,6 +131,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme private boolean mUserUnlockedWithBiometric; private Runnable mCancelDelayedUpdateVisibilityRunnable; private Runnable mOnGestureDetectedRunnable; private Runnable mLongPressCancelRunnable; private boolean mUdfpsSupported; private float mHeightPixels; Loading Loading @@ -181,7 +191,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mView.setImageDrawable(mIcon); mUnlockedLabel = resources.getString(R.string.accessibility_unlock_button); mLockedLabel = resources.getString(R.string.accessibility_lock_icon); dumpManager.registerDumpable("LockIconViewController", this); dumpManager.registerDumpable(TAG, this); } @Override Loading Loading @@ -320,7 +330,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme getResources().getString(R.string.accessibility_enter_hint)); public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(v, info); if (isClickable()) { if (isActionable()) { if (mShowLockIcon) { info.addAction(mAccessibilityAuthenticateHint); } else if (mShowUnlockIcon) { Loading Loading @@ -475,7 +485,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme @Override public void onKeyguardVisibilityChanged(boolean showing) { // reset mIsBouncerShowing state in case it was preemptively set // onAffordanceClick // onLongPress mIsBouncerShowing = mKeyguardViewController.isBouncerShowing(); updateVisibility(); } Loading Loading @@ -569,74 +579,102 @@ public class LockIconViewController extends ViewController<LockIconView> impleme } }; private final GestureDetector mGestureDetector = new GestureDetector(new SimpleOnGestureListener() { public boolean onDown(MotionEvent e) { if (!isClickable()) { mDownDetected = false; /** * Handles the touch if it is within the lock icon view and {@link #isActionable()} is true. * Subsequently, will trigger {@link #onLongPress()} if a touch is continuously in the lock icon * area for {@link #LONG_PRESS_TIMEOUT} ms. * * Touch speed debouncing mimics logic from the velocity tracker in {@link UdfpsController}. */ public boolean onTouchEvent(MotionEvent event, Runnable onGestureDetectedRunnable) { if (!onInterceptTouchEvent(event)) { cancelTouches(); return false; } // intercept all following touches until we see MotionEvent.ACTION_CANCEL UP or // MotionEvent.ACTION_UP (see #onTouchEvent) mOnGestureDetectedRunnable = onGestureDetectedRunnable; switch(event.getActionMasked()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_HOVER_ENTER: if (mVibrator != null && !mDownDetected) { if (mTick == null) { mTick = UdfpsController.lowTick(getContext(), true, LONG_PRESS_TIMEOUT); } mVibrator.vibrate( Process.myUid(), getContext().getOpPackageName(), UdfpsController.EFFECT_CLICK, "lockIcon-onDown", mTick, "lock-icon-tick", VIBRATION_SONIFICATION_ATTRIBUTES); } mDownDetected = true; return true; } public void onLongPress(MotionEvent e) { if (!wasClickableOnDownEvent()) { return; // The pointer that causes ACTION_DOWN is always at index 0. // We need to persist its ID to track it during ACTION_MOVE that could include // data for many other pointers because of multi-touch support. mActivePointerId = event.getPointerId(0); if (mVelocityTracker == null) { // To simplify the lifecycle of the velocity tracker, make sure it's never null // after ACTION_DOWN, and always null after ACTION_CANCEL or ACTION_UP. mVelocityTracker = VelocityTracker.obtain(); } else { // ACTION_UP or ACTION_CANCEL is not guaranteed to be called before a new // ACTION_DOWN, in that case we should just reuse the old instance. mVelocityTracker.clear(); } mVelocityTracker.addMovement(event); if (onAffordanceClick() && mVibrator != null) { // only vibrate if the click went through and wasn't intercepted by falsing mVibrator.vibrate( Process.myUid(), getContext().getOpPackageName(), UdfpsController.EFFECT_CLICK, "lockIcon-onLongPress", VIBRATION_SONIFICATION_ATTRIBUTES); } mDownDetected = true; mLongPressCancelRunnable = mExecutor.executeDelayed( this::onLongPress, LONG_PRESS_TIMEOUT); break; case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_HOVER_MOVE: mVelocityTracker.addMovement(event); // Compute pointer velocity in pixels per second. mVelocityTracker.computeCurrentVelocity(1000); float velocity = UdfpsController.computePointerSpeed(mVelocityTracker, mActivePointerId); if (event.getClassification() != MotionEvent.CLASSIFICATION_DEEP_PRESS && UdfpsController.exceedsVelocityThreshold(velocity)) { Log.v(TAG, "lock icon long-press rescheduled due to " + "high pointer velocity=" + velocity); mLongPressCancelRunnable.run(); mLongPressCancelRunnable = mExecutor.executeDelayed( this::onLongPress, LONG_PRESS_TIMEOUT); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_HOVER_EXIT: cancelTouches(); break; } public boolean onSingleTapUp(MotionEvent e) { if (!wasClickableOnDownEvent()) { return false; } onAffordanceClick(); return true; } public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { if (!wasClickableOnDownEvent()) { /** * Intercepts the touch if the onDown event and current event are within this lock icon view's * bounds. */ public boolean onInterceptTouchEvent(MotionEvent event) { if (!inLockIconArea(event) || !isActionable()) { return false; } onAffordanceClick(); if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { return true; } private boolean wasClickableOnDownEvent() { return mDownDetected; } /** * Whether we tried to launch the affordance. * * If falsing intercepts the click, returns false. */ private boolean onAffordanceClick() { private void onLongPress() { cancelTouches(); if (mFalsingManager.isFalseTouch(LOCK_ICON)) { return false; Log.v(TAG, "lock icon long-press rejected by the falsing manager."); return; } // pre-emptively set to true to hide view Loading @@ -649,49 +687,31 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mOnGestureDetectedRunnable.run(); } mKeyguardViewController.showBouncer(/* scrim */ true); return true; } }); /** * Send touch events to this view and handles it if the touch is within this view and we are * in a 'clickable' state * @return whether to intercept the touch event */ public boolean onTouchEvent(MotionEvent event, Runnable onGestureDetectedRunnable) { if (onInterceptTouchEvent(event)) { mOnGestureDetectedRunnable = onGestureDetectedRunnable; mGestureDetector.onTouchEvent(event); return true; } private void cancelTouches() { mDownDetected = false; return false; if (mLongPressCancelRunnable != null) { mLongPressCancelRunnable.run(); } /** * Intercepts the touch if the onDown event and current event are within this lock icon view's * bounds. */ public boolean onInterceptTouchEvent(MotionEvent event) { if (!inLockIconArea(event) || !isClickable()) { return false; if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { return true; if (mVibrator != null) { mVibrator.cancel(); } return mDownDetected; } private boolean inLockIconArea(MotionEvent event) { return mSensorTouchLocation.contains((int) event.getX(), (int) event.getY()) && (mView.getVisibility() == View.VISIBLE || (mAodFp != null && mAodFp.getVisibility() == View.VISIBLE)); } private boolean isClickable() { private boolean isActionable() { return mUdfpsSupported || mShowUnlockIcon; } Loading
packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +39 −17 Original line number Diff line number Diff line Loading @@ -103,6 +103,7 @@ import kotlin.Unit; public class UdfpsController implements DozeReceiver { private static final String TAG = "UdfpsController"; private static final long AOD_INTERRUPT_TIMEOUT_MILLIS = 1000; private static final long DEFAULT_VIBRATION_DURATION = 1000; // milliseconds // Minimum required delay between consecutive touch logs in milliseconds. private static final long MIN_TOUCH_LOG_INTERVAL = 50; Loading Loading @@ -164,8 +165,7 @@ public class UdfpsController implements DozeReceiver { private boolean mAttemptedToDismissKeyguard; private Set<Callback> mCallbacks = new HashSet<>(); // by default, use low tick private int mPrimitiveTick = VibrationEffect.Composition.PRIMITIVE_LOW_TICK; private static final int DEFAULT_TICK = VibrationEffect.Composition.PRIMITIVE_LOW_TICK; private final VibrationEffect mTick; @VisibleForTesting Loading Loading @@ -327,12 +327,23 @@ public class UdfpsController implements DozeReceiver { } } private static float computePointerSpeed(@NonNull VelocityTracker tracker, int pointerId) { /** * Calculate the pointer speed given a velocity tracker and the pointer id. * This assumes that the velocity tracker has already been passed all relevant motion events. */ public static float computePointerSpeed(@NonNull VelocityTracker tracker, int pointerId) { final float vx = tracker.getXVelocity(pointerId); final float vy = tracker.getYVelocity(pointerId); return (float) Math.sqrt(Math.pow(vx, 2.0) + Math.pow(vy, 2.0)); } /** * Whether the velocity exceeds the acceptable UDFPS debouncing threshold. */ public static boolean exceedsVelocityThreshold(float velocity) { return velocity > 750f; } private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Loading Loading @@ -467,7 +478,7 @@ public class UdfpsController implements DozeReceiver { final float v = computePointerSpeed(mVelocityTracker, mActivePointerId); final float minor = event.getTouchMinor(idx); final float major = event.getTouchMajor(idx); final boolean exceedsVelocityThreshold = v > 750f; final boolean exceedsVelocityThreshold = exceedsVelocityThreshold(v); final String touchInfo = String.format( "minor: %.1f, major: %.1f, v: %.1f, exceedsVelocityThreshold: %b", minor, major, v, exceedsVelocityThreshold); Loading Loading @@ -575,7 +586,7 @@ public class UdfpsController implements DozeReceiver { mConfigurationController = configurationController; mSystemClock = systemClock; mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; mTick = lowTick(); mTick = lowTick(context, false /* useShortRampup */, DEFAULT_VIBRATION_DURATION); mSensorProps = findFirstUdfps(); // At least one UDFPS sensor exists Loading Loading @@ -610,32 +621,43 @@ public class UdfpsController implements DozeReceiver { udfpsHapticsSimulator.setUdfpsController(this); } private VibrationEffect lowTick() { boolean useLowTickDefault = mContext.getResources() /** * Returns the continuous low tick effect that starts playing on the udfps finger-down event. */ public static VibrationEffect lowTick( Context context, boolean useShortRampUp, long duration ) { boolean useLowTickDefault = context.getResources() .getBoolean(R.bool.config_udfpsUseLowTick); int primitiveTick = DEFAULT_TICK; if (Settings.Global.getFloat( mContext.getContentResolver(), context.getContentResolver(), "tick-low", useLowTickDefault ? 1 : 0) == 0) { mPrimitiveTick = VibrationEffect.Composition.PRIMITIVE_TICK; primitiveTick = VibrationEffect.Composition.PRIMITIVE_TICK; } float tickIntensity = Settings.Global.getFloat( mContext.getContentResolver(), context.getContentResolver(), "tick-intensity", mContext.getResources().getFloat(R.dimen.config_udfpsTickIntensity)); context.getResources().getFloat(R.dimen.config_udfpsTickIntensity)); int tickDelay = Settings.Global.getInt( mContext.getContentResolver(), context.getContentResolver(), "tick-delay", mContext.getResources().getInteger(R.integer.config_udfpsTickDelay)); context.getResources().getInteger(R.integer.config_udfpsTickDelay)); VibrationEffect.Composition composition = VibrationEffect.startComposition(); composition.addPrimitive(mPrimitiveTick, tickIntensity, 0); int primitives = 1000 / tickDelay; composition.addPrimitive(primitiveTick, tickIntensity, 0); int primitives = (int) (duration / tickDelay); float[] rampUp = new float[]{.48f, .58f, .69f, .83f}; if (useShortRampUp) { rampUp = new float[]{.5f, .7f}; } for (int i = 0; i < rampUp.length; i++) { composition.addPrimitive(mPrimitiveTick, tickIntensity * rampUp[i], tickDelay); composition.addPrimitive(primitiveTick, tickIntensity * rampUp[i], tickDelay); } for (int i = rampUp.length; i < primitives; i++) { composition.addPrimitive(mPrimitiveTick, tickIntensity, tickDelay); composition.addPrimitive(primitiveTick, tickIntensity, tickDelay); } return composition.compose(); } Loading
packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java +4 −1 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.systemui.classifier; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DIAGONAL_HORIZONTAL_ANGLE_RANGE; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DIAGONAL_VERTICAL_ANGLE_RANGE; import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE; import static com.android.systemui.classifier.Classifier.LOCK_ICON; import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE; import android.provider.DeviceConfig; Loading Loading @@ -71,7 +72,9 @@ class DiagonalClassifier extends FalsingClassifier { return Result.passed(0); } if (interactionType == LEFT_AFFORDANCE || interactionType == RIGHT_AFFORDANCE) { if (interactionType == LEFT_AFFORDANCE || interactionType == RIGHT_AFFORDANCE || interactionType == LOCK_ICON) { return Result.passed(0); } Loading