summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2009-04-07 21:08:40 -0700
committerJeff Sharkey <jsharkey@android.com>2009-04-08 16:44:27 -0700
commit70864289fba6daf07b8de98524cdfb765a62552d (patch)
tree5660819a8b529322c9c175c54f77be12b070a548
parentbb760cc21e6e66306a7524ed79454c676a491702 (diff)
downloadandroid_packages_apps_Trebuchet-70864289fba6daf07b8de98524cdfb765a62552d.tar.gz
android_packages_apps_Trebuchet-70864289fba6daf07b8de98524cdfb765a62552d.tar.bz2
android_packages_apps_Trebuchet-70864289fba6daf07b8de98524cdfb765a62552d.zip
Make Launcher more forgiving when dragging desktop items.
This change adds nearby-area searching when dragging desktop items into already-occupied cells. This approach tries harder to find a matching area, instead of strictly rejecting invalid moves. We also draw a "snag" during the drag to show where an item would be dropped, but only if we would drop into an alternative cell. This gives users better feedback about where things will drop. http://b/issue?id=1634887
-rw-r--r--res/values/colors.xml1
-rw-r--r--src/com/android/launcher/CellLayout.java108
-rw-r--r--src/com/android/launcher/DeleteZone.java5
-rw-r--r--src/com/android/launcher/DragLayer.java84
-rw-r--r--src/com/android/launcher/DropTarget.java38
-rw-r--r--src/com/android/launcher/FolderIcon.java5
-rw-r--r--src/com/android/launcher/UserFolder.java5
-rw-r--r--src/com/android/launcher/Workspace.java101
8 files changed, 271 insertions, 76 deletions
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 557494405..f9cb0c531 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -24,4 +24,5 @@
<color name="delete_color_filter">#A5FF0000</color>
<color name="appwidget_error_color">#fccc</color>
+ <color name="snag_callout_color">#f444</color>
</resources>
diff --git a/src/com/android/launcher/CellLayout.java b/src/com/android/launcher/CellLayout.java
index 91f04200a..fe6b193e1 100644
--- a/src/com/android/launcher/CellLayout.java
+++ b/src/com/android/launcher/CellLayout.java
@@ -174,7 +174,7 @@ public class CellLayout extends ViewGroup {
final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
final boolean[][] occupied = mOccupied;
- findOccupiedCells(xCount, yCount, occupied);
+ findOccupiedCells(xCount, yCount, occupied, null);
cellInfo.cell = null;
cellInfo.cellX = cellXY[0];
@@ -215,7 +215,7 @@ public class CellLayout extends ViewGroup {
final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
final boolean[][] occupied = mOccupied;
- findOccupiedCells(xCount, yCount, occupied);
+ findOccupiedCells(xCount, yCount, occupied, null);
findIntersectingVacantCells(info, info.cellX, info.cellY, xCount, yCount, occupied);
@@ -315,7 +315,7 @@ public class CellLayout extends ViewGroup {
return true;
}
- CellInfo findAllVacantCells(boolean[] occupiedCells) {
+ CellInfo findAllVacantCells(boolean[] occupiedCells, View ignoreView) {
final boolean portrait = mPortrait;
final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
@@ -329,7 +329,7 @@ public class CellLayout extends ViewGroup {
}
}
} else {
- findOccupiedCells(xCount, yCount, occupied);
+ findOccupiedCells(xCount, yCount, occupied, ignoreView);
}
CellInfo cellInfo = new CellInfo();
@@ -527,64 +527,72 @@ public class CellLayout extends ViewGroup {
super.setChildrenDrawnWithCacheEnabled(enabled);
}
- boolean acceptChildDrop(int x, int y, int cellHSpan, int cellVSpan, View cell) {
- int[] cellXY = mCellXY;
- pointToCellRounded(x, y, cellXY);
- int cellX = cellXY[0];
- int cellY = cellXY[1];
-
- return findCell(cellX, cellY, cellHSpan, cellVSpan, cell) == null;
- }
-
/**
- * Finds the first View intersecting with the specified cell. If the cell is outside
- * of the layout, this is returned.
- *
- * @param cellX The X location of the cell to test.
- * @param cellY The Y location of the cell to test.
- * @param cellHSpan The horizontal span of the cell to test.
- * @param cellVSpan The vertical span of the cell to test.
- * @param ignoreCell View to ignore during the test.
- *
- * @return Returns the first View intersecting with the specified cell, this if the cell
- * lies outside of this layout's grid or null if no View was found.
+ * Find a vacant area that will fit the given bounds nearest the requested
+ * cell location. Uses Euclidean distance to score multiple vacant areas.
+ *
+ * @param cellX The X location of the desired location.
+ * @param cellY The Y location of the desired location.
+ * @param spanX Horizontal span of the object.
+ * @param spanY Vertical span of the object.
+ * @param vacantCells Pre-computed set of vacant cells to search.
+ * @param recycle Previously returned value to possibly recycle.
+ * @return The X, Y cell of a vacant area that can contain this object,
+ * nearest the requested location.
*/
- View findCell(int cellX, int cellY, int cellHSpan, int cellVSpan, View ignoreCell) {
- if (cellX < 0 || cellX + cellHSpan > (mPortrait ? mShortAxisCells : mLongAxisCells) ||
- cellY < 0 || cellY + cellVSpan > (mPortrait ? mLongAxisCells : mShortAxisCells)) {
- return this;
+ int[] findNearestVacantArea(int pixelX, int pixelY, int spanX, int spanY,
+ CellInfo vacantCells, int[] recycle) {
+
+ // Keep track of best-scoring drop area
+ final int[] bestXY = recycle != null ? recycle : new int[2];
+ final int[] cellXY = mCellXY;
+ double bestDistance = Double.MAX_VALUE;
+
+ // Bail early if vacant cells aren't valid
+ if (!vacantCells.valid) {
+ return null;
}
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
- final View view = getChildAt(i);
- if (view == ignoreCell) {
+ // Look across all vacant cells for best fit
+ final int size = vacantCells.vacantCells.size();
+ for (int i = 0; i < size; i++) {
+ final CellInfo.VacantCell cell = vacantCells.vacantCells.get(i);
+
+ // Reject if vacant cell isn't our exact size
+ if (cell.spanX != spanX || cell.spanY != spanY) {
continue;
}
-
- final LayoutParams lp = (LayoutParams) view.getLayoutParams();
- if (cellX < lp.cellX + lp.cellHSpan && lp.cellX < cellX + cellHSpan &&
- cellY < lp.cellY + lp.cellVSpan && lp.cellY < cellY + cellVSpan) {
- return view;
+
+ // Score is center distance from requested pixel
+ cellToPoint(cell.cellX, cell.cellY, cellXY);
+
+ double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2) +
+ Math.pow(cellXY[1] - pixelY, 2));
+ if (distance <= bestDistance) {
+ bestDistance = distance;
+ bestXY[0] = cell.cellX;
+ bestXY[1] = cell.cellY;
}
}
- return null;
+ // Return null if no suitable location found
+ if (bestDistance < Double.MAX_VALUE) {
+ return bestXY;
+ } else {
+ return null;
+ }
}
-
+
/**
* Drop a child at the specified position
*
* @param child The child that is being dropped
- * @param cellX The child's new x location
- * @param cellY The child's new y location
+ * @param targetXY Destination area to move to
*/
- void onDropChild(View child, int cellX, int cellY) {
- int[] cellXY = mCellXY;
- pointToCellRounded(cellX, cellY, cellXY);
+ void onDropChild(View child, int[] targetXY) {
LayoutParams lp = (LayoutParams) child.getLayoutParams();
- lp.cellX = cellXY[0];
- lp.cellY = cellXY[1];
+ lp.cellX = targetXY[0];
+ lp.cellY = targetXY[1];
lp.isDragging = false;
mDragRect.setEmpty();
child.requestLayout();
@@ -688,7 +696,7 @@ public class CellLayout extends ViewGroup {
final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
final boolean[][] occupied = mOccupied;
- findOccupiedCells(xCount, yCount, occupied);
+ findOccupiedCells(xCount, yCount, occupied, null);
return findVacantCell(vacant, spanX, spanY, xCount, yCount, occupied);
}
@@ -723,7 +731,7 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
final boolean[][] occupied = mOccupied;
- findOccupiedCells(xCount, yCount, occupied);
+ findOccupiedCells(xCount, yCount, occupied, null);
final boolean[] flat = new boolean[xCount * yCount];
for (int y = 0; y < yCount; y++) {
@@ -735,7 +743,7 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
return flat;
}
- private void findOccupiedCells(int xCount, int yCount, boolean[][] occupied) {
+ private void findOccupiedCells(int xCount, int yCount, boolean[][] occupied, View ignoreView) {
for (int x = 0; x < xCount; x++) {
for (int y = 0; y < yCount; y++) {
occupied[x][y] = false;
@@ -745,7 +753,7 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
- if (child instanceof Folder) {
+ if (child instanceof Folder || child.equals(ignoreView)) {
continue;
}
LayoutParams lp = (LayoutParams) child.getLayoutParams();
diff --git a/src/com/android/launcher/DeleteZone.java b/src/com/android/launcher/DeleteZone.java
index 7f92c2334..02e8011d0 100644
--- a/src/com/android/launcher/DeleteZone.java
+++ b/src/com/android/launcher/DeleteZone.java
@@ -19,6 +19,7 @@ package com.android.launcher;
import android.widget.ImageView;
import android.content.Context;
import android.content.res.TypedArray;
+import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.TranslateAnimation;
@@ -77,6 +78,10 @@ public class DeleteZone extends ImageView implements DropTarget, DragController.
Object dragInfo) {
return true;
}
+
+ public Rect estimateDropLocation(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo, Rect recycle) {
+ return null;
+ }
public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo) {
final ItemInfo item = (ItemInfo) dragInfo;
diff --git a/src/com/android/launcher/DragLayer.java b/src/com/android/launcher/DragLayer.java
index b542de62a..b5b84b86e 100644
--- a/src/com/android/launcher/DragLayer.java
+++ b/src/com/android/launcher/DragLayer.java
@@ -113,8 +113,25 @@ public class DragLayer extends FrameLayout implements DragController {
private DropTarget mLastDropTarget;
private final Paint mTrashPaint = new Paint();
+ private final Paint mEstimatedPaint = new Paint();
private Paint mDragPaint;
+ /**
+ * If true, draw a "snag" showing where the object currently being dragged
+ * would end up if dropped from current location.
+ */
+ private static final boolean DRAW_TARGET_SNAG = false;
+
+ private Rect mEstimatedRect = new Rect();
+ private float[] mDragCenter = new float[2];
+ private float[] mEstimatedCenter = new float[2];
+ private boolean mDrawEstimated = false;
+
+ private int mTriggerWidth = -1;
+ private int mTriggerHeight = -1;
+
+ private static final int DISTANCE_DRAW_SNAG = 20;
+
private static final int ANIMATION_STATE_STARTING = 1;
private static final int ANIMATION_STATE_RUNNING = 2;
private static final int ANIMATION_STATE_DONE = 3;
@@ -141,6 +158,13 @@ public class DragLayer extends FrameLayout implements DragController {
final int srcColor = context.getResources().getColor(R.color.delete_color_filter);
mTrashPaint.setColorFilter(new PorterDuffColorFilter(srcColor, PorterDuff.Mode.SRC_ATOP));
+
+ // Make estimated paint area in gray
+ int snagColor = context.getResources().getColor(R.color.snag_callout_color);
+ mEstimatedPaint.setColor(snagColor);
+ mEstimatedPaint.setStrokeWidth(3);
+ mEstimatedPaint.setAntiAlias(true);
+
}
public void startDrag(View v, DragSource source, Object dragInfo, int dragAction) {
@@ -177,6 +201,9 @@ public class DragLayer extends FrameLayout implements DragController {
int width = viewBitmap.getWidth();
int height = viewBitmap.getHeight();
+ mTriggerWidth = width * 2 / 3;
+ mTriggerHeight = height * 2 / 3;
+
Matrix scale = new Matrix();
float scaleFactor = v.getWidth();
scaleFactor = (scaleFactor + DRAG_SCALE) /scaleFactor;
@@ -252,6 +279,13 @@ public class DragLayer extends FrameLayout implements DragController {
break;
}
} else {
+ // Only draw estimate drop "snag" when requested
+ if (DRAW_TARGET_SNAG && mDrawEstimated) {
+ canvas.drawLine(mDragCenter[0], mDragCenter[1], mEstimatedCenter[0], mEstimatedCenter[1], mEstimatedPaint);
+ canvas.drawCircle(mEstimatedCenter[0], mEstimatedCenter[1], 8, mEstimatedPaint);
+ }
+
+ // Draw actual icon being dragged
canvas.drawBitmap(mDragBitmap,
mScrollX + mLastMotionX - mTouchOffsetX - mBitmapOffsetX,
mScrollY + mLastMotionY - mTouchOffsetY - mBitmapOffsetY, mDragPaint);
@@ -355,8 +389,16 @@ public class DragLayer extends FrameLayout implements DragController {
left = (int) (scrollX + x - touchX - offsetX);
top = (int) (scrollY + y - touchY - offsetY);
+ // Invalidate current icon position
rect.union(left - 1, top - 1, left + width + 1, top + height + 1);
- invalidate(rect);
+
+ mDragCenter[0] = rect.centerX();
+ mDragCenter[1] = rect.centerY();
+
+ // Invalidate any old estimated location
+ if (DRAW_TARGET_SNAG && mDrawEstimated) {
+ rect.union(mEstimatedRect);
+ }
final int[] coordinates = mDropCoordinates;
DropTarget dropTarget = findDropTarget((int) x, (int) y, coordinates);
@@ -378,6 +420,33 @@ public class DragLayer extends FrameLayout implements DragController {
(int) mTouchOffsetX, (int) mTouchOffsetY, mDragInfo);
}
}
+
+ // Render estimated drop "snag" only outside of width
+ mDrawEstimated = false;
+ if (DRAW_TARGET_SNAG && dropTarget != null) {
+ Rect foundEstimate = dropTarget.estimateDropLocation(mDragSource,
+ (int) (scrollX + mLastMotionX), (int) (scrollY + mLastMotionY),
+ (int) mTouchOffsetX, (int) mTouchOffsetY, mDragInfo, mEstimatedRect);
+
+ if (foundEstimate != null) {
+ mEstimatedCenter[0] = foundEstimate.centerX();
+ mEstimatedCenter[1] = foundEstimate.centerY();
+
+ int deltaX = (int) Math.abs(mEstimatedCenter[0] - mDragCenter[0]);
+ int deltaY = (int) Math.abs(mEstimatedCenter[1] - mDragCenter[1]);
+
+ if (deltaX > mTriggerWidth || deltaY > mTriggerHeight) {
+ mDrawEstimated = true;
+ }
+ }
+ }
+
+ // Include new estimated area in invalidated rectangle
+ if (DRAW_TARGET_SNAG && mDrawEstimated) {
+ rect.union(mEstimatedRect);
+ }
+ invalidate(rect);
+
mLastDropTarget = dropTarget;
boolean inDragRegion = false;
@@ -478,9 +547,15 @@ public class DragLayer extends FrameLayout implements DragController {
}
if (target == null) {
if (child instanceof DropTarget) {
- dropCoordinates[0] = x;
- dropCoordinates[1] = y;
- return (DropTarget) child;
+ // Only consider this child if they will accept
+ DropTarget childTarget = (DropTarget) child;
+ if (childTarget.acceptDrop(mDragSource, x, y, 0, 0, mDragInfo)) {
+ dropCoordinates[0] = x;
+ dropCoordinates[1] = y;
+ return (DropTarget) child;
+ } else {
+ return null;
+ }
}
} else {
return target;
@@ -531,6 +606,7 @@ public class DragLayer extends FrameLayout implements DragController {
public void run() {
if (mDragScroller != null) {
+ mDrawEstimated = false;
if (mDirection == SCROLL_LEFT) {
mDragScroller.scrollLeft();
} else {
diff --git a/src/com/android/launcher/DropTarget.java b/src/com/android/launcher/DropTarget.java
index 812908906..4835323d8 100644
--- a/src/com/android/launcher/DropTarget.java
+++ b/src/com/android/launcher/DropTarget.java
@@ -16,6 +16,8 @@
package com.android.launcher;
+import android.graphics.Rect;
+
/**
* Interface defining an object that can receive a drag.
*
@@ -42,18 +44,38 @@ public interface DropTarget {
void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo);
/**
- * Indicates whether a drop action can occur at the specified location. The method
- * {@link #onDrop(DragSource, int, int, int, int, Object)} will be invoked on this
- * drop target only if this method returns true.
- *
+ * Check if a drop action can occur at, or near, the requested location.
+ * This may be called repeatedly during a drag, so any calls should return
+ * quickly.
+ *
* @param source DragSource where the drag started
* @param x X coordinate of the drop location
* @param y Y coordinate of the drop location
- * @param xOffset Horizontal offset with the object being dragged where the original touch happened
- * @param yOffset Vertical offset with the object being dragged where the original touch happened
+ * @param xOffset Horizontal offset with the object being dragged where the
+ * original touch happened
+ * @param yOffset Vertical offset with the object being dragged where the
+ * original touch happened
* @param dragInfo Data associated with the object being dragged
- *
- * return True if the drop is accepted, false otherwise.
+ * @return True if the drop will be accepted, false otherwise.
*/
boolean acceptDrop(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo);
+
+ /**
+ * Estimate the surface area where this object would land if dropped at the
+ * given location.
+ *
+ * @param source DragSource where the drag started
+ * @param x X coordinate of the drop location
+ * @param y Y coordinate of the drop location
+ * @param xOffset Horizontal offset with the object being dragged where the
+ * original touch happened
+ * @param yOffset Vertical offset with the object being dragged where the
+ * original touch happened
+ * @param dragInfo Data associated with the object being dragged
+ * @param recycle {@link Rect} object to be possibly recycled.
+ * @return Estimated area that would be occupied if object was dropped at
+ * the given location. Should return null if no estimate is found,
+ * or if this target doesn't provide estimations.
+ */
+ Rect estimateDropLocation(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo, Rect recycle);
}
diff --git a/src/com/android/launcher/FolderIcon.java b/src/com/android/launcher/FolderIcon.java
index 667f92ee1..a56101d51 100644
--- a/src/com/android/launcher/FolderIcon.java
+++ b/src/com/android/launcher/FolderIcon.java
@@ -18,6 +18,7 @@ package com.android.launcher;
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
@@ -69,6 +70,10 @@ public class FolderIcon extends BubbleTextView implements DropTarget {
&& item.container != mInfo.id;
}
+ public Rect estimateDropLocation(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo, Rect recycle) {
+ return null;
+ }
+
public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo) {
final ApplicationInfo item = (ApplicationInfo) dragInfo;
// TODO: update open folder that is looking at this data
diff --git a/src/com/android/launcher/UserFolder.java b/src/com/android/launcher/UserFolder.java
index 1044e969f..6cdfed950 100644
--- a/src/com/android/launcher/UserFolder.java
+++ b/src/com/android/launcher/UserFolder.java
@@ -1,6 +1,7 @@
package com.android.launcher;
import android.content.Context;
+import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
@@ -33,6 +34,10 @@ public class UserFolder extends Folder implements DropTarget {
return (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) && item.container != mInfo.id;
}
+
+ public Rect estimateDropLocation(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo, Rect recycle) {
+ return null;
+ }
public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo) {
final ApplicationInfo item = (ApplicationInfo) dragInfo;
diff --git a/src/com/android/launcher/Workspace.java b/src/com/android/launcher/Workspace.java
index 359767aea..12bdf7d0a 100644
--- a/src/com/android/launcher/Workspace.java
+++ b/src/com/android/launcher/Workspace.java
@@ -48,7 +48,7 @@ import java.util.ArrayList;
*/
public class Workspace extends ViewGroup implements DropTarget, DragSource, DragScroller {
private static final int INVALID_SCREEN = -1;
-
+
/**
* The velocity at which a fling gesture will cause us to snap to the next screen
*/
@@ -75,6 +75,11 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
* CellInfo for the cell that is currently being dragged
*/
private CellLayout.CellInfo mDragInfo;
+
+ /**
+ * Target drop area calculated during last acceptDrop call.
+ */
+ private int[] mTargetCell = null;
private float mLastMotionX;
private float mLastMotionY;
@@ -88,8 +93,14 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
private Launcher mLauncher;
private DragController mDragger;
-
+
+ /**
+ * Cache of vacant cells, used during drag events and invalidated as needed.
+ */
+ private CellLayout.CellInfo mVacantCache = null;
+
private int[] mTempCell = new int[2];
+ private int[] mTempEstimate = new int[2];
private boolean mAllowLongPress;
private boolean mLocked;
@@ -363,7 +374,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
CellLayout.CellInfo findAllVacantCells(boolean[] occupied) {
CellLayout group = (CellLayout) getChildAt(mCurrentScreen);
if (group != null) {
- return group.findAllVacantCells(occupied);
+ return group.findAllVacantCells(occupied, null);
}
return null;
}
@@ -890,7 +901,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
}
public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo) {
- final CellLayout cellLayout = (CellLayout) getChildAt(mCurrentScreen);
+ final CellLayout cellLayout = getCurrentDropLayout();
if (source != this) {
onDropExternal(x - xOffset, y - yOffset, dragInfo, cellLayout);
} else {
@@ -902,7 +913,9 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
originalCellLayout.removeView(cell);
cellLayout.addView(cell);
}
- cellLayout.onDropChild(cell, x - xOffset, y - yOffset);
+ mTargetCell = estimateDropCell(source, x - xOffset, y - yOffset,
+ mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout, mTargetCell);
+ cellLayout.onDropChild(cell, mTargetCell);
final ItemInfo info = (ItemInfo)cell.getTag();
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
@@ -914,6 +927,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset,
Object dragInfo) {
+ mVacantCache = null;
}
public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
@@ -922,6 +936,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
public void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset,
Object dragInfo) {
+ mVacantCache = null;
}
private void onDropExternal(int x, int y, Object dragInfo, CellLayout cellLayout) {
@@ -955,7 +970,8 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
cellLayout.addView(view, insertAtFirst ? 0 : -1);
view.setOnLongClickListener(mLongClickListener);
- cellLayout.onDropChild(view, x, y);
+ mTargetCell = estimateDropCell(null, x, y, 1, 1, view, cellLayout, mTargetCell);
+ cellLayout.onDropChild(view, mTargetCell);
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
final LauncherModel model = Launcher.getModel();
@@ -963,18 +979,73 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentScreen, lp.cellX, lp.cellY);
}
+
+ /**
+ * Return the current {@link CellLayout}, correctly picking the destination
+ * screen while a scroll is in progress.
+ */
+ private CellLayout getCurrentDropLayout() {
+ int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen;
+ return (CellLayout) getChildAt(index);
+ }
- public boolean acceptDrop(DragSource source, int x, int y, int xOffset, int yOffset,
- Object dragInfo) {
-
+ /**
+ * {@inheritDoc}
+ */
+ public boolean acceptDrop(DragSource source, int x, int y,
+ int xOffset, int yOffset, Object dragInfo) {
+ // Workspaces accept everything
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Rect estimateDropLocation(DragSource source, int x, int y,
+ int xOffset, int yOffset, Object dragInfo, Rect recycle) {
+ final CellLayout layout = getCurrentDropLayout();
+
final CellLayout.CellInfo cellInfo = mDragInfo;
- int cellHSpan = cellInfo == null ? 1 : cellInfo.spanX;
- int cellVSpan = cellInfo == null ? 1 : cellInfo.spanY;
-
- return ((CellLayout) getChildAt(mCurrentScreen)).acceptChildDrop(x - xOffset, y - yOffset,
- cellHSpan, cellVSpan, cellInfo == null ? null : cellInfo.cell);
+ final int spanX = cellInfo == null ? 1 : cellInfo.spanX;
+ final int spanY = cellInfo == null ? 1 : cellInfo.spanY;
+ final View ignoreView = cellInfo == null ? null : cellInfo.cell;
+
+ final Rect location = recycle != null ? recycle : new Rect();
+
+ // Find drop cell and convert into rectangle
+ int[] dropCell = estimateDropCell(source, x - xOffset, y - yOffset,
+ spanX, spanY, ignoreView, layout, mTempCell);
+
+ if (dropCell == null) {
+ return null;
+ }
+
+ layout.cellToPoint(dropCell[0], dropCell[1], mTempEstimate);
+ location.left = mTempEstimate[0];
+ location.top = mTempEstimate[1];
+
+ layout.cellToPoint(dropCell[0] + spanX, dropCell[1] + spanY, mTempEstimate);
+ location.right = mTempEstimate[0];
+ location.bottom = mTempEstimate[1];
+
+ return location;
}
+ /**
+ * Calculate the nearest cell where the given object would be dropped.
+ */
+ private int[] estimateDropCell(DragSource source, int pixelX, int pixelY,
+ int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) {
+ // Create vacant cell cache if none exists
+ if (mVacantCache == null) {
+ mVacantCache = layout.findAllVacantCells(null, ignoreView);
+ }
+
+ // Find the best target drop location
+ return layout.findNearestVacantArea(pixelX, pixelY,
+ spanX, spanY, mVacantCache, recycle);
+ }
+
void setLauncher(Launcher launcher) {
mLauncher = launcher;
}
@@ -1002,12 +1073,14 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
}
public void scrollLeft() {
+ mVacantCache = null;
if (mNextScreen == INVALID_SCREEN && mCurrentScreen > 0 && mScroller.isFinished()) {
snapToScreen(mCurrentScreen - 1);
}
}
public void scrollRight() {
+ mVacantCache = null;
if (mNextScreen == INVALID_SCREEN && mCurrentScreen < getChildCount() -1 &&
mScroller.isFinished()) {
snapToScreen(mCurrentScreen + 1);