From 19f3792523fe2d55ea791a9286398a6120920690 Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Wed, 21 Mar 2012 11:59:11 -0700 Subject: Implementing spring-back version of reordering -> If you hover above occupied cells, we try to find a new location for the items. The items only remain in the temporary position while the drag ivew is over their original position after which they animate back. -> Items in the temporary positions animate in such a way so indicate that they are in a temporary state, and in such a way so as to hint at where they will return to. Change-Id: I7537c65228c505afbd2f1c22938cfd9d7719839a --- src/com/android/launcher2/CellLayout.java | 332 ++++++++++++++++++++++++++---- src/com/android/launcher2/Workspace.java | 114 +++++----- 2 files changed, 360 insertions(+), 86 deletions(-) (limited to 'src/com/android/launcher2') diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java index 3b77f352e..e8d5c19c6 100644 --- a/src/com/android/launcher2/CellLayout.java +++ b/src/com/android/launcher2/CellLayout.java @@ -18,8 +18,6 @@ package com.android.launcher2; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.animation.PropertyValuesHolder; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; @@ -129,12 +127,15 @@ public class CellLayout extends ViewGroup { private HashMap mReorderAnimators = new HashMap(); + private HashMap + mShakeAnimators = new HashMap(); + + private boolean mItemPlacementDirty = false; // When a drag operation is in progress, holds the nearest cell to the touch point private final int[] mDragCell = new int[2]; private boolean mDragging = false; - private boolean mItemLocationsDirty = false; private TimeInterpolator mEaseOutInterpolator; private ShortcutAndWidgetContainer mShortcutsAndWidgets; @@ -147,12 +148,17 @@ public class CellLayout extends ViewGroup { public static final int MODE_ON_DROP = 1; public static final int MODE_ON_DROP_EXTERNAL = 2; public static final int MODE_ACCEPT_DROP = 3; - private static final boolean DESTRUCTIVE_REORDER = true; + private static final boolean DESTRUCTIVE_REORDER = false; private static final boolean DEBUG_VISUALIZE_OCCUPIED = false; + private static final float REORDER_HINT_MAGNITUDE = 0.27f; + private static final int REORDER_ANIMATION_DURATION = 150; + private float mReorderHintAnimationMagnitude; + private ArrayList mIntersectingViews = new ArrayList(); private Rect mOccupiedRect = new Rect(); private int[] mDirectionVector = new int[2]; + int[] mPreviousReorderDirection = new int[2]; public CellLayout(Context context) { this(context, null); @@ -197,6 +203,9 @@ public class CellLayout extends ViewGroup { mForegroundPadding = res.getDimensionPixelSize(R.dimen.workspace_overscroll_drawable_padding); + mReorderHintAnimationMagnitude = (REORDER_HINT_MAGNITUDE * + res.getDimensionPixelSize(R.dimen.app_icon_size)); + mNormalBackground.setFilterBitmap(true); mActiveGlowBackground.setFilterBitmap(true); @@ -611,8 +620,6 @@ public class CellLayout extends ViewGroup { (bubbleChild.getMeasuredWidth() | bubbleChild.getMeasuredHeight()) == 0) { getShortcutsAndWidgets().measureChild(bubbleChild); } - int measuredWidth = bubbleChild.getMeasuredWidth(); - int measuredHeight = bubbleChild.getMeasuredHeight(); bubbleChild.setScaleX(scale); bubbleChild.setScaleY(scale); @@ -887,13 +894,28 @@ public class CellLayout extends ViewGroup { void regionToCenterPoint(int cellX, int cellY, int spanX, int spanY, int[] result) { final int hStartPadding = getPaddingLeft(); final int vStartPadding = getPaddingTop(); - result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap) + (spanX * mCellWidth + (spanX - 1) * mWidthGap) / 2; result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap) + (spanY * mCellHeight + (spanY - 1) * mHeightGap) / 2; } + /** + * Given a cell coordinate and span fills out a corresponding pixel rect + * + * @param cellX X coordinate of the cell + * @param cellY Y coordinate of the cell + * @param result Rect in which to write the result + */ + void regionToRect(int cellX, int cellY, int spanX, int spanY, Rect result) { + final int hStartPadding = getPaddingLeft(); + final int vStartPadding = getPaddingTop(); + final int left = hStartPadding + cellX * (mCellWidth + mWidthGap); + final int top = vStartPadding + cellY * (mCellHeight + mHeightGap); + result.set(left, top, left + (spanX * mCellWidth + (spanX - 1) * mWidthGap), + top + (spanY * mCellHeight + (spanY - 1) * mHeightGap)); + } + public float getDistanceFromCell(float x, float y, int[] cell) { cellToCenterPoint(cell[0], cell[1], mTmpPoint); float distance = (float) Math.sqrt( Math.pow(x - mTmpPoint[0], 2) + @@ -1055,7 +1077,7 @@ public class CellLayout extends ViewGroup { occupied = mTmpOccupied; } - if (clc.indexOfChild(child) != -1 && !occupied[cellX][cellY]) { + if (clc.indexOfChild(child) != -1) { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); final ItemInfo info = (ItemInfo) child.getTag(); @@ -1101,8 +1123,8 @@ public class CellLayout extends ViewGroup { @Override public void onAnimationUpdate(ValueAnimator animation) { float r = ((Float) animation.getAnimatedValue()).floatValue(); - lp.x = (int) (r * newX + (1 - r) * oldX); - lp.y = (int) (r * newY + (1 - r) * oldY); + lp.x = (int) ((1 - r) * oldX + r * newX); + lp.y = (int) ((1 - r) * oldY + r * newY); child.requestLayout(); } }); @@ -1594,7 +1616,7 @@ public class CellLayout extends ViewGroup { // This method looks in the specified direction to see if there is an additional view // immediately adjecent in that direction private boolean addViewInDirection(ArrayList views, Rect boundingRect, int[] direction, - boolean[][] occupied, ItemConfiguration currentState) { + boolean[][] occupied, View dragView, ItemConfiguration currentState) { boolean found = false; int childCount = mShortcutsAndWidgets.getChildCount(); @@ -1619,7 +1641,7 @@ public class CellLayout extends ViewGroup { for (int i = 0; i < childCount; i++) { View child = mShortcutsAndWidgets.getChildAt(i); - if (views.contains(child)) continue; + if (views.contains(child) || child == dragView) continue; CellAndSpan c = currentState.map.get(child); LayoutParams lp = (LayoutParams) child.getLayoutParams(); @@ -1649,7 +1671,7 @@ public class CellLayout extends ViewGroup { } private boolean addViewsToTempLocation(ArrayList views, Rect rectOccupiedByPotentialDrop, - int[] direction, boolean push, ItemConfiguration currentState) { + int[] direction, boolean push, View dragView, ItemConfiguration currentState) { if (views.size() == 0) return true; boolean success = false; @@ -1668,7 +1690,7 @@ public class CellLayout extends ViewGroup { ArrayList dup = (ArrayList) views.clone(); // We try and expand the group of views in the direction vector passed, based on // whether they are physically adjacent, ie. based on "push mechanics". - while (push && addViewInDirection(dup, boundingRect, direction, mTmpOccupied, + while (push && addViewInDirection(dup, boundingRect, direction, mTmpOccupied, dragView, currentState)) { } @@ -1731,11 +1753,11 @@ public class CellLayout extends ViewGroup { // Mark the desired location of the view currently being dragged. if (ignoreView != null) { CellAndSpan c = solution.map.get(ignoreView); - c.x = cellX; - c.y = cellY; + if (c != null) { + c.x = cellX; + c.y = cellY; + } } - - //int childCount = mChildren.getChildCount(); Rect r0 = new Rect(cellX, cellY, cellX + spanX, cellY + spanY); Rect r1 = new Rect(); for (View child: solution.map.keySet()) { @@ -1752,13 +1774,15 @@ public class CellLayout extends ViewGroup { } // We try to move the intersecting views as a block using the push mechanic - if (addViewsToTempLocation(mIntersectingViews, mOccupiedRect, direction, true, solution)) { + if (addViewsToTempLocation(mIntersectingViews, mOccupiedRect, direction, true, ignoreView, + solution)) { return true; } // Try the opposite direction direction[0] *= -1; direction[1] *= -1; - if (addViewsToTempLocation(mIntersectingViews, mOccupiedRect, direction, true, solution)) { + if (addViewsToTempLocation(mIntersectingViews, mOccupiedRect, direction, true, ignoreView, + solution)) { return true; } // Switch the direction back @@ -1766,7 +1790,8 @@ public class CellLayout extends ViewGroup { direction[1] *= -1; // Next we try moving the views as a block , but without requiring the push mechanic - if (addViewsToTempLocation(mIntersectingViews, mOccupiedRect, direction, false, solution)) { + if (addViewsToTempLocation(mIntersectingViews, mOccupiedRect, direction, false, ignoreView, + solution)) { return true; } @@ -1898,12 +1923,10 @@ public class CellLayout extends ViewGroup { for (int i = 0; i < childCount; i++) { View child = mShortcutsAndWidgets.getChildAt(i); if (child == dragView) continue; - LayoutParams lp = (LayoutParams) child.getLayoutParams(); CellAndSpan c = solution.map.get(child); if (c != null) { - if (lp.cellX != c.x || lp.cellY != c.y) { - animateChildToPosition(child, c.x, c.y, 150, 0, DESTRUCTIVE_REORDER, false); - } + animateChildToPosition(child, c.x, c.y, REORDER_ANIMATION_DURATION, 0, + DESTRUCTIVE_REORDER, false); markCellsForView(c.x, c.y, c.spanX, c.spanY, occupied, true); } } @@ -1913,6 +1936,128 @@ public class CellLayout extends ViewGroup { } } + // This method starts or changes the reorder hint animations + private void beginOrAdjustHintAnimations(ItemConfiguration solution, View dragView, int delay) { + int childCount = mShortcutsAndWidgets.getChildCount(); + int timeForPriorAnimationToComplete = getMaxCompletionTime(); + for (int i = 0; i < childCount; i++) { + View child = mShortcutsAndWidgets.getChildAt(i); + if (child == dragView) continue; + CellAndSpan c = solution.map.get(child); + LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (c != null) { + ReorderHintAnimation rha = new ReorderHintAnimation(child, lp.cellX, lp.cellY, + c.x, c.y, c.spanX, c.spanY); + rha.animate(timeForPriorAnimationToComplete); + } + } + } + + // Class which represents the reorder hint animations. These animations show that an item is + // in a temporary state, and hint at where the item will return to. + class ReorderHintAnimation { + View child; + float deltaX; + float deltaY; + private static final int DURATION = 140; + private int repeatCount; + private boolean cancelOnCycleComplete = false; + ValueAnimator va; + + public ReorderHintAnimation(View child, int cellX0, int cellY0, int cellX1, int cellY1, + int spanX, int spanY) { + regionToCenterPoint(cellX0, cellY0, spanX, spanY, mTmpPoint); + final int x0 = mTmpPoint[0]; + final int y0 = mTmpPoint[1]; + regionToCenterPoint(cellX1, cellY1, spanX, spanY, mTmpPoint); + final int x1 = mTmpPoint[0]; + final int y1 = mTmpPoint[1]; + final int dX = x1 - x0; + final int dY = y1 - y0; + deltaX = 0; + deltaY = 0; + if (dX == dY && dX == 0) { + } else { + if (dY == 0) { + deltaX = mReorderHintAnimationMagnitude; + } else if (dX == 0) { + deltaY = mReorderHintAnimationMagnitude; + } else { + double angle = Math.atan( (float) (dY) / dX); + deltaX = (int) (Math.cos(angle) * mReorderHintAnimationMagnitude); + deltaY = (int) (Math.sin(angle) * mReorderHintAnimationMagnitude); + } + } + this.child = child; + } + + void animate(int delay) { + if (mShakeAnimators.containsKey(child)) { + ReorderHintAnimation oldAnimation = mShakeAnimators.get(child); + oldAnimation.completeAnimation(); + mShakeAnimators.remove(child); + } + if (deltaX == 0 && deltaY == 0) { + return; + } + va = ValueAnimator.ofFloat(0f, 1f); + va.setRepeatMode(ValueAnimator.REVERSE); + va.setRepeatCount(ValueAnimator.INFINITE); + va.setDuration(DURATION); + va.addUpdateListener(new AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float r = ((Float) animation.getAnimatedValue()).floatValue(); + float x = r * deltaX; + float y = r * deltaY; + child.setTranslationX(x); + child.setTranslationY(y); + } + }); + va.addListener(new AnimatorListenerAdapter() { + public void onAnimationRepeat(Animator animation) { + repeatCount++; + // We make sure to end only after a full period + if (cancelOnCycleComplete && repeatCount % 2 == 0) { + va.cancel(); + } + } + }); + va.setStartDelay(Math.max(REORDER_ANIMATION_DURATION, delay)); + mShakeAnimators.put(child, this); + va.start(); + } + + + private void completeAnimation() { + cancelOnCycleComplete = true; + } + + // Returns the time required to complete the current oscillating animation + private int completionTime() { + if (repeatCount % 2 == 0) { + return (int) (va.getDuration() - va.getCurrentPlayTime() + DURATION); + } else { + return (int) (va.getDuration() - va.getCurrentPlayTime()); + } + } + } + + private void completeAndClearReorderHintAnimations() { + for (ReorderHintAnimation a: mShakeAnimators.values()) { + a.completeAnimation(); + } + mShakeAnimators.clear(); + } + + private int getMaxCompletionTime() { + int maxTime = 0; + for (ReorderHintAnimation a: mShakeAnimators.values()) { + maxTime = Math.max(maxTime, a.completionTime()); + } + return maxTime; + } + private void commitTempPlacement() { for (int i = 0; i < mCountX; i++) { for (int j = 0; j < mCountY; j++) { @@ -1958,9 +2103,103 @@ public class CellLayout extends ViewGroup { markCellsAsUnoccupiedForView(child); } + /* This seems like it should be obvious and straight-forward, but when the direction vector + needs to match with the notion of the dragView pushing other views, we have to employ + a slightly more subtle notion of the direction vector. The question is what two points is + the vector between? The center of the dragView and its desired destination? Not quite, as + this doesn't necessarily coincide with the interaction of the dragView and items occupying + those cells. Instead we use some heuristics to often lock the vector to up, down, left + or right, which helps make pushing feel right. + */ + private void getDirectionVectorForDrop(int dragViewCenterX, int dragViewCenterY, int spanX, + int spanY, View dragView, int[] resultDirection) { + int[] targetDestination = new int[2]; + + findNearestArea(dragViewCenterX, dragViewCenterY, spanX, spanY, targetDestination); + Rect dragRect = new Rect(); + regionToRect(targetDestination[0], targetDestination[1], spanX, spanY, dragRect); + dragRect.offset(dragViewCenterX - dragRect.centerX(), dragViewCenterY - dragRect.centerY()); + + Rect dropRegionRect = new Rect(); + getViewsIntersectingRegion(targetDestination[0], targetDestination[1], spanX, spanY, + dragView, dropRegionRect, mIntersectingViews); + + int dropRegionSpanX = dropRegionRect.width(); + int dropRegionSpanY = dropRegionRect.height(); + + regionToRect(dropRegionRect.left, dropRegionRect.top, dropRegionRect.width(), + dropRegionRect.height(), dropRegionRect); + + int deltaX = (dropRegionRect.centerX() - dragViewCenterX) / spanX; + int deltaY = (dropRegionRect.centerY() - dragViewCenterY) / spanY; + + if (dropRegionSpanX == mCountX || spanX == mCountX) { + deltaX = 0; + } + if (dropRegionSpanY == mCountY || spanY == mCountY) { + deltaY = 0; + } + + if (deltaX == 0 && deltaY == 0) { + // No idea what to do, give a random direction. + resultDirection[0] = 1; + resultDirection[1] = 0; + } else { + computeDirectionVector(deltaX, deltaY, resultDirection); + } + } + + // For a given cell and span, fetch the set of views intersecting the region. + private void getViewsIntersectingRegion(int cellX, int cellY, int spanX, int spanY, + View dragView, Rect boundingRect, ArrayList intersectingViews) { + if (boundingRect != null) { + boundingRect.set(cellX, cellY, cellX + spanX, cellY + spanY); + } + intersectingViews.clear(); + Rect r0 = new Rect(cellX, cellY, cellX + spanX, cellY + spanY); + Rect r1 = new Rect(); + final int count = mShortcutsAndWidgets.getChildCount(); + for (int i = 0; i < count; i++) { + View child = mShortcutsAndWidgets.getChildAt(i); + if (child == dragView) continue; + LayoutParams lp = (LayoutParams) child.getLayoutParams(); + r1.set(lp.cellX, lp.cellY, lp.cellX + lp.cellHSpan, lp.cellY + lp.cellVSpan); + if (Rect.intersects(r0, r1)) { + mIntersectingViews.add(child); + if (boundingRect != null) { + boundingRect.union(r1); + } + } + } + } + + boolean isNearestDropLocationOccupied(int pixelX, int pixelY, int spanX, int spanY, + View dragView, int[] result) { + result = findNearestArea(pixelX, pixelY, spanX, spanY, result); + getViewsIntersectingRegion(result[0], result[1], spanX, spanY, dragView, null, + mIntersectingViews); + return !mIntersectingViews.isEmpty(); + } + + void revertTempState() { + if (!isItemPlacementDirty() || DESTRUCTIVE_REORDER) return; + final int count = mShortcutsAndWidgets.getChildCount(); + for (int i = 0; i < count; i++) { + View child = mShortcutsAndWidgets.getChildAt(i); + LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY) { + lp.tmpCellX = lp.cellX; + lp.tmpCellY = lp.cellY; + animateChildToPosition(child, lp.cellX, lp.cellY, REORDER_ANIMATION_DURATION, + 0, false, false); + } + } + completeAndClearReorderHintAnimations(); + setItemPlacementDirty(false); + } + int[] createArea(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY, View dragView, int[] result, int resultSpan[], int mode) { - // First we determine if things have moved enough to cause a different layout result = findNearestArea(pixelX, pixelY, spanX, spanY, result); @@ -1968,10 +2207,24 @@ public class CellLayout extends ViewGroup { resultSpan = new int[2]; } - // We attempt the first algorithm - regionToCenterPoint(result[0], result[1], spanX, spanY, mTmpPoint); - computeDirectionVector((mTmpPoint[0] - pixelX) / spanX, (mTmpPoint[1] - pixelY) / spanY, - mDirectionVector); + // When we are checking drop validity or actually dropping, we don't recompute the + // direction vector, since we want the solution to match the preview, and it's possible + // that the exact position of the item has changed to result in a new reordering outcome. + if ((mode == MODE_ON_DROP || mode == MODE_ACCEPT_DROP) + && mPreviousReorderDirection[0] != -1) { + mDirectionVector[0] = mPreviousReorderDirection[0]; + mDirectionVector[1] = mPreviousReorderDirection[1]; + // We reset this vector after drop + if (mode == MODE_ON_DROP) { + mPreviousReorderDirection[0] = -1; + mPreviousReorderDirection[1] = -1; + } + } else { + getDirectionVectorForDrop(pixelX, pixelY, spanX, spanY, dragView, mDirectionVector); + mPreviousReorderDirection[0] = mDirectionVector[0]; + mPreviousReorderDirection[1] = mDirectionVector[1]; + } + ItemConfiguration swapSolution = simpleSwap(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, mDirectionVector, dragView, true, new ItemConfiguration()); @@ -2007,8 +2260,14 @@ public class CellLayout extends ViewGroup { setItemPlacementDirty(true); animateItemsToSolution(finalSolution, dragView, mode == MODE_ON_DROP); - if (!DESTRUCTIVE_REORDER && mode == MODE_ON_DROP) { + if (!DESTRUCTIVE_REORDER && + (mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL)) { commitTempPlacement(); + completeAndClearReorderHintAnimations(); + setItemPlacementDirty(false); + } else { + beginOrAdjustHintAnimations(finalSolution, dragView, + REORDER_ANIMATION_DURATION); } } } else { @@ -2024,12 +2283,11 @@ public class CellLayout extends ViewGroup { return result; } - public boolean isItemPlacementDirty() { - return mItemLocationsDirty; + void setItemPlacementDirty(boolean dirty) { + mItemPlacementDirty = dirty; } - - public void setItemPlacementDirty(boolean dirty) { - mItemLocationsDirty = dirty; + boolean isItemPlacementDirty() { + return mItemPlacementDirty; } private class ItemConfiguration { @@ -2264,7 +2522,7 @@ public class CellLayout extends ViewGroup { mDragCell[0] = mDragCell[1] = -1; mDragOutlineAnims[mDragOutlineCurrent].animateOut(); mDragOutlineCurrent = (mDragOutlineCurrent + 1) % mDragOutlineAnims.length; - + revertTempState(); setIsDragOverlapping(false); } diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java index 5c8694dcd..d1a111ced 100644 --- a/src/com/android/launcher2/Workspace.java +++ b/src/com/android/launcher2/Workspace.java @@ -198,6 +198,7 @@ public class Workspace extends SmoothPagedView private FolderRingAnimator mDragFolderRingAnimator = null; private View mLastDragOverView = null; private boolean mCreateUserFolderOnDrop = false; + private boolean mWillAddToExistingFolder = false; // Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget) private float mXDown; @@ -375,32 +376,12 @@ public class Workspace extends SmoothPagedView mIsDragOccuring = true; updateChildrenLayersEnabled(); mLauncher.lockScreenOrientationOnLargeUI(); - - // Fade out the workspace slightly to highlight the currently dragging item - int count = getChildCount(); - for (int i = 0; i < count; i++) { - CellLayout cl = (CellLayout) getPageAt(i); - cl.getShortcutsAndWidgets().animate().alpha(mDragFadeOutAlpha) - .setInterpolator(new AccelerateInterpolator(1.5f)) - .setDuration(mDragFadeOutDuration) - .start(); - } } public void onDragEnd() { mIsDragOccuring = false; updateChildrenLayersEnabled(); mLauncher.unlockScreenOrientationOnLargeUI(); - - // Fade the workspace back in after we have completed dragging - int count = getChildCount(); - for (int i = 0; i < count; i++) { - CellLayout cl = (CellLayout) getPageAt(i); - cl.getShortcutsAndWidgets().animate().alpha(1f) - .setInterpolator(new DecelerateInterpolator(1.5f)) - .setDuration(mDragFadeOutDuration) - .start(); - } } /** @@ -429,7 +410,7 @@ public class Workspace extends SmoothPagedView mWallpaperTravelWidth = (int) (mDisplayWidth * wallpaperTravelToScreenWidthRatio(mDisplayWidth, mDisplayHeight)); - mMaxDistanceForFolderCreation = (0.5f * res.getDimension(R.dimen.app_icon_size)); + mMaxDistanceForFolderCreation = (0.5f * res.getDimensionPixelSize(R.dimen.app_icon_size)); mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity); } @@ -1424,9 +1405,8 @@ public class Workspace extends SmoothPagedView } private void updateChildrenLayersEnabled() { - boolean small = isSmall() || mIsSwitchingState; - boolean dragging = mAnimatingViewIntoPlace || mIsDragOccuring; - boolean enableChildrenLayers = small || dragging || isPageMoving(); + boolean small = mState == State.SMALL || mIsSwitchingState; + boolean enableChildrenLayers = small || mAnimatingViewIntoPlace || isPageMoving(); if (enableChildrenLayers != mChildrenLayersEnabled) { mChildrenLayersEnabled = enableChildrenLayers; @@ -2021,12 +2001,10 @@ public class Workspace extends SmoothPagedView int spanX = 1; int spanY = 1; - View ignoreView = null; if (mDragInfo != null) { final CellLayout.CellInfo dragCellInfo = mDragInfo; spanX = dragCellInfo.spanX; spanY = dragCellInfo.spanY; - ignoreView = dragCellInfo.cell; } else { final ItemInfo dragInfo = (ItemInfo) d.dragInfo; spanX = dragInfo.spanX; @@ -2083,9 +2061,15 @@ public class Workspace extends SmoothPagedView boolean willCreateUserFolder(ItemInfo info, CellLayout target, int[] targetCell, float distance, boolean considerTimeout) { if (distance > mMaxDistanceForFolderCreation) return false; - View dropOverView = target.getChildAt(targetCell[0], targetCell[1]); + if (dropOverView != null) { + CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams(); + if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.tmpCellY)) { + return false; + } + } + boolean hasntMoved = false; if (mDragInfo != null) { hasntMoved = dropOverView == mDragInfo.cell; @@ -2106,8 +2090,15 @@ public class Workspace extends SmoothPagedView boolean willAddToExistingUserFolder(Object dragInfo, CellLayout target, int[] targetCell, float distance) { if (distance > mMaxDistanceForFolderCreation) return false; - View dropOverView = target.getChildAt(targetCell[0], targetCell[1]); + + if (dropOverView != null) { + CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams(); + if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.tmpCellY)) { + return false; + } + } + if (dropOverView instanceof FolderIcon) { FolderIcon fi = (FolderIcon) dropOverView; if (fi.acceptDrop(dragInfo)) { @@ -2122,6 +2113,7 @@ public class Workspace extends SmoothPagedView Runnable postAnimationRunnable) { if (distance > mMaxDistanceForFolderCreation) return false; View v = target.getChildAt(targetCell[0], targetCell[1]); + boolean hasntMoved = false; if (mDragInfo != null) { CellLayout cellParent = getParentCellLayoutForView(mDragInfo.cell); @@ -2174,6 +2166,9 @@ public class Workspace extends SmoothPagedView if (distance > mMaxDistanceForFolderCreation) return false; View dropOverView = target.getChildAt(targetCell[0], targetCell[1]); + if (!mWillAddToExistingFolder) return false; + mWillAddToExistingFolder = false; + if (dropOverView instanceof FolderIcon) { FolderIcon fi = (FolderIcon) dropOverView; if (fi.acceptDrop(d.dragInfo)) { @@ -2283,10 +2278,11 @@ public class Workspace extends SmoothPagedView // update the item's position after drop CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams(); - lp.cellX = mTargetCell[0]; - lp.cellY = mTargetCell[1]; + lp.cellX = lp.tmpCellX = mTargetCell[0]; + lp.cellY = lp.tmpCellY = mTargetCell[1]; lp.cellHSpan = item.spanX; lp.cellVSpan = item.spanY; + lp.isLockedToGrid = true; cell.setId(LauncherModel.getCellLayoutChildId(container, mDragInfo.screen, mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY)); @@ -2782,8 +2778,31 @@ public class Workspace extends SmoothPagedView minSpanY = item.minSpanY; } - if (!folder && !mReorderAlarm.alarmPending() && (mLastReorderX != mTargetCell[0] || - mLastReorderY != mTargetCell[1])) { + int[] reorderPosition = new int[2]; + reorderPosition = findNearestArea((int) mDragViewVisualCenter[0], + (int) mDragViewVisualCenter[1], item.spanX, item.spanY, mDragTargetLayout, + reorderPosition); + + if (!mDragTargetLayout.isNearestDropLocationOccupied((int) mDragViewVisualCenter[0], + (int) mDragViewVisualCenter[1], item.spanX, item.spanY, child, mTargetCell)) { + // If the current hover area isn't occupied (permanently) by any items, then we + // reset all the reordering. + mDragTargetLayout.revertTempState(); + mDragMode = DRAG_MODE_NONE; + mLastDragOverView = dragOverView; + if (mReorderAlarm != null) { + mReorderAlarm.cancelAlarm(); + } + mLastReorderX = -1; + mLastReorderY = -1; + mDragTargetLayout.visualizeDropLocation(child, mDragOutline, + (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], + mTargetCell[0], mTargetCell[1], item.spanX, item.spanY, false, + d.dragView.getDragVisualizeOffset(), d.dragView.getDragRegion()); + } else if (!folder && !mReorderAlarm.alarmPending() && + (mLastReorderX != reorderPosition[0] || mLastReorderY != reorderPosition[1])) { + // Otherwise, if we aren't adding to or creating a folder and there's no pending + // reorder, then we schedule a reorder cancelFolderCreation(); ReorderAlarmListener listener = new ReorderAlarmListener(mDragViewVisualCenter, minSpanX, minSpanY, item.spanX, item.spanY, d.dragView, child); @@ -2793,18 +2812,10 @@ public class Workspace extends SmoothPagedView if (mReorderAlarm != null) { mReorderAlarm.cancelAlarm(); } + mDragTargetLayout.revertTempState(); + mLastReorderX = -1; + mLastReorderY = -1; } - // TODO: need to determine what we're going to about visualizing drop locations - /* - boolean resize = resultSpan[0] != info.spanX || resultSpan[1] != info.spanY; - if (!folder) { - mDragTargetLayout.visualizeDropLocation(child, mDragOutline, - (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], - mTargetCell[0], mTargetCell[1], resultSpan[0], resultSpan[1], resize, - d.dragView.getDragVisualizeOffset(), d.dragView.getDragRegion()); - - } - */ } } @@ -2825,6 +2836,7 @@ public class Workspace extends SmoothPagedView if (willAddToFolder && mDragMode == DRAG_MODE_NONE) { FolderIcon fi = ((FolderIcon) dragOverView); mDragMode = DRAG_MODE_ADD_TO_FOLDER; + mWillAddToExistingFolder = true; fi.onDragEnter(info); if (targetLayout != null) { targetLayout.clearDragOutlines(); @@ -2834,12 +2846,13 @@ public class Workspace extends SmoothPagedView if (dragOverView != lastDragOverView || (mCreateUserFolderOnDrop && !userFolderPending) || (!willAddToFolder && mDragMode == DRAG_MODE_ADD_TO_FOLDER)) { cancelFolderCreation(); + mWillAddToExistingFolder = false; if (lastDragOverView != null && lastDragOverView instanceof FolderIcon) { ((FolderIcon) lastDragOverView).onDragExit(info); } } - return willAddToFolder || userFolderPending; + return (willAddToFolder || userFolderPending) && mDragMode != DRAG_MODE_REORDER; } private void cleanupFolderCreation(DragObject d) { @@ -2906,25 +2919,28 @@ public class Workspace extends SmoothPagedView public void onAlarm(Alarm alarm) { int[] resultSpan = new int[2]; + mTargetCell = findNearestArea((int) mDragViewVisualCenter[0], + (int) mDragViewVisualCenter[1], spanX, spanY, mDragTargetLayout, mTargetCell); + mLastReorderX = mTargetCell[0]; + mLastReorderY = mTargetCell[1]; + mTargetCell = mDragTargetLayout.createArea((int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, child, mTargetCell, resultSpan, CellLayout.MODE_DRAG_OVER); - mLastReorderX = mTargetCell[0]; - mLastReorderY = mTargetCell[1]; + if (mTargetCell[0] < 0 || mTargetCell[1] < 0) { + mDragTargetLayout.revertTempState(); + } if (mDragMode == DRAG_MODE_ADD_TO_FOLDER) { } mDragMode = DRAG_MODE_REORDER; - // TODO: need to determine what we're going to about visualizing drop locations - /* boolean resize = resultSpan[0] != spanX || resultSpan[1] != spanY; mDragTargetLayout.visualizeDropLocation(child, mDragOutline, (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], mTargetCell[0], mTargetCell[1], resultSpan[0], resultSpan[1], resize, dragView.getDragVisualizeOffset(), dragView.getDragRegion()); - */ } } -- cgit v1.2.3