summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTony Wickham <twickham@google.com>2016-09-19 21:06:31 (GMT)
committerTony <twickham@google.com>2016-09-24 01:44:47 (GMT)
commit6e74e899d314663415f54895227bb79a51fd734b (patch)
tree184d41b9090fae07da4fb10c44f9d2a976144345
parent00341907b7326fbdc4c2a39e8b6cda16b7074daf (diff)
downloadandroid_packages_apps_Trebuchet-6e74e899d314663415f54895227bb79a51fd734b.zip
android_packages_apps_Trebuchet-6e74e899d314663415f54895227bb79a51fd734b.tar.gz
android_packages_apps_Trebuchet-6e74e899d314663415f54895227bb79a51fd734b.tar.bz2
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
-rw-r--r--res/values/dimens.xml2
-rw-r--r--src/com/android/launcher3/Launcher.java12
-rw-r--r--src/com/android/launcher3/Workspace.java6
-rw-r--r--src/com/android/launcher3/allapps/AllAppsContainerView.java31
-rw-r--r--src/com/android/launcher3/allapps/AllAppsGridAdapter.java10
-rw-r--r--src/com/android/launcher3/dragndrop/DragController.java37
-rw-r--r--src/com/android/launcher3/dragndrop/DragOptions.java40
-rw-r--r--src/com/android/launcher3/folder/Folder.java26
-rw-r--r--src/com/android/launcher3/folder/FolderPagedView.java8
-rw-r--r--src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java150
-rw-r--r--src/com/android/launcher3/shortcuts/ShortcutsContainerListener.java252
11 files changed, 163 insertions, 411 deletions
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 33466a8..367cee4 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -164,7 +164,7 @@
<dimen name="bg_pill_height">48dp</dimen>
<dimen name="bg_pill_radius">24dp</dimen>
<dimen name="deep_shortcuts_spacing">4dp</dimen>
- <dimen name="deep_shortcuts_drag_view_scale">6dp</dimen>
+ <dimen name="deferred_drag_view_scale">6dp</dimen>
<!-- an icon with shortcuts must be dragged this far before the container is removed. -->
<dimen name="deep_shortcuts_start_drag_threshold">16dp</dimen>
<dimen name="deep_shortcut_icon_size">36dp</dimen>
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index b6474e6..2b64d42 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -3109,7 +3109,17 @@ public class Launcher extends Activity
longClickCellInfo.cellX, longClickCellInfo.cellY));
if (!(itemUnderLongClick instanceof Folder || isAllAppsButton)) {
// User long pressed on an item
- mWorkspace.startDrag(longClickCellInfo, new DragOptions());
+ DragOptions dragOptions = new DragOptions();
+ if (itemUnderLongClick instanceof BubbleTextView) {
+ BubbleTextView icon = (BubbleTextView) itemUnderLongClick;
+ if (icon.hasDeepShortcuts()) {
+ DeepShortcutsContainer dsc = DeepShortcutsContainer.showForIcon(icon);
+ if (dsc != null) {
+ dragOptions.deferDragCondition = dsc.createDeferDragCondition(null);
+ }
+ }
+ }
+ mWorkspace.startDrag(longClickCellInfo, dragOptions);
}
}
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 0e25b1e..ea5401e 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -74,8 +74,6 @@ import com.android.launcher3.dragndrop.SpringLoadedDragController;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.graphics.DragPreviewProvider;
-import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutsContainerListener;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -1175,10 +1173,6 @@ public class Workspace extends PagedView
if (!(child instanceof Folder)) {
child.setHapticFeedbackEnabled(false);
child.setOnLongClickListener(mLongClickListener);
- if (child instanceof BubbleTextView && DeepShortcutManager.supportsShortcuts(info)) {
- // TODO: only add this listener if the item has shortcuts associated with it.
- child.setOnTouchListener(new ShortcutsContainerListener((BubbleTextView) child));
- }
}
if (child instanceof DropTarget) {
mDragController.addDropTarget((DropTarget) child);
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 290accb..5892787 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -36,6 +36,7 @@ import android.view.ViewGroup;
import com.android.launcher3.AppInfo;
import com.android.launcher3.BaseContainerView;
+import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeleteDropTarget;
import com.android.launcher3.DeviceProfile;
@@ -53,6 +54,7 @@ import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.graphics.TintedDrawableSpan;
import com.android.launcher3.keyboard.FocusedItemDecorator;
+import com.android.launcher3.shortcuts.DeepShortcutsContainer;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.ComponentKey;
@@ -542,13 +544,32 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
if (!mLauncher.isAppsViewVisible() ||
mLauncher.getWorkspace().isSwitchingState()) return false;
- // Return if global dragging is not enabled
+ // Return if global dragging is not enabled or we are already dragging
if (!mLauncher.isDraggingEnabled()) return false;
+ if (mLauncher.getDragController().isDragging()) return false;
// Start the drag
- mLauncher.getWorkspace().beginDragShared(v, this, new DragOptions());
- // Enter spring loaded mode
- mLauncher.enterSpringLoadedDragMode();
+ DragOptions dragOptions = new DragOptions();
+ if (v instanceof BubbleTextView) {
+ final BubbleTextView icon = (BubbleTextView) v;
+ if (icon.hasDeepShortcuts()) {
+ DeepShortcutsContainer dsc = DeepShortcutsContainer.showForIcon(icon);
+ if (dsc != null) {
+ dragOptions.deferDragCondition = dsc.createDeferDragCondition(new Runnable() {
+ @Override
+ public void run() {
+ icon.setVisibility(VISIBLE);
+ }
+ });
+ }
+ }
+ }
+ mLauncher.getWorkspace().beginDragShared(v, this, dragOptions);
+ if (FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) {
+ // Enter spring loaded mode (the new workspace does this in
+ // onDragStart(), so we don't want to do it here)
+ mLauncher.enterSpringLoadedDragMode();
+ }
return false;
}
@@ -598,7 +619,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
// target layout we were dropping on.
if (!success) {
boolean showOutOfSpaceMessage = false;
- if (target instanceof Workspace) {
+ if (target instanceof Workspace && !mLauncher.getDragController().isDeferringDrag()) {
int currentScreen = mLauncher.getCurrentWorkspaceScreen();
Workspace workspace = (Workspace) target;
CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen);
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 39ab58b..7b6aef1 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -42,8 +42,6 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutsContainerListener;
import java.util.HashMap;
import java.util.List;
@@ -503,10 +501,6 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
AppInfo info = mApps.getAdapterItems().get(position).appInfo;
BubbleTextView icon = (BubbleTextView) holder.mContent;
icon.applyFromApplicationInfo(info);
- if (DeepShortcutManager.supportsShortcuts(info)) {
- // TODO: only add this listener if the item has shortcuts associated with it.
- icon.setOnTouchListener(new ShortcutsContainerListener(icon));
- }
icon.setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
break;
}
@@ -514,10 +508,6 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
AppInfo info = mApps.getAdapterItems().get(position).appInfo;
BubbleTextView icon = (BubbleTextView) holder.mContent;
icon.applyFromApplicationInfo(info);
- if (DeepShortcutManager.supportsShortcuts(info)) {
- // TODO: only add this listener if the item has shortcuts associated with it.
- icon.setOnTouchListener(new ShortcutsContainerListener(icon));
- }
icon.setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
break;
}
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index a93ee90..6eb7dcc 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -131,6 +131,8 @@ public class DragController implements DragDriver.EventListener, TouchController
protected final int mFlingToDeleteThresholdVelocity;
private VelocityTracker mVelocityTracker;
+ private boolean mIsDragDeferred;
+
/**
* Interface to receive notifications when a drag starts or stops
*/
@@ -228,9 +230,14 @@ public class DragController implements DragDriver.EventListener, TouchController
mDragObject = new DropTarget.DragObject();
+ mIsDragDeferred = !mOptions.deferDragCondition.shouldStartDeferredDrag(0);
+
final Resources res = mLauncher.getResources();
- final float scaleDps = FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND ?
- res.getDimensionPixelSize(R.dimen.dragViewScale) : 0f;
+ final float scaleDps = FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND
+ ? res.getDimensionPixelSize(R.dimen.dragViewScale)
+ : mIsDragDeferred
+ ? res.getDimensionPixelSize(R.dimen.deferred_drag_view_scale)
+ : 0f;
final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX,
registrationY, initialDragViewScale, scaleDps);
@@ -264,8 +271,10 @@ public class DragController implements DragDriver.EventListener, TouchController
dragView.show(mMotionDownX, mMotionDownY);
mDistanceSinceScroll = 0;
- for (DragListener listener : new ArrayList<>(mListeners)) {
- listener.onDragStart(mDragObject, mOptions);
+ if (!mIsDragDeferred) {
+ startDeferredDrag();
+ } else {
+ mOptions.deferDragCondition.onDeferredDragStart();
}
mLastTouch[0] = mMotionDownX;
@@ -275,8 +284,16 @@ public class DragController implements DragDriver.EventListener, TouchController
return dragView;
}
- public Point getMotionDown() {
- return new Point(mMotionDownX, mMotionDownY);
+ public boolean isDeferringDrag() {
+ return mIsDragDeferred;
+ }
+
+ public void startDeferredDrag() {
+ for (DragListener listener : new ArrayList<>(mListeners)) {
+ listener.onDragStart(mDragObject, mOptions);
+ }
+ mOptions.deferDragCondition.onDragStart();
+ mIsDragDeferred = false;
}
/**
@@ -518,6 +535,11 @@ public class DragController implements DragDriver.EventListener, TouchController
mLastTouch[0] = x;
mLastTouch[1] = y;
checkScrollState(x, y);
+
+ if (mIsDragDeferred && mOptions.deferDragCondition.shouldStartDeferredDrag(
+ Math.hypot(x - mMotionDownX, y - mMotionDownY))) {
+ startDeferredDrag();
+ }
}
public float getDistanceDragged() {
@@ -715,6 +737,9 @@ public class DragController implements DragDriver.EventListener, TouchController
mDragObject.dragSource.onDropCompleted(
dropTargetAsView, mDragObject, flingVel != null, accepted);
mLauncher.getUserEventDispatcher().logDragNDrop(mDragObject, dropTargetAsView);
+ if (mIsDragDeferred) {
+ mOptions.deferDragCondition.onDropBeforeDeferredDrag();
+ }
}
private DropTarget findDropTarget(int x, int y, int[] dropCoordinates) {
diff --git a/src/com/android/launcher3/dragndrop/DragOptions.java b/src/com/android/launcher3/dragndrop/DragOptions.java
index 3d52a48..dbf46f3 100644
--- a/src/com/android/launcher3/dragndrop/DragOptions.java
+++ b/src/com/android/launcher3/dragndrop/DragOptions.java
@@ -28,4 +28,44 @@ public class DragOptions {
/** Specifies the start location for the system DnD, null when using internal DnD */
public Point systemDndStartPoint = null;
+
+ /** Determines when a deferred drag should start. By default, drags aren't deferred at all. */
+ public DeferDragCondition deferDragCondition = new DeferDragCondition();
+
+ /**
+ * Specifies a condition that must be met before DragListener#onDragStart() is called.
+ * By default, there is no condition and onDragStart() is called immediately following
+ * DragController#startDrag().
+ *
+ * This condition can be overridden, and callbacks are provided for the following cases:
+ * - The drag starts, but onDragStart() is deferred (onDeferredDragStart()).
+ * - The drag ends before the condition is met (onDropBeforeDeferredDrag()).
+ * - The condition is met (onDragStart()).
+ */
+ public static class DeferDragCondition {
+ public boolean shouldStartDeferredDrag(double distanceDragged) {
+ return true;
+ }
+
+ /**
+ * The drag has started, but onDragStart() is deferred.
+ * This happens when shouldStartDeferredDrag() returns true.
+ */
+ public void onDeferredDragStart() {
+ // Do nothing.
+ }
+
+ /**
+ * User dropped before the deferred condition was met,
+ * i.e. before shouldStartDeferredDrag() returned true.
+ */
+ public void onDropBeforeDeferredDrag() {
+ // Do nothing
+ }
+
+ /** onDragStart() has been called, now we are in a normal drag. */
+ public void onDragStart() {
+ // Do nothing
+ }
+ }
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index b64d12c..a666b56 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -51,6 +51,7 @@ import android.widget.TextView;
import com.android.launcher3.Alarm;
import com.android.launcher3.AppInfo;
+import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DragSource;
@@ -77,6 +78,7 @@ import com.android.launcher3.dragndrop.DragController.DragListener;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.pageindicators.PageIndicatorDots;
+import com.android.launcher3.shortcuts.DeepShortcutsContainer;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.CircleRevealOutlineProvider;
@@ -279,7 +281,17 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
public boolean onLongClick(View v) {
// Return if global dragging is not enabled
if (!mLauncher.isDraggingEnabled()) return true;
- return startDrag(v, new DragOptions());
+ DragOptions dragOptions = new DragOptions();
+ if (v instanceof BubbleTextView) {
+ BubbleTextView icon = (BubbleTextView) v;
+ if (icon.hasDeepShortcuts()) {
+ DeepShortcutsContainer dsc = DeepShortcutsContainer.showForIcon(icon);
+ if (dsc != null) {
+ dragOptions.deferDragCondition = dsc.createDeferDragCondition(null);
+ }
+ }
+ }
+ return startDrag(v, dragOptions);
}
public boolean startDrag(View v, DragOptions options) {
@@ -1297,7 +1309,9 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mIsExternalDrag = false;
} else {
currentDragView = mCurrentDragView;
- mContent.addViewForRank(currentDragView, si, mEmptyCellRank);
+ if (!mDragController.isDeferringDrag()) {
+ mContent.addViewForRank(currentDragView, si, mEmptyCellRank);
+ }
}
if (d.dragView.hasDrawn()) {
@@ -1318,9 +1332,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mItemsInvalidated = true;
rearrangeChildren();
- // Temporarily suppress the listener, as we did all the work already here.
- try (SuppressInfoChanges s = new SuppressInfoChanges()) {
- mInfo.add(si, false);
+ if (!mDragController.isDeferringDrag()) {
+ // Temporarily suppress the listener, as we did all the work already here.
+ try (SuppressInfoChanges s = new SuppressInfoChanges()) {
+ mInfo.add(si, false);
+ }
}
// Clear the drag info, as it is no longer being dragged.
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 1171d48..c6b0671 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -44,10 +44,8 @@ import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.dragndrop.DragController;
-import com.android.launcher3.pageindicators.PageIndicator;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
-import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutsContainerListener;
+import com.android.launcher3.pageindicators.PageIndicator;
import com.android.launcher3.util.Thunk;
import java.util.ArrayList;
@@ -236,10 +234,6 @@ public class FolderPagedView extends PagedView {
textView.applyFromShortcutInfo(item, mIconCache);
textView.setOnClickListener(mFolder);
textView.setOnLongClickListener(mFolder);
- if (DeepShortcutManager.supportsShortcuts(item)) {
- // TODO: only add this listener if the item has shortcuts associated with it.
- textView.setOnTouchListener(new ShortcutsContainerListener(textView));
- }
textView.setOnFocusChangeListener(mFocusIndicatorHelper);
textView.setOnKeyListener(mKeyListener);
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
index 5ef1288..2702d4e 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 31f0969..0000000
--- 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();
- }
- }
-}