From 4716913d96b821b757e40a1e1a96b492e5c5ebac Mon Sep 17 00:00:00 2001 From: Philip Quinn Date: Mon, 23 Mar 2020 19:04:55 -0700 Subject: [PATCH 1/2] Support deep press on notifications. In addition to handling CLASSIFICATION_DEEP_PRESS in SwipeHelper, touch slop calculations are updated for scroll/flick interactions that may be performed on notifications or the notification panel. Fixes: 148172385 Test: notifications expand when deep pressed Change-Id: I49f71f919d762ce1a5da145a9377c70422a66c87 --- .../com/android/systemui/ExpandHelper.java | 15 +++- .../src/com/android/systemui/SwipeHelper.java | 69 +++++++++++-------- .../systemui/statusbar/DragDownHelper.java | 14 +++- .../stack/NotificationStackScrollLayout.java | 23 +++++-- .../NotificationPanelViewController.java | 3 +- .../systemui/statusbar/phone/PanelView.java | 2 - .../statusbar/phone/PanelViewController.java | 17 ++++- 7 files changed, 95 insertions(+), 48 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java index e2b12daf441e0..59af458e24025 100644 --- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java +++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java @@ -86,7 +86,8 @@ public class ExpandHelper implements Gefingerpoken { private float mInitialTouchSpan; private float mLastFocusY; private float mLastSpanY; - private int mTouchSlop; + private final int mTouchSlop; + private final float mSlopMultiplier; private float mLastMotionY; private float mPullGestureMinXSpan; private Callback mCallback; @@ -177,6 +178,7 @@ public class ExpandHelper implements Gefingerpoken { final ViewConfiguration configuration = ViewConfiguration.get(mContext); mTouchSlop = configuration.getScaledTouchSlop(); + mSlopMultiplier = configuration.getAmbiguousGestureMultiplier(); mSGD = new ScaleGestureDetector(context, mScaleGestureListener); mFlingAnimationUtils = new FlingAnimationUtils(mContext.getResources().getDisplayMetrics(), @@ -258,6 +260,13 @@ public class ExpandHelper implements Gefingerpoken { mScrollAdapter = adapter; } + private float getTouchSlop(MotionEvent event) { + // Adjust the touch slop if another gesture may be being performed. + return event.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE + ? mTouchSlop * mSlopMultiplier + : mTouchSlop; + } + @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (!isEnabled()) { @@ -303,7 +312,7 @@ public class ExpandHelper implements Gefingerpoken { if (mWatchingForPull) { final float yDiff = ev.getRawY() - mInitialTouchY; final float xDiff = ev.getRawX() - mInitialTouchX; - if (yDiff > mTouchSlop && yDiff > Math.abs(xDiff)) { + if (yDiff > getTouchSlop(ev) && yDiff > Math.abs(xDiff)) { if (DEBUG) Log.v(TAG, "got venetian gesture (dy=" + yDiff + "px)"); mWatchingForPull = false; if (mResizedView != null && !isFullyExpanded(mResizedView)) { @@ -431,7 +440,7 @@ public class ExpandHelper implements Gefingerpoken { if (mWatchingForPull) { final float yDiff = ev.getRawY() - mInitialTouchY; final float xDiff = ev.getRawX() - mInitialTouchX; - if (yDiff > mTouchSlop && yDiff > Math.abs(xDiff)) { + if (yDiff > getTouchSlop(ev) && yDiff > Math.abs(xDiff)) { if (DEBUG) Log.v(TAG, "got venetian gesture (dy=" + yDiff + "px)"); mWatchingForPull = false; if (mResizedView != null && !isFullyExpanded(mResizedView)) { diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index 02a452182d366..d17ca4041b31e 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -69,6 +69,7 @@ public class SwipeHelper implements Gefingerpoken { private final FlingAnimationUtils mFlingAnimationUtils; private float mPagingTouchSlop; + private final float mSlopMultiplier; private final Callback mCallback; private final int mSwipeDirection; private final VelocityTracker mVelocityTracker; @@ -84,11 +85,28 @@ public class SwipeHelper implements Gefingerpoken { private float mTranslation = 0; private boolean mMenuRowIntercepting; - private boolean mLongPressSent; - private Runnable mWatchLongPress; private final long mLongPressTimeout; + private boolean mLongPressSent; + private final float[] mDownLocation = new float[2]; + private final Runnable mPerformLongPress = new Runnable() { + + private final int[] mViewOffset = new int[2]; + + @Override + public void run() { + if (mCurrView != null && !mLongPressSent) { + mLongPressSent = true; + if (mCurrView instanceof ExpandableNotificationRow) { + mCurrView.getLocationOnScreen(mViewOffset); + final int x = (int) mDownLocation[0] - mViewOffset[0]; + final int y = (int) mDownLocation[1] - mViewOffset[1]; + mCurrView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); + ((ExpandableNotificationRow) mCurrView).doLongClickCallback(x, y); + } + } + } + }; - final private int[] mTmpPos = new int[2]; private final int mFalsingThreshold; private boolean mTouchAboveFalsingThreshold; private boolean mDisableHwLayers; @@ -102,7 +120,9 @@ public class SwipeHelper implements Gefingerpoken { mHandler = new Handler(); mSwipeDirection = swipeDirection; mVelocityTracker = VelocityTracker.obtain(); - mPagingTouchSlop = ViewConfiguration.get(context).getScaledPagingTouchSlop(); + final ViewConfiguration configuration = ViewConfiguration.get(context); + mPagingTouchSlop = configuration.getScaledPagingTouchSlop(); + mSlopMultiplier = configuration.getScaledAmbiguousGestureMultiplier(); // Extra long-press! mLongPressTimeout = (long) (ViewConfiguration.getLongPressTimeout() * 1.5f); @@ -255,10 +275,7 @@ public class SwipeHelper implements Gefingerpoken { } public void cancelLongPress() { - if (mWatchLongPress != null) { - mHandler.removeCallbacks(mWatchLongPress); - mWatchLongPress = null; - } + mHandler.removeCallbacks(mPerformLongPress); } @Override @@ -287,27 +304,9 @@ public class SwipeHelper implements Gefingerpoken { mInitialTouchPos = getPos(ev); mPerpendicularInitialTouchPos = getPerpendicularPos(ev); mTranslation = getTranslation(mCurrView); - if (mWatchLongPress == null) { - mWatchLongPress = new Runnable() { - @Override - public void run() { - if (mCurrView != null && !mLongPressSent) { - mLongPressSent = true; - mCurrView.getLocationOnScreen(mTmpPos); - final int x = (int) ev.getRawX() - mTmpPos[0]; - final int y = (int) ev.getRawY() - mTmpPos[1]; - if (mCurrView instanceof ExpandableNotificationRow) { - mCurrView.sendAccessibilityEvent( - AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); - ExpandableNotificationRow currRow = - (ExpandableNotificationRow) mCurrView; - currRow.doLongClickCallback(x, y); - } - } - } - }; - } - mHandler.postDelayed(mWatchLongPress, mLongPressTimeout); + mDownLocation[0] = ev.getRawX(); + mDownLocation[1] = ev.getRawY(); + mHandler.postDelayed(mPerformLongPress, mLongPressTimeout); } break; @@ -318,7 +317,12 @@ public class SwipeHelper implements Gefingerpoken { float perpendicularPos = getPerpendicularPos(ev); float delta = pos - mInitialTouchPos; float deltaPerpendicular = perpendicularPos - mPerpendicularInitialTouchPos; - if (Math.abs(delta) > mPagingTouchSlop + // Adjust the touch slop if another gesture may be being performed. + final float pagingTouchSlop = + ev.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE + ? mPagingTouchSlop * mSlopMultiplier + : mPagingTouchSlop; + if (Math.abs(delta) > pagingTouchSlop && Math.abs(delta) > Math.abs(deltaPerpendicular)) { if (mCallback.canChildBeDragged(mCurrView)) { mCallback.onBeginDrag(mCurrView); @@ -327,6 +331,11 @@ public class SwipeHelper implements Gefingerpoken { mTranslation = getTranslation(mCurrView); } cancelLongPress(); + } else if (ev.getClassification() == MotionEvent.CLASSIFICATION_DEEP_PRESS + && mHandler.hasCallbacks(mPerformLongPress)) { + // Accelerate the long press signal. + cancelLongPress(); + mPerformLongPress.run(); } } break; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java index 5adee40613e64..4fa782269c2d7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java @@ -48,7 +48,8 @@ public class DragDownHelper implements Gefingerpoken { private float mInitialTouchX; private float mInitialTouchY; private boolean mDraggingDown; - private float mTouchSlop; + private final float mTouchSlop; + private final float mSlopMultiplier; private DragDownCallback mDragDownCallback; private View mHost; private final int[] mTemp2 = new int[2]; @@ -62,7 +63,9 @@ public class DragDownHelper implements Gefingerpoken { FalsingManager falsingManager) { mMinDragDistance = context.getResources().getDimensionPixelSize( R.dimen.keyguard_drag_down_min_distance); - mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); + final ViewConfiguration configuration = ViewConfiguration.get(context); + mTouchSlop = configuration.getScaledTouchSlop(); + mSlopMultiplier = configuration.getScaledAmbiguousGestureMultiplier(); mCallback = callback; mDragDownCallback = dragDownCallback; mHost = host; @@ -85,7 +88,12 @@ public class DragDownHelper implements Gefingerpoken { case MotionEvent.ACTION_MOVE: final float h = y - mInitialTouchY; - if (h > mTouchSlop && h > Math.abs(x - mInitialTouchX)) { + // Adjust the touch slop if another gesture may be being performed. + final float touchSlop = + event.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE + ? mTouchSlop * mSlopMultiplier + : mTouchSlop; + if (h > touchSlop && h > Math.abs(x - mInitialTouchX)) { mFalsingManager.onNotificatonStartDraggingDown(); mDraggingDown = true; captureStartingChild(mInitialTouchX, mInitialTouchY); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 4d4a2ded08ca8..c63e19e009757 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -222,6 +222,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private boolean mIsScrollerBoundSet; private Runnable mFinishScrollingCallback; private int mTouchSlop; + private float mSlopMultiplier; private int mMinimumVelocity; private int mMaximumVelocity; private int mOverflingDistance; @@ -1022,6 +1023,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd setClipChildren(false); final ViewConfiguration configuration = ViewConfiguration.get(context); mTouchSlop = configuration.getScaledTouchSlop(); + mSlopMultiplier = configuration.getScaledAmbiguousGestureMultiplier(); mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); mOverflingDistance = configuration.getScaledOverflingDistance(); @@ -3744,6 +3746,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mLongPressListener = listener; } + private float getTouchSlop(MotionEvent event) { + // Adjust the touch slop if another gesture may be being performed. + return event.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE + ? mTouchSlop * mSlopMultiplier + : mTouchSlop; + } + @Override @ShadeViewRefactor(RefactorComponent.INPUT) public boolean onTouchEvent(MotionEvent ev) { @@ -3891,12 +3900,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd int deltaY = mLastMotionY - y; final int xDiff = Math.abs(x - mDownX); final int yDiff = Math.abs(deltaY); - if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) { + final float touchSlop = getTouchSlop(ev); + if (!mIsBeingDragged && yDiff > touchSlop && yDiff > xDiff) { setIsBeingDragged(true); if (deltaY > 0) { - deltaY -= mTouchSlop; + deltaY -= touchSlop; } else { - deltaY += mTouchSlop; + deltaY += touchSlop; } } if (mIsBeingDragged) { @@ -4083,8 +4093,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private void handleEmptySpaceClick(MotionEvent ev) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_MOVE: - if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop - || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop)) { + final float touchSlop = getTouchSlop(ev); + if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > touchSlop + || Math.abs(ev.getX() - mInitialTouchX) > touchSlop)) { mTouchIsClick = false; } break; @@ -4168,7 +4179,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd final int x = (int) ev.getX(pointerIndex); final int yDiff = Math.abs(y - mLastMotionY); final int xDiff = Math.abs(x - mDownX); - if (yDiff > mTouchSlop && yDiff > xDiff) { + if (yDiff > getTouchSlop(ev) && yDiff > xDiff) { setIsBeingDragged(true); mLastMotionY = y; mDownX = x; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index f9726d2d77f95..5588c24f2fd60 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -1021,7 +1021,8 @@ public class NotificationPanelViewController extends PanelViewController { trackMovement(event); return true; } - if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX) + if (Math.abs(h) > getTouchSlop(event) + && Math.abs(h) > Math.abs(x - mInitialTouchX) && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) { mQsTracking = true; onQsExpansionStarted(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index 2719a3278d244..481401b3eb7bb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -30,8 +30,6 @@ public abstract class PanelView extends FrameLayout { protected StatusBar mStatusBar; protected HeadsUpManagerPhone mHeadsUpManager; - protected int mTouchSlop; - protected KeyguardBottomAreaView mKeyguardBottomArea; private OnConfigurationChangedListener mOnConfigurationChangedListener; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java index 30367ed026acd..83cc4e33e2db2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -91,7 +91,8 @@ public abstract class PanelViewController { protected boolean mTracking; private boolean mTouchSlopExceeded; private int mTrackingPointer; - protected int mTouchSlop; + private int mTouchSlop; + private float mSlopMultiplier; protected boolean mHintAnimationRunning; private boolean mOverExpandedBeforeFling; private boolean mTouchAboveFalsingThreshold; @@ -260,11 +261,19 @@ public abstract class PanelViewController { protected void loadDimens() { final ViewConfiguration configuration = ViewConfiguration.get(mView.getContext()); mTouchSlop = configuration.getScaledTouchSlop(); + mSlopMultiplier = configuration.getScaledAmbiguousGestureMultiplier(); mHintDistance = mResources.getDimension(R.dimen.hint_move_distance); mUnlockFalsingThreshold = mResources.getDimensionPixelSize( R.dimen.unlock_falsing_threshold); } + protected float getTouchSlop(MotionEvent event) { + // Adjust the touch slop if another gesture may be being performed. + return event.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE + ? mTouchSlop * mSlopMultiplier + : mTouchSlop; + } + private void addMovement(MotionEvent event) { // Add movement to velocity tracker using raw screen X and Y coordinates instead // of window coordinates because the window frame may be moving at the same time. @@ -1111,7 +1120,8 @@ public abstract class PanelViewController { addMovement(event); if (scrolledToBottom || mTouchStartedInEmptyArea || mAnimatingOnDown) { float hAbs = Math.abs(h); - if ((h < -mTouchSlop || (mAnimatingOnDown && hAbs > mTouchSlop)) + float touchSlop = getTouchSlop(event); + if ((h < -touchSlop || (mAnimatingOnDown && hAbs > touchSlop)) && hAbs > Math.abs(x - mInitialTouchX)) { cancelHeightAnimator(); startExpandMotion(x, y, true /* startTracking */, mExpandedHeight); @@ -1228,7 +1238,8 @@ public abstract class PanelViewController { // If the panel was collapsed when touching, we only need to check for the // y-component of the gesture, as we have no conflicting horizontal gesture. - if (Math.abs(h) > mTouchSlop && (Math.abs(h) > Math.abs(x - mInitialTouchX) + if (Math.abs(h) > getTouchSlop(event) + && (Math.abs(h) > Math.abs(x - mInitialTouchX) || mIgnoreXTouchSlop)) { mTouchSlopExceeded = true; if (mGestureWaitForTouchSlop && !mTracking && !mCollapsedAndHeadsUpOnDown) { From 97f2e74c5c5c0d2480229c22000f58a588262951 Mon Sep 17 00:00:00 2001 From: Philip Quinn Date: Mon, 23 Mar 2020 19:07:09 -0700 Subject: [PATCH 2/2] Do not expand a notification if its guts are exposed. A notification may be expanded by both ExpandHelper and SwipeHelper if both the touch slop and long-press thresholds are crossed at the same time. This is quite difficult to trigger with conventional touches, but is quite easy to trigger with a deep press. To prevent this, the SwipeHelper should not receive touches if the ExpandHelper has expanded a notification (mExpandingNotification), and, conversely, the ExpandHelper should not receive touches if the SwipeHelper has exposed a notification (guts != null). Bug: 148172385 Test: deep press on notifications; guts are exposed without visual/haptic glitches Change-Id: Id81c9034bfa9c29b35d43a6125537ca5f9a2e9c4 --- .../stack/NotificationStackScrollLayout.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index c63e19e009757..be8af82f8baf4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -3756,12 +3756,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @Override @ShadeViewRefactor(RefactorComponent.INPUT) public boolean onTouchEvent(MotionEvent ev) { + NotificationGuts guts = mNotificationGutsManager.getExposedGuts(); boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL || ev.getActionMasked() == MotionEvent.ACTION_UP; handleEmptySpaceClick(ev); boolean expandWantsIt = false; boolean swipingInProgress = mSwipingInProgress; - if (mIsExpanded && !swipingInProgress && !mOnlyScrollingInThisMotion) { + if (mIsExpanded && !swipingInProgress && !mOnlyScrollingInThisMotion && guts == null) { if (isCancelOrUp) { mExpandHelper.onlyObserveMovements(false); } @@ -3787,7 +3788,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } // Check if we need to clear any snooze leavebehinds - NotificationGuts guts = mNotificationGutsManager.getExposedGuts(); if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts) && guts.getGutsContent() instanceof NotificationSnooze) { NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent(); @@ -4056,9 +4056,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd public boolean onInterceptTouchEvent(MotionEvent ev) { initDownStates(ev); handleEmptySpaceClick(ev); + + NotificationGuts guts = mNotificationGutsManager.getExposedGuts(); boolean expandWantsIt = false; boolean swipingInProgress = mSwipingInProgress; - if (!swipingInProgress && !mOnlyScrollingInThisMotion) { + if (!swipingInProgress && !mOnlyScrollingInThisMotion && guts == null) { expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev); } boolean scrollWantsIt = false; @@ -4075,7 +4077,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } // Check if we need to clear any snooze leavebehinds boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP; - NotificationGuts guts = mNotificationGutsManager.getExposedGuts(); if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt && !expandWantsIt && !scrollWantsIt) { mCheckForLeavebehind = false;