summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Cohen <adamcohen@google.com>2013-12-06 16:10:55 -0800
committerAdam Cohen <adamcohen@google.com>2013-12-09 18:04:57 -0800
commitfa3c58f647e603760c0bd929166c0edee25675a9 (patch)
tree88a1aa2d3619a6028ecd0693d8d3938774f07e46
parent5d518fa77c56a65104f02f40269ad8e43f3431b2 (diff)
downloadandroid_packages_apps_Trebuchet-fa3c58f647e603760c0bd929166c0edee25675a9.tar.gz
android_packages_apps_Trebuchet-fa3c58f647e603760c0bd929166c0edee25675a9.tar.bz2
android_packages_apps_Trebuchet-fa3c58f647e603760c0bd929166c0edee25675a9.zip
Adding reorder hinting, hopefully will help with (issue 10654628)
-> Adds instant feedback that reorder _will_ happen, so hopefully will improve perceived disambiguation Change-Id: Icd99e7beeb87e79627a6b9fc78a9132781410379
-rw-r--r--src/com/android/launcher3/CellLayout.java156
-rw-r--r--src/com/android/launcher3/Workspace.java17
2 files changed, 109 insertions, 64 deletions
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index df16d4194..a05dd90c0 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -133,8 +133,8 @@ public class CellLayout extends ViewGroup {
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;
@@ -149,19 +149,20 @@ public class CellLayout extends ViewGroup {
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();
@@ -225,7 +226,7 @@ public class CellLayout extends ViewGroup {
mForegroundPadding =
res.getDimensionPixelSize(R.dimen.workspace_overscroll_drawable_padding);
- mReorderHintAnimationMagnitude = (REORDER_HINT_MAGNITUDE *
+ mReorderPreviewAnimationMagnitude = (REORDER_PREVIEW_MAGNITUDE *
grid.iconSizePx);
mNormalBackground.setFilterBitmap(true);
@@ -2088,6 +2089,8 @@ public class CellLayout extends ViewGroup {
}
}
+ 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.
@@ -2136,8 +2139,9 @@ public class CellLayout extends ViewGroup {
}
}
- 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
@@ -2159,11 +2163,11 @@ public class CellLayout extends ViewGroup {
// 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 {
@@ -2243,25 +2247,30 @@ public class CellLayout extends ViewGroup {
}
}
- // 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;
@@ -2269,11 +2278,18 @@ public class CellLayout extends ViewGroup {
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];
@@ -2284,20 +2300,22 @@ public class CellLayout extends ViewGroup {
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();
@@ -2307,7 +2325,7 @@ public class CellLayout extends ViewGroup {
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) {
@@ -2322,14 +2340,15 @@ public class CellLayout extends ViewGroup {
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;
@@ -2343,6 +2362,7 @@ public class CellLayout extends ViewGroup {
initDeltaX = 0;
initDeltaY = 0;
initScale = getChildrenScale();
+ repeating = true;
}
});
mShakeAnimators.put(child, this);
@@ -2374,8 +2394,8 @@ public class CellLayout extends ViewGroup {
}
}
- private void completeAndClearReorderHintAnimations() {
- for (ReorderHintAnimation a: mShakeAnimators.values()) {
+ private void completeAndClearReorderPreviewAnimations() {
+ for (ReorderPreviewAnimation a: mShakeAnimators.values()) {
a.completeAnimationImmediately();
}
mShakeAnimators.clear();
@@ -2518,20 +2538,21 @@ public class CellLayout extends ViewGroup {
}
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,
@@ -2540,7 +2561,7 @@ public class CellLayout extends ViewGroup {
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);
@@ -2554,18 +2575,18 @@ public class CellLayout extends ViewGroup {
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);
@@ -2592,7 +2613,8 @@ public class CellLayout extends ViewGroup {
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
@@ -2600,12 +2622,29 @@ public class CellLayout extends ViewGroup {
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);
@@ -2630,11 +2669,11 @@ public class CellLayout extends ViewGroup {
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 {
@@ -2661,6 +2700,7 @@ public class CellLayout extends ViewGroup {
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;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index bf4f561e0..72399e4ec 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -216,7 +216,7 @@ public class Workspace extends SmoothPagedView
// 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;
@@ -2709,7 +2709,7 @@ public class Workspace extends SmoothPagedView
}
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;
@@ -2935,7 +2935,7 @@ public class Workspace extends SmoothPagedView
}
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);
@@ -3517,6 +3517,11 @@ public class Workspace extends SmoothPagedView
&& !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,
@@ -3621,7 +3626,7 @@ public class Workspace extends SmoothPagedView
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);
@@ -3731,7 +3736,7 @@ public class Workspace extends SmoothPagedView
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);
@@ -3823,7 +3828,7 @@ public class Workspace extends SmoothPagedView
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 {