From ccf92c65871ac50c883df93791956486181a778c Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Thu, 16 Jul 2020 16:08:12 -0700 Subject: [PATCH] Use TouchDelegate to listen for icon view taps on RecentsView Using a negative margin to move the icon view to stick out above the task prevents that part sticking out from receiving touches. They go directly through to RecentsView, which interprets it as a touch to home. Now we use a touch delegate that recents view passes touches to before processing it directly. Note we can't override should steal touches from children in LauncherRecentsView because the icon view isn't actually seen as a child of RecentsView if it's outside of the task view area. Fixes: 159820339 Test: Touching anywhere on the icon works. Scrolled to previous/next tasks and in different rotations and verified tap registers correctly. Change-Id: I4559c34b20079e06aac401e2c93ac36a74b653ea --- .../android/quickstep/views/RecentsView.java | 11 +++++ .../com/android/quickstep/views/TaskView.java | 44 ++++++++++++++++--- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index 027a737cef..ac9049f869 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -78,6 +78,7 @@ import android.view.HapticFeedbackConstants; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; +import android.view.TouchDelegate; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; @@ -648,6 +649,16 @@ public abstract class RecentsView extends PagedView @Override public boolean onTouchEvent(MotionEvent ev) { super.onTouchEvent(ev); + + TaskView taskView = getCurrentPageTaskView(); + if (taskView != null) { + TouchDelegate mChildTouchDelegate = taskView.getIconTouchDelegate(ev); + if (mChildTouchDelegate != null && mChildTouchDelegate.onTouchEvent(ev)) { + // Keep consuming events to pass to delegate + return true; + } + } + final int x = (int) ev.getX(); final int y = (int) ev.getY(); switch (ev.getAction()) { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java index 222f6e6728..2058a7f375 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java @@ -22,16 +22,19 @@ import static android.view.Gravity.CENTER_VERTICAL; import static android.view.Gravity.END; import static android.view.Gravity.START; import static android.view.Gravity.TOP; +import static android.view.Surface.ROTATION_180; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; import static android.widget.Toast.LENGTH_SHORT; import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION; import static com.android.launcher3.Utilities.comp; +import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor; import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; -import static com.android.launcher3.logging.StatsLogManager.LauncherEvent - .LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP; import android.animation.Animator; @@ -53,7 +56,9 @@ import android.os.Handler; import android.util.AttributeSet; import android.util.FloatProperty; import android.util.Log; +import android.view.MotionEvent; import android.view.Surface; +import android.view.TouchDelegate; import android.view.View; import android.view.ViewOutlineProvider; import android.view.accessibility.AccessibilityNodeInfo; @@ -78,6 +83,7 @@ import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.TransformingTouchDelegate; import com.android.launcher3.util.ViewPool.Reusable; import com.android.quickstep.RecentsModel; import com.android.quickstep.TaskIconCache; @@ -122,6 +128,13 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { public static final long SCALE_ICON_DURATION = 120; private static final long DIM_ANIM_DURATION = 700; + /** + * This technically can be a vanilla {@link TouchDelegate} class, however that class requires + * setting the touch bounds at construction, so we'd repeatedly be created many instances + * unnecessarily as scrolling occurs, whereas {@link TransformingTouchDelegate} allows touch + * delegated bounds only to be updated. + */ + private TransformingTouchDelegate mIconTouchDelegate; private static final List SYSTEM_GESTURE_EXCLUSION_RECT = Collections.singletonList(new Rect()); @@ -186,6 +199,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { private int mStackHeight; private View mContextualChipWrapper; private View mContextualChip; + private final float[] mIconCenterCoords = new float[2]; public TaskView(Context context) { this(context, null); @@ -246,6 +260,26 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { super.onFinishInflate(); mSnapshotView = findViewById(R.id.snapshot); mIconView = findViewById(R.id.icon); + mIconTouchDelegate = new TransformingTouchDelegate(mIconView); + } + + public TouchDelegate getIconTouchDelegate(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + computeAndSetIconTouchDelegate(); + } + return mIconTouchDelegate; + } + + private void computeAndSetIconTouchDelegate() { + float iconHalfSize = mIconView.getWidth() / 2f; + mIconCenterCoords[0] = mIconCenterCoords[1] = iconHalfSize; + getDescendantCoordRelativeToAncestor(mIconView, mActivity.getDragLayer(), mIconCenterCoords, + false); + mIconTouchDelegate.setBounds( + (int) (mIconCenterCoords[0] - iconHalfSize), + (int) (mIconCenterCoords[1] - iconHalfSize), + (int) (mIconCenterCoords[0] + iconHalfSize), + (int) (mIconCenterCoords[1] + iconHalfSize)); } /** @@ -468,18 +502,18 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { int thumbnailPadding = (int) getResources().getDimension(R.dimen.task_thumbnail_top_margin); LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams(); switch (orientationHandler.getRotation()) { - case Surface.ROTATION_90: + case ROTATION_90: iconParams.gravity = (isRtl ? START : END) | CENTER_VERTICAL; iconParams.rightMargin = -thumbnailPadding; iconParams.leftMargin = 0; iconParams.topMargin = snapshotParams.topMargin / 2; break; - case Surface.ROTATION_180: + case ROTATION_180: iconParams.gravity = BOTTOM | CENTER_HORIZONTAL; iconParams.bottomMargin = -thumbnailPadding; iconParams.leftMargin = iconParams.topMargin = iconParams.rightMargin = 0; break; - case Surface.ROTATION_270: + case ROTATION_270: iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL; iconParams.leftMargin = -thumbnailPadding; iconParams.rightMargin = 0;