summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3/shortcuts
diff options
context:
space:
mode:
authorTony Wickham <twickham@google.com>2016-09-19 14:06:31 -0700
committerTony <twickham@google.com>2016-09-23 18:44:47 -0700
commit6e74e899d314663415f54895227bb79a51fd734b (patch)
tree184d41b9090fae07da4fb10c44f9d2a976144345 /src/com/android/launcher3/shortcuts
parent00341907b7326fbdc4c2a39e8b6cda16b7074daf (diff)
downloadandroid_packages_apps_Trebuchet-6e74e899d314663415f54895227bb79a51fd734b.tar.gz
android_packages_apps_Trebuchet-6e74e899d314663415f54895227bb79a51fd734b.tar.bz2
android_packages_apps_Trebuchet-6e74e899d314663415f54895227bb79a51fd734b.zip
Refactor shortcuts drag and drop.
- Instead of creating our own drag view within the container, and handling logic to determine when to start a real drag, we start the drag immediately and just defer onDragStart(). - To determine when the deferred drag should start, we add a DeferDragCondition to DragOptions. The default DeferDragCondition never defers a drag, but is overridden for apps with shortcuts to defer until the icon is dragged a given distance. - Because the drag is handled in DragController, including checking when to start the deferred drag, DeepShortcutsContainer no longer needs to handle touch events and ShortcutsContainerListener has been removed. This change has several immediate benefits: - The code is much cleaner, because it allows touch handling to be done by the DragController through the normal drag flow, without recreating logic in ShortcutsContainerListener/DeepShortcutContainer. - The janky second haptic feedback has been removed (now it vibrates when you long press, like everywhere else, but not again when the shortcuts close after dragging a distance). - Drops are animated, instead of just popping the icon back into place. Bug: 30769920 Bug: 30465972 Bug: 31533078 Change-Id: I679b412b72fbf6c3895d76963311eb5010c8e8db
Diffstat (limited to 'src/com/android/launcher3/shortcuts')
-rw-r--r--src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java150
-rw-r--r--src/com/android/launcher3/shortcuts/ShortcutsContainerListener.java252
2 files changed, 32 insertions, 370 deletions
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
index 5ef128811..2702d4e8e 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
@@ -34,16 +34,13 @@ import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.view.Gravity;
-import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.DecelerateInterpolator;
import android.widget.LinearLayout;
-import com.android.launcher3.AppInfo;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
@@ -59,7 +56,6 @@ import com.android.launcher3.LogAccelerateInterpolator;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
-import com.android.launcher3.Workspace;
import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.dragndrop.DragController;
@@ -85,18 +81,12 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
private final Launcher mLauncher;
private final DeepShortcutManager mDeepShortcutsManager;
- private final int mDragDeadzone;
private final int mStartDragThreshold;
private final ShortcutMenuAccessibilityDelegate mAccessibilityDelegate;
+ private final boolean mIsRtl;
private BubbleTextView mDeferredDragIcon;
- private int mActivePointerId;
- private int[] mTouchDown = null;
- private DragView mDragView;
- private float mLastX, mLastY;
- private float mDistanceDragged = 0;
private final Rect mTempRect = new Rect();
- private final int[] mTempXY = new int[2];
private Point mIconLastTouchPos = new Point();
private boolean mIsLeftAligned;
private boolean mIsAboveIcon;
@@ -106,16 +96,11 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
private boolean mDeferContainerRemoval;
private boolean mIsOpen;
- private boolean mSrcIconDragStarted;
- private boolean mIsRtl;
- private int mArrowHorizontalOffset;
-
public DeepShortcutsContainer(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mLauncher = Launcher.getLauncher(context);
mDeepShortcutsManager = LauncherAppState.getInstance().getShortcutManager();
- mDragDeadzone = ViewConfiguration.get(context).getScaledTouchSlop();
mStartDragThreshold = getResources().getDimensionPixelSize(
R.dimen.deep_shortcuts_start_drag_threshold);
mAccessibilityDelegate = new ShortcutMenuAccessibilityDelegate(mLauncher);
@@ -134,7 +119,7 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
final Resources resources = getResources();
final int arrowWidth = resources.getDimensionPixelSize(R.dimen.deep_shortcuts_arrow_width);
final int arrowHeight = resources.getDimensionPixelSize(R.dimen.deep_shortcuts_arrow_height);
- mArrowHorizontalOffset = resources.getDimensionPixelSize(
+ final int arrowHorizontalOffset = resources.getDimensionPixelSize(
R.dimen.deep_shortcuts_arrow_horizontal_offset);
final int arrowVerticalOffset = resources.getDimensionPixelSize(
R.dimen.deep_shortcuts_arrow_vertical_offset);
@@ -159,7 +144,7 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
orientAboutIcon(originalIcon, arrowHeight + arrowVerticalOffset);
// Add the arrow.
- mArrow = addArrowView(mArrowHorizontalOffset, arrowVerticalOffset, arrowWidth, arrowHeight);
+ mArrow = addArrowView(arrowHorizontalOffset, arrowVerticalOffset, arrowWidth, arrowHeight);
mArrow.setPivotX(arrowWidth / 2);
mArrow.setPivotY(mIsAboveIcon ? 0 : arrowHeight);
@@ -347,7 +332,6 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
mIsAboveIcon = y > dragLayer.getTop() + insets.top;
if (!mIsAboveIcon) {
y = mTempRect.top + icon.getPaddingTop() + iconHeight;
- icon.setTextVisibility(false);
}
// Insets are added later, so subtract them now.
@@ -393,7 +377,6 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
private void deferDrag(BubbleTextView originalIcon) {
mDeferredDragIcon = originalIcon;
- showDragView(originalIcon);
mLauncher.getDragController().addDragListener(this);
}
@@ -401,103 +384,39 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
return mDeferredDragIcon;
}
- private void showDragView(BubbleTextView originalIcon) {
- // TODO: implement support for Drawable DragViews so we don't have to create a bitmap here.
- Bitmap b = Utilities.createIconBitmap(originalIcon.getIcon(), mLauncher);
- float scale = mLauncher.getDragLayer().getLocationInDragLayer(originalIcon, mTempXY);
- int dragLayerX = Math.round(mTempXY[0] - (b.getWidth() - scale * originalIcon.getWidth()) / 2);
- int dragLayerY = Math.round(mTempXY[1] - (b.getHeight() - scale * b.getHeight()) / 2
- - Workspace.DRAG_BITMAP_PADDING / 2) + originalIcon.getPaddingTop();
- int motionDownX = mLauncher.getDragController().getMotionDown().x;
- int motionDownY = mLauncher.getDragController().getMotionDown().y;
- final int registrationX = motionDownX - dragLayerX;
- final int registrationY = motionDownY - dragLayerY;
-
- float scaleDps = getResources().getDimensionPixelSize(R.dimen.deep_shortcuts_drag_view_scale);
- mDragView = new DragView(mLauncher, b, registrationX, registrationY, 1f, scaleDps);
- mLastX = mLastY = mDistanceDragged = 0;
- mDragView.show(motionDownX, motionDownY);
- }
-
- public boolean onForwardedEvent(MotionEvent ev, int activePointerId, int[] touchDown) {
- mActivePointerId = activePointerId;
- mTouchDown = touchDown;
- return dispatchTouchEvent(ev);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- if (mDeferredDragIcon == null) {
- return false;
- }
-
- final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
- if (activePointerIndex < 0) {
- return false;
- }
- final float x = ev.getX(activePointerIndex);
- final float y = ev.getY(activePointerIndex);
-
-
- int action = ev.getAction();
- // The event was in this container's coordinate system before this,
- // but will be in DragLayer's coordinate system from now on.
- Utilities.translateEventCoordinates(this, mLauncher.getDragLayer(), ev);
- final int dragLayerX = (int) ev.getX();
- final int dragLayerY = (int) ev.getY();
- if (action == MotionEvent.ACTION_MOVE) {
- if (mLastX != 0 || mLastY != 0) {
- mDistanceDragged += Math.hypot(mLastX - x, mLastY - y);
- }
- mLastX = x;
- mLastY = y;
-
- if (shouldStartDeferredDrag((int) x, (int) y)) {
- mSrcIconDragStarted = true;
- cleanupDeferredDrag(true);
- mDeferredDragIcon.getParent().requestDisallowInterceptTouchEvent(false);
- mDeferredDragIcon.getOnLongClickListener().onLongClick(mDeferredDragIcon);
- mLauncher.getDragController().onTouchEvent(ev);
- return true;
- } else if (mDistanceDragged > mDragDeadzone) {
- // After dragging further than a small deadzone,
- // have the drag view follow the user's finger.
- mDragView.setVisibility(VISIBLE);
- mDragView.move(dragLayerX, dragLayerY);
- mDeferredDragIcon.setVisibility(INVISIBLE);
- }
- } else if (action == MotionEvent.ACTION_UP) {
- cleanupDeferredDrag(true);
- mLauncher.getUserEventDispatcher().logDeepShortcutsOpen(mDeferredDragIcon);
- } else if (action == MotionEvent.ACTION_CANCEL) {
- // Do not change the source icon visibility if we are already dragging the source icon.
- cleanupDeferredDrag(!mSrcIconDragStarted);
- }
- return true;
- }
-
/**
- * Determines whether the deferred drag should be started based on touch coordinates
- * relative to the original icon and the shortcuts container.
+ * Determines when the deferred drag should be started.
*
* Current behavior:
* - Start the drag if the touch passes a certain distance from the original touch down.
- *
- * @param x the x touch coordinate relative to this container
- * @param y the y touch coordinate relative to this container
*/
- private boolean shouldStartDeferredDrag(int x, int y) {
- double distFromTouchDown = Math.hypot(x - mTouchDown[0], y - mTouchDown[1]);
- return distFromTouchDown > mStartDragThreshold;
- }
+ public DragOptions.DeferDragCondition createDeferDragCondition(final Runnable onDragStart) {
+ return new DragOptions.DeferDragCondition() {
+ @Override
+ public boolean shouldStartDeferredDrag(double distanceDragged) {
+ return distanceDragged > mStartDragThreshold;
+ }
- private void cleanupDeferredDrag(boolean updateSrcVisibility) {
- if (mDragView != null) {
- mDragView.remove();
- }
- if (updateSrcVisibility) {
- mDeferredDragIcon.setVisibility(VISIBLE);
- }
+ @Override
+ public void onDeferredDragStart() {
+ mDeferredDragIcon.setVisibility(INVISIBLE);
+ }
+
+ @Override
+ public void onDropBeforeDeferredDrag() {
+ mLauncher.getUserEventDispatcher().logDeepShortcutsOpen(mDeferredDragIcon);
+ if (!mIsAboveIcon) {
+ mDeferredDragIcon.setTextVisibility(false);
+ }
+ }
+
+ @Override
+ public void onDragStart() {
+ if (onDragStart != null) {
+ onDragStart.run();
+ }
+ }
+ };
}
@Override
@@ -581,9 +500,7 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
@Override
public void onDragEnd() {
- if (mIsOpen) {
- animateClose();
- } else {
+ if (!mIsOpen) {
if (mOpenCloseAnimator != null) {
// Close animation is running.
mDeferContainerRemoval = false;
@@ -594,6 +511,7 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
}
}
}
+ mDeferredDragIcon.setVisibility(VISIBLE);
}
@Override
@@ -701,8 +619,6 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
}
mIsOpen = false;
mDeferContainerRemoval = false;
- // Make the original icon visible in All Apps, but not in Workspace or Folders.
- cleanupDeferredDrag(mDeferredDragIcon.getTag() instanceof AppInfo);
boolean isInHotseat = ((ItemInfo) mDeferredDragIcon.getTag()).container
== LauncherSettings.Favorites.CONTAINER_HOTSEAT;
mDeferredDragIcon.setTextVisibility(!isInHotseat);
@@ -734,8 +650,6 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
container.setVisibility(View.INVISIBLE);
launcher.getDragLayer().addView(container);
container.populateAndShow(icon, ids);
- icon.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
return container;
}
return null;
diff --git a/src/com/android/launcher3/shortcuts/ShortcutsContainerListener.java b/src/com/android/launcher3/shortcuts/ShortcutsContainerListener.java
deleted file mode 100644
index 31f096990..000000000
--- a/src/com/android/launcher3/shortcuts/ShortcutsContainerListener.java
+++ /dev/null
@@ -1,252 +0,0 @@
-package com.android.launcher3.shortcuts;
-
-import android.os.SystemClock;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.CheckLongPressHelper;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.dragndrop.DragLayer;
-
-/**
- * A {@link android.view.View.OnTouchListener} that creates a {@link DeepShortcutsContainer} and
- * forwards touch events to it. This listener should be put on any icon that supports shortcuts.
- */
-public class ShortcutsContainerListener implements View.OnTouchListener,
- View.OnAttachStateChangeListener {
-
- /** Scaled touch slop, used for detecting movement outside bounds. */
- private final float mScaledTouchSlop;
-
- /** Timeout before accepting a long-press to start forwarding. */
- private final int mLongPressTimeout;
-
- /** Source view from which events are forwarded. */
- private final BubbleTextView mSrcIcon;
-
- /** Runnable used to trigger forwarding on long-press. */
- private Runnable mTriggerLongPress;
-
- /** Whether this listener is currently forwarding touch events. */
- private boolean mForwarding;
-
- /** The id of the first pointer down in the current event stream. */
- private int mActivePointerId;
-
- private Launcher mLauncher;
- private DragLayer mDragLayer;
- /** The coordinates of the touch down, relative to the shortcuts container. */
- private final int[] mTouchDown;
- private boolean mHasMappedTouchDownToContainerCoord;
-
- /** If true, the gesture is not handled. The value is reset when next gesture starts. */
- private boolean mIgnoreCurrentGesture;
- private DeepShortcutsContainer mShortcutsContainer;
-
- public ShortcutsContainerListener(BubbleTextView icon) {
- mSrcIcon = icon;
- mScaledTouchSlop = ViewConfiguration.get(icon.getContext()).getScaledTouchSlop();
-
- mLongPressTimeout = CheckLongPressHelper.DEFAULT_LONG_PRESS_TIMEOUT;
-
- icon.addOnAttachStateChangeListener(this);
-
- mLauncher = Launcher.getLauncher(mSrcIcon.getContext());
- mDragLayer = mLauncher.getDragLayer();
- mTouchDown = new int[2];
- }
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- // There are no shortcuts associated with this item,
- // so return to normal touch handling.
- mIgnoreCurrentGesture = !mSrcIcon.hasDeepShortcuts();
-
- mTouchDown[0] = (int) event.getX();
- mTouchDown[1] = (int) event.getY();
- mDragLayer.getDescendantCoordRelativeToSelf(mSrcIcon, mTouchDown);
- mHasMappedTouchDownToContainerCoord = false;
- }
-
- if (mIgnoreCurrentGesture) {
- return false;
- }
-
- final boolean wasForwarding = mForwarding;
- final boolean forwarding;
- if (wasForwarding) {
- forwarding = onTouchForwarded(event) || !onForwardingStopped();
- } else {
- forwarding = onTouchObserved(event) && onForwardingStarted();
-
- if (forwarding) {
- // Make sure we cancel any ongoing source event stream.
- final long now = SystemClock.uptimeMillis();
- final MotionEvent e = MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL,
- 0.0f, 0.0f, 0);
- mSrcIcon.onTouchEvent(e);
- e.recycle();
- }
- }
-
- mForwarding = forwarding;
- return forwarding || wasForwarding;
- }
-
- @Override
- public void onViewAttachedToWindow(View v) {
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {
- mForwarding = false;
- mActivePointerId = MotionEvent.INVALID_POINTER_ID;
- }
-
- /**
- * Called when forwarding would like to start.
- * <p>
- * This is when we populate the shortcuts container and add it to the DragLayer.
- *
- * @return true to start forwarding, false otherwise
- */
- protected boolean onForwardingStarted() {
- mShortcutsContainer = DeepShortcutsContainer.showForIcon(mSrcIcon);
- return mShortcutsContainer != null;
- }
-
- /**
- * Called when forwarding would like to stop.
- *
- * @return true to stop forwarding, false otherwise
- */
- protected boolean onForwardingStopped() {
- mShortcutsContainer = null;
- return true;
- }
-
- /**
- * Observes motion events and determines when to start forwarding.
- *
- * @param srcEvent motion event in source view coordinates
- * @return true to start forwarding motion events, false otherwise
- */
- private boolean onTouchObserved(MotionEvent srcEvent) {
- final View src = mSrcIcon;
- if (!src.isEnabled()) {
- return false;
- }
-
- final int actionMasked = srcEvent.getActionMasked();
- switch (actionMasked) {
- case MotionEvent.ACTION_DOWN:
- mActivePointerId = srcEvent.getPointerId(0);
-
- if (mTriggerLongPress == null) {
- mTriggerLongPress = new TriggerLongPress();
- }
- src.postDelayed(mTriggerLongPress, mLongPressTimeout);
- break;
- case MotionEvent.ACTION_MOVE:
- final int activePointerIndex = srcEvent.findPointerIndex(mActivePointerId);
- if (activePointerIndex >= 0) {
- final float x = srcEvent.getX(activePointerIndex);
- final float y = srcEvent.getY(activePointerIndex);
-
- // Has the pointer moved outside of the view?
- if (!Utilities.pointInView(src, x, y, mScaledTouchSlop)) {
- clearCallbacks();
-
- return false;
- }
- }
- break;
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- clearCallbacks();
- break;
- }
-
- return false;
- }
-
- private void clearCallbacks() {
- if (mTriggerLongPress != null) {
- mSrcIcon.removeCallbacks(mTriggerLongPress);
- }
- }
-
- private void onLongPress() {
- clearCallbacks();
-
- final BubbleTextView src = mSrcIcon;
- if (!src.isEnabled() || !src.hasDeepShortcuts()) {
- // Ignore long-press if the view is disabled or doesn't have shortcuts.
- return;
- }
-
- if (!onForwardingStarted()) {
- return;
- }
-
- // Don't let the parent intercept our events.
- src.getParent().requestDisallowInterceptTouchEvent(true);
-
- // Make sure we cancel any ongoing source event stream.
- final long now = SystemClock.uptimeMillis();
- final MotionEvent e = MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL, 0, 0, 0);
- src.onTouchEvent(e);
- e.recycle();
-
- mForwarding = true;
- }
-
- /**
- * Handles forwarded motion events and determines when to stop
- * forwarding.
- *
- * @param srcEvent motion event in source view coordinates
- * @return true to continue forwarding motion events, false to cancel
- */
- private boolean onTouchForwarded(MotionEvent srcEvent) {
- final View src = mSrcIcon;
- final DeepShortcutsContainer dst = mShortcutsContainer;
- if (dst == null) {
- return false;
- }
- // Always cancel forwarding when the touch stream ends.
- final int action = srcEvent.getActionMasked();
- final boolean keepForwarding = action != MotionEvent.ACTION_UP
- && action != MotionEvent.ACTION_CANCEL;
- if (!dst.isLaidOut()) {
- return keepForwarding;
- }
-
- // Convert event to destination-local coordinates.
- final MotionEvent dstEvent = MotionEvent.obtainNoHistory(srcEvent);
- Utilities.translateEventCoordinates(src, dst, dstEvent);
-
- // Convert touch down event to destination-local coordinates.
- if (!mHasMappedTouchDownToContainerCoord) {
- mDragLayer.mapCoordInSelfToDescendent(dst, mTouchDown);
- mHasMappedTouchDownToContainerCoord = true;
- }
-
- // Forward converted event to destination view, then recycle it.
- final boolean handled = dst.onForwardedEvent(dstEvent, mActivePointerId, mTouchDown);
- dstEvent.recycle();
-
- return handled && keepForwarding;
- }
-
- private class TriggerLongPress implements Runnable {
- @Override
- public void run() {
- onLongPress();
- }
- }
-}