private HashMap<CellLayout.LayoutParams, Animator> mReorderAnimators = new
HashMap<CellLayout.LayoutParams, Animator>();
- private HashMap<View, ReorderHintAnimation>
- mShakeAnimators = new HashMap<View, ReorderHintAnimation>();
+ private HashMap<View, ReorderPreviewAnimation>
+ mShakeAnimators = new HashMap<View, ReorderPreviewAnimation>();
private boolean mItemPlacementDirty = false;
private boolean mIsHotseat = false;
private float mHotseatScale = 1f;
- public static final int MODE_DRAG_OVER = 0;
- 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;
+ public static final int MODE_SHOW_REORDER_HINT = 0;
+ public static final int MODE_DRAG_OVER = 1;
+ public static final int MODE_ON_DROP = 2;
+ public static final int MODE_ON_DROP_EXTERNAL = 3;
+ public static final int MODE_ACCEPT_DROP = 4;
private static final boolean DESTRUCTIVE_REORDER = false;
private static final boolean DEBUG_VISUALIZE_OCCUPIED = false;
static final int LANDSCAPE = 0;
static final int PORTRAIT = 1;
- private static final float REORDER_HINT_MAGNITUDE = 0.12f;
+ private static final float REORDER_PREVIEW_MAGNITUDE = 0.12f;
private static final int REORDER_ANIMATION_DURATION = 150;
- private float mReorderHintAnimationMagnitude;
+ private float mReorderPreviewAnimationMagnitude;
private ArrayList<View> mIntersectingViews = new ArrayList<View>();
private Rect mOccupiedRect = new Rect();
mForegroundPadding =
res.getDimensionPixelSize(R.dimen.workspace_overscroll_drawable_padding);
- mReorderHintAnimationMagnitude = (REORDER_HINT_MAGNITUDE *
+ mReorderPreviewAnimationMagnitude = (REORDER_PREVIEW_MAGNITUDE *
grid.iconSizePx);
mNormalBackground.setFilterBitmap(true);
}
}
+ solution.intersectingViews = new ArrayList<View>(mIntersectingViews);
+
// First we try to find a solution which respects the push mechanic. That is,
// we try to find a solution such that no displaced item travels through another item
// without also displacing that item.
}
}
- ItemConfiguration simpleSwap(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX,
- int spanY, int[] direction, View dragView, boolean decX, ItemConfiguration solution) {
+ ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX, int minSpanY,
+ int spanX, int spanY, int[] direction, View dragView, boolean decX,
+ ItemConfiguration solution) {
// Copy the current state into the solution. This solution will be manipulated as necessary.
copyCurrentStateToSolution(solution, false);
// Copy the current occupied array into the temporary occupied array. This array will be
// We try shrinking the widget down to size in an alternating pattern, shrink 1 in
// x, then 1 in y etc.
if (spanX > minSpanX && (minSpanY == spanY || decX)) {
- return simpleSwap(pixelX, pixelY, minSpanX, minSpanY, spanX - 1, spanY, direction,
- dragView, false, solution);
+ return findReorderSolution(pixelX, pixelY, minSpanX, minSpanY, spanX - 1, spanY,
+ direction, dragView, false, solution);
} else if (spanY > minSpanY) {
- return simpleSwap(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY - 1, direction,
- dragView, true, solution);
+ return findReorderSolution(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY - 1,
+ direction, dragView, true, solution);
}
solution.isSolution = false;
} else {
}
}
- // This method starts or changes the reorder hint animations
- private void beginOrAdjustHintAnimations(ItemConfiguration solution, View dragView, int delay) {
+
+ // This method starts or changes the reorder preview animations
+ private void beginOrAdjustReorderPreviewAnimations(ItemConfiguration solution,
+ View dragView, int delay, int mode) {
int childCount = mShortcutsAndWidgets.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = mShortcutsAndWidgets.getChildAt(i);
if (child == dragView) continue;
CellAndSpan c = solution.map.get(child);
+ boolean skip = mode == ReorderPreviewAnimation.MODE_HINT && solution.intersectingViews
+ != null && !solution.intersectingViews.contains(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);
+ if (c != null && !skip) {
+ ReorderPreviewAnimation rha = new ReorderPreviewAnimation(child, mode, lp.cellX,
+ lp.cellY, c.x, c.y, c.spanX, c.spanY);
rha.animate();
}
}
}
- // Class which represents the reorder hint animations. These animations show that an item is
+ // Class which represents the reorder preview animations. These animations show that an item is
// in a temporary state, and hint at where the item will return to.
- class ReorderHintAnimation {
+ class ReorderPreviewAnimation {
View child;
float finalDeltaX;
float finalDeltaY;
float initDeltaY;
float finalScale;
float initScale;
- private static final int DURATION = 300;
+ int mode;
+ boolean repeating = false;
+ private static final int PREVIEW_DURATION = 300;
+ private static final int HINT_DURATION = Workspace.REORDER_TIMEOUT;
+
+ public static final int MODE_HINT = 0;
+ public static final int MODE_PREVIEW = 1;
+
Animator a;
- public ReorderHintAnimation(View child, int cellX0, int cellY0, int cellX1, int cellY1,
- int spanX, int spanY) {
+ public ReorderPreviewAnimation(View child, int mode, 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];
final int dY = y1 - y0;
finalDeltaX = 0;
finalDeltaY = 0;
+ int dir = mode == MODE_HINT ? -1 : 1;
if (dX == dY && dX == 0) {
} else {
if (dY == 0) {
- finalDeltaX = - Math.signum(dX) * mReorderHintAnimationMagnitude;
+ finalDeltaX = - dir * Math.signum(dX) * mReorderPreviewAnimationMagnitude;
} else if (dX == 0) {
- finalDeltaY = - Math.signum(dY) * mReorderHintAnimationMagnitude;
+ finalDeltaY = - dir * Math.signum(dY) * mReorderPreviewAnimationMagnitude;
} else {
double angle = Math.atan( (float) (dY) / dX);
- finalDeltaX = (int) (- Math.signum(dX) *
- Math.abs(Math.cos(angle) * mReorderHintAnimationMagnitude));
- finalDeltaY = (int) (- Math.signum(dY) *
- Math.abs(Math.sin(angle) * mReorderHintAnimationMagnitude));
+ finalDeltaX = (int) (- dir * Math.signum(dX) *
+ Math.abs(Math.cos(angle) * mReorderPreviewAnimationMagnitude));
+ finalDeltaY = (int) (- dir * Math.signum(dY) *
+ Math.abs(Math.sin(angle) * mReorderPreviewAnimationMagnitude));
}
}
+ this.mode = mode;
initDeltaX = child.getTranslationX();
initDeltaY = child.getTranslationY();
finalScale = getChildrenScale() - 4.0f / child.getWidth();
void animate() {
if (mShakeAnimators.containsKey(child)) {
- ReorderHintAnimation oldAnimation = mShakeAnimators.get(child);
+ ReorderPreviewAnimation oldAnimation = mShakeAnimators.get(child);
oldAnimation.cancel();
mShakeAnimators.remove(child);
if (finalDeltaX == 0 && finalDeltaY == 0) {
a = va;
va.setRepeatMode(ValueAnimator.REVERSE);
va.setRepeatCount(ValueAnimator.INFINITE);
- va.setDuration(DURATION);
+ va.setDuration(mode == MODE_HINT ? HINT_DURATION : PREVIEW_DURATION);
va.setStartDelay((int) (Math.random() * 60));
va.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float r = ((Float) animation.getAnimatedValue()).floatValue();
- float x = r * finalDeltaX + (1 - r) * initDeltaX;
- float y = r * finalDeltaY + (1 - r) * initDeltaY;
+ float r1 = (mode == MODE_HINT && repeating) ? 1.0f : r;
+ float x = r1 * finalDeltaX + (1 - r1) * initDeltaX;
+ float y = r1 * finalDeltaY + (1 - r1) * initDeltaY;
child.setTranslationX(x);
child.setTranslationY(y);
float s = r * finalScale + (1 - r) * initScale;
initDeltaX = 0;
initDeltaY = 0;
initScale = getChildrenScale();
+ repeating = true;
}
});
mShakeAnimators.put(child, this);
}
}
- private void completeAndClearReorderHintAnimations() {
- for (ReorderHintAnimation a: mShakeAnimators.values()) {
+ private void completeAndClearReorderPreviewAnimations() {
+ for (ReorderPreviewAnimation a: mShakeAnimators.values()) {
a.completeAnimationImmediately();
}
mShakeAnimators.clear();
}
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);
+ completeAndClearReorderPreviewAnimations();
+ if (isItemPlacementDirty() && !DESTRUCTIVE_REORDER) {
+ 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);
+ }
}
+ setItemPlacementDirty(false);
}
- completeAndClearReorderHintAnimations();
- setItemPlacementDirty(false);
}
boolean createAreaForResize(int cellX, int cellY, int spanX, int spanY,
regionToCenterPoint(cellX, cellY, spanX, spanY, pixelXY);
// First we determine if things have moved enough to cause a different layout
- ItemConfiguration swapSolution = simpleSwap(pixelXY[0], pixelXY[1], spanX, spanY,
+ ItemConfiguration swapSolution = findReorderSolution(pixelXY[0], pixelXY[1], spanX, spanY,
spanX, spanY, direction, dragView, true, new ItemConfiguration());
setUseTempCoords(true);
if (commit) {
commitTempPlacement();
- completeAndClearReorderHintAnimations();
+ completeAndClearReorderPreviewAnimations();
setItemPlacementDirty(false);
} else {
- beginOrAdjustHintAnimations(swapSolution, dragView,
- REORDER_ANIMATION_DURATION);
+ beginOrAdjustReorderPreviewAnimations(swapSolution, dragView,
+ REORDER_ANIMATION_DURATION, ReorderPreviewAnimation.MODE_PREVIEW);
}
mShortcutsAndWidgets.requestLayout();
}
return swapSolution.isSolution;
}
- int[] createArea(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY,
+ int[] performReorder(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);
mPreviousReorderDirection[1] = mDirectionVector[1];
}
- ItemConfiguration swapSolution = simpleSwap(pixelX, pixelY, minSpanX, minSpanY,
+ // Find a solution involving pushing / displacing any items in the way
+ ItemConfiguration swapSolution = findReorderSolution(pixelX, pixelY, minSpanX, minSpanY,
spanX, spanY, mDirectionVector, dragView, true, new ItemConfiguration());
// We attempt the approach which doesn't shuffle views at all
minSpanY, spanX, spanY, dragView, new ItemConfiguration());
ItemConfiguration finalSolution = null;
+
+ // If the reorder solution requires resizing (shrinking) the item being dropped, we instead
+ // favor a solution in which the item is not resized, but
if (swapSolution.isSolution && swapSolution.area() >= noShuffleSolution.area()) {
finalSolution = swapSolution;
} else if (noShuffleSolution.isSolution) {
finalSolution = noShuffleSolution;
}
+ if (mode == MODE_SHOW_REORDER_HINT) {
+ beginOrAdjustReorderPreviewAnimations(finalSolution, dragView, 0,
+ ReorderPreviewAnimation.MODE_HINT);
+ if (finalSolution != null) {
+ result[0] = finalSolution.dragViewX;
+ result[1] = finalSolution.dragViewY;
+ resultSpan[0] = finalSolution.dragViewSpanX;
+ resultSpan[1] = finalSolution.dragViewSpanY;
+ } else {
+ result[0] = result[1] = resultSpan[0] = resultSpan[1] = -1;
+ }
+ return result;
+ }
+
boolean foundSolution = true;
if (!DESTRUCTIVE_REORDER) {
setUseTempCoords(true);
if (!DESTRUCTIVE_REORDER &&
(mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL)) {
commitTempPlacement();
- completeAndClearReorderHintAnimations();
+ completeAndClearReorderPreviewAnimations();
setItemPlacementDirty(false);
} else {
- beginOrAdjustHintAnimations(finalSolution, dragView,
- REORDER_ANIMATION_DURATION);
+ beginOrAdjustReorderPreviewAnimations(finalSolution, dragView,
+ REORDER_ANIMATION_DURATION, ReorderPreviewAnimation.MODE_PREVIEW);
}
}
} else {
HashMap<View, CellAndSpan> map = new HashMap<View, CellAndSpan>();
private HashMap<View, CellAndSpan> savedMap = new HashMap<View, CellAndSpan>();
ArrayList<View> sortedViews = new ArrayList<View>();
+ ArrayList<View> intersectingViews;
boolean isSolution = false;
int dragViewX, dragViewY, dragViewSpanX, dragViewSpanY;
// Variables relating to the creation of user folders by hovering shortcuts over shortcuts
private static final int FOLDER_CREATION_TIMEOUT = 0;
- private static final int REORDER_TIMEOUT = 250;
+ public static final int REORDER_TIMEOUT = 350;
private final Alarm mFolderCreationAlarm = new Alarm();
private final Alarm mReorderAlarm = new Alarm();
private FolderRingAnimator mDragFolderRingAnimator = null;
}
int[] resultSpan = new int[2];
- mTargetCell = dropTargetLayout.createArea((int) mDragViewVisualCenter[0],
+ mTargetCell = dropTargetLayout.performReorder((int) mDragViewVisualCenter[0],
(int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY,
null, mTargetCell, resultSpan, CellLayout.MODE_ACCEPT_DROP);
boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;
}
int[] resultSpan = new int[2];
- mTargetCell = dropTargetLayout.createArea((int) mDragViewVisualCenter[0],
+ mTargetCell = dropTargetLayout.performReorder((int) mDragViewVisualCenter[0],
(int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, cell,
mTargetCell, resultSpan, CellLayout.MODE_ON_DROP);
&& !mReorderAlarm.alarmPending() && (mLastReorderX != reorderX ||
mLastReorderY != reorderY)) {
+ int[] resultSpan = new int[2];
+ mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0],
+ (int) mDragViewVisualCenter[1], minSpanX, minSpanY, item.spanX, item.spanY,
+ child, mTargetCell, resultSpan, CellLayout.MODE_SHOW_REORDER_HINT);
+
// Otherwise, if we aren't adding to or creating a folder and there's no pending
// reorder, then we schedule a reorder
ReorderAlarmListener listener = new ReorderAlarmListener(mDragViewVisualCenter,
mLastReorderX = mTargetCell[0];
mLastReorderY = mTargetCell[1];
- mTargetCell = mDragTargetLayout.createArea((int) mDragViewVisualCenter[0],
+ mTargetCell = mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0],
(int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY,
child, mTargetCell, resultSpan, CellLayout.MODE_DRAG_OVER);
minSpanY = item.minSpanY;
}
int[] resultSpan = new int[2];
- mTargetCell = cellLayout.createArea((int) mDragViewVisualCenter[0],
+ mTargetCell = cellLayout.performReorder((int) mDragViewVisualCenter[0],
(int) mDragViewVisualCenter[1], minSpanX, minSpanY, info.spanX, info.spanY,
null, mTargetCell, resultSpan, CellLayout.MODE_ON_DROP_EXTERNAL);
if (touchXY != null) {
// when dragging and dropping, just find the closest free spot
- mTargetCell = cellLayout.createArea((int) mDragViewVisualCenter[0],
+ mTargetCell = cellLayout.performReorder((int) mDragViewVisualCenter[0],
(int) mDragViewVisualCenter[1], 1, 1, 1, 1,
null, mTargetCell, null, CellLayout.MODE_ON_DROP_EXTERNAL);
} else {