From 0ac045fe23cc7b29c4d6712211c2b77bb4eff6e2 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Wed, 3 Nov 2021 13:17:02 -0700 Subject: [PATCH 1/3] Update CellLayout.DEBUG_VISUALIZE_OCCUPIED to include drag over targets Instead of just drawing the occupied cells in red, now we draw the occupied cells based on drag over regions: - Dragging over the red regions will reorder the cell - Dragging over the green regions will create/add to a folder Test: visual with internal flag on Bug: 204406063 Change-Id: I62105c1c1a1101b6cd6f9fd222980d03ba6d8b84 --- src/com/android/launcher3/CellLayout.java | 58 ++++++++++++++++++----- src/com/android/launcher3/Workspace.java | 13 ++--- 2 files changed, 50 insertions(+), 21 deletions(-) diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 02eb1dea6f..bd1d736ab4 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -19,6 +19,7 @@ package com.android.launcher3; import static android.animation.ValueAnimator.areAnimatorsEnabled; import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5; +import static com.android.launcher3.dragndrop.DraggableView.DRAGGABLE_ICON; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -37,7 +38,6 @@ import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; -import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Parcelable; import android.util.ArrayMap; @@ -61,6 +61,7 @@ import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.dragndrop.DraggableView; import com.android.launcher3.folder.PreviewBackground; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.util.CellAndSpan; @@ -442,18 +443,41 @@ public class CellLayout extends ViewGroup { } if (DEBUG_VISUALIZE_OCCUPIED) { - int[] pt = new int[2]; - ColorDrawable cd = new ColorDrawable(Color.RED); - cd.setBounds(0, 0, mCellWidth, mCellHeight); - for (int i = 0; i < mCountX; i++) { - for (int j = 0; j < mCountY; j++) { - if (mOccupied.cells[i][j]) { - cellToPoint(i, j, pt); - canvas.save(); - canvas.translate(pt[0], pt[1]); - cd.draw(canvas); - canvas.restore(); + Rect cellBounds = new Rect(); + // Will contain the bounds of the cell including spacing between cells. + Rect cellBoundsWithSpacing = new Rect(); + int[] cellCenter = new int[2]; + Paint debugPaint = new Paint(); + debugPaint.setStrokeWidth(Utilities.dpToPx(1)); + for (int x = 0; x < mCountX; x++) { + for (int y = 0; y < mCountY; y++) { + if (!mOccupied.cells[x][y]) { + continue; } + View child = getChildAt(x, y); + boolean canCreateFolder = child instanceof DraggableView + && ((DraggableView) child).getViewType() == DRAGGABLE_ICON; + + cellToRect(x, y, 1, 1, cellBounds); + cellBoundsWithSpacing.set(cellBounds); + cellBoundsWithSpacing.inset(-mBorderSpace.x / 2, -mBorderSpace.y / 2); + cellToCenterPoint(x, y, cellCenter); + + canvas.save(); + canvas.clipRect(cellBoundsWithSpacing); + + // Draw reorder drag target. + debugPaint.setColor(Color.RED); + canvas.drawRect(cellBoundsWithSpacing, debugPaint); + + // Draw folder creation drag target. + if (canCreateFolder) { + debugPaint.setColor(Color.GREEN); + canvas.drawCircle(cellCenter[0], cellCenter[1], + getFolderCreationRadius(), debugPaint); + } + + canvas.restore(); } } } @@ -817,7 +841,7 @@ public class CellLayout extends ViewGroup { } /** - * Given a cell coordinate and span return the point that represents the center of the regio + * Given a cell coordinate and span return the point that represents the center of the region * * @param cellX X coordinate of the cell * @param cellY Y coordinate of the cell @@ -835,6 +859,14 @@ public class CellLayout extends ViewGroup { return (float) Math.hypot(x - mTmpPoint[0], y - mTmpPoint[1]); } + /** + * Returns the max distance from the center of a cell that can accept a drop to create a folder. + */ + public float getFolderCreationRadius() { + DeviceProfile grid = mActivity.getDeviceProfile(); + return grid.isTablet ? 0.75f * grid.iconSizePx : 0.55f * grid.iconSizePx; + } + public int getCellWidth() { return mCellWidth; } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index fc717c9387..bac71013ae 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -220,7 +220,6 @@ public class Workspace extends PagedView private FolderIcon mDragOverFolderIcon = null; private boolean mCreateUserFolderOnDrop = false; private boolean mAddToExistingFolderOnDrop = false; - private float mMaxDistanceForFolderCreation; // Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget) private float mXDown; @@ -308,8 +307,6 @@ public class Workspace extends PagedView public void setInsets(Rect insets) { DeviceProfile grid = mLauncher.getDeviceProfile(); - mMaxDistanceForFolderCreation = grid.isTablet - ? 0.75f * grid.iconSizePx : 0.55f * grid.iconSizePx; mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens(); Rect padding = grid.workspacePadding; @@ -1789,7 +1786,7 @@ public class Workspace extends PagedView boolean willCreateUserFolder(ItemInfo info, CellLayout target, int[] targetCell, float distance, boolean considerTimeout) { - if (distance > mMaxDistanceForFolderCreation) return false; + if (distance > target.getFolderCreationRadius()) return false; View dropOverView = target.getChildAt(targetCell[0], targetCell[1]); return willCreateUserFolder(info, dropOverView, considerTimeout); } @@ -1824,7 +1821,7 @@ public class Workspace extends PagedView boolean willAddToExistingUserFolder(ItemInfo dragInfo, CellLayout target, int[] targetCell, float distance) { - if (distance > mMaxDistanceForFolderCreation) return false; + if (distance > target.getFolderCreationRadius()) return false; View dropOverView = target.getChildAt(targetCell[0], targetCell[1]); return willAddToExistingUserFolder(dragInfo, dropOverView); @@ -1848,7 +1845,7 @@ public class Workspace extends PagedView boolean createUserFolderIfNecessary(View newView, int container, CellLayout target, int[] targetCell, float distance, boolean external, DragObject d) { - if (distance > mMaxDistanceForFolderCreation) return false; + if (distance > target.getFolderCreationRadius()) return false; View v = target.getChildAt(targetCell[0], targetCell[1]); boolean hasntMoved = false; @@ -1905,7 +1902,7 @@ public class Workspace extends PagedView boolean addToExistingFolderIfNecessary(View newView, CellLayout target, int[] targetCell, float distance, DragObject d, boolean external) { - if (distance > mMaxDistanceForFolderCreation) return false; + if (distance > target.getFolderCreationRadius()) return false; View dropOverView = target.getChildAt(targetCell[0], targetCell[1]); if (!mAddToExistingFolderOnDrop) return false; @@ -2509,7 +2506,7 @@ public class Workspace extends PagedView } private void manageFolderFeedback(float distance, DragObject dragObject) { - if (distance > mMaxDistanceForFolderCreation) { + if (distance > mDragTargetLayout.getFolderCreationRadius()) { if ((mDragMode == DRAG_MODE_ADD_TO_FOLDER || mDragMode == DRAG_MODE_CREATE_FOLDER)) { setDragMode(DRAG_MODE_NONE); From 3cfa5edc9329a36834066351520f2b50f377ee9e Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Wed, 3 Nov 2021 13:20:43 -0700 Subject: [PATCH 2/3] Fix misaligned folder creation drag over target Previously, the folder creation distance was based on the center of the cell, rather than the *visual* center, i.e. around the icon. Updated to use existing getWorkspaceVisualDragBounds() to handle this. Test: with DEBUG_VISUALIZE_OCCUPIED=true, ensure green circles are centered around the app icons; manually drag and drop to check the drawn regions are correct Bug: 204406063 Change-Id: I691a5cbbfc18c88436b88b7bda42f7920b9a5750 --- src/com/android/launcher3/CellLayout.java | 25 ++++++++++++++++++++--- src/com/android/launcher3/Workspace.java | 18 ++++++++-------- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index bd1d736ab4..c9befe8451 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -461,7 +461,7 @@ public class CellLayout extends ViewGroup { cellToRect(x, y, 1, 1, cellBounds); cellBoundsWithSpacing.set(cellBounds); cellBoundsWithSpacing.inset(-mBorderSpace.x / 2, -mBorderSpace.y / 2); - cellToCenterPoint(x, y, cellCenter); + getWorkspaceCellVisualCenter(x, y, cellCenter); canvas.save(); canvas.clipRect(cellBoundsWithSpacing); @@ -854,11 +854,30 @@ public class CellLayout extends ViewGroup { result[1] = mTempRect.centerY(); } - public float getDistanceFromCell(float x, float y, int[] cell) { - cellToCenterPoint(cell[0], cell[1], mTmpPoint); + /** + * Returns the distance between the given coordinate and the visual center of the given cell. + */ + public float getDistanceFromWorkspaceCellVisualCenter(float x, float y, int[] cell) { + getWorkspaceCellVisualCenter(cell[0], cell[1], mTmpPoint); return (float) Math.hypot(x - mTmpPoint[0], y - mTmpPoint[1]); } + private void getWorkspaceCellVisualCenter(int cellX, int cellY, int[] outPoint) { + View child = getChildAt(cellX, cellY); + if (child instanceof DraggableView) { + DraggableView draggableChild = (DraggableView) child; + if (draggableChild.getViewType() == DRAGGABLE_ICON) { + cellToPoint(cellX, cellY, outPoint); + draggableChild.getWorkspaceVisualDragBounds(mTempRect); + mTempRect.offset(outPoint[0], outPoint[1]); + outPoint[0] = mTempRect.centerX(); + outPoint[1] = mTempRect.centerY(); + return; + } + } + cellToCenterPoint(cellX, cellY, outPoint); + } + /** * Returns the max distance from the center of a cell that can accept a drop to create a folder. */ diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index bac71013ae..2345f51694 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -1751,8 +1751,8 @@ public class Workspace extends PagedView mTargetCell = findNearestArea((int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], minSpanX, minSpanY, dropTargetLayout, mTargetCell); - float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0], - mDragViewVisualCenter[1], mTargetCell); + float distance = dropTargetLayout.getDistanceFromWorkspaceCellVisualCenter( + mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell); if (mCreateUserFolderOnDrop && willCreateUserFolder(d.dragInfo, dropTargetLayout, mTargetCell, distance, true)) { return true; @@ -1966,8 +1966,8 @@ public class Workspace extends PagedView mTargetCell = findNearestArea((int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], spanX, spanY, dropTargetLayout, mTargetCell); - float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0], - mDragViewVisualCenter[1], mTargetCell); + float distance = dropTargetLayout.getDistanceFromWorkspaceCellVisualCenter( + mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell); // If the item being dropped is a shortcut and the nearest drop // cell also contains a shortcut, then create a folder with the two shortcuts. @@ -2395,7 +2395,7 @@ public class Workspace extends PagedView setCurrentDropOverCell(mTargetCell[0], mTargetCell[1]); - float targetCellDistance = mDragTargetLayout.getDistanceFromCell( + float targetCellDistance = mDragTargetLayout.getDistanceFromWorkspaceCellVisualCenter( mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell); manageFolderFeedback(targetCellDistance, d); @@ -2651,8 +2651,8 @@ public class Workspace extends PagedView if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { mTargetCell = findNearestArea(touchXY[0], touchXY[1], spanX, spanY, cellLayout, mTargetCell); - float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0], - mDragViewVisualCenter[1], mTargetCell); + float distance = cellLayout.getDistanceFromWorkspaceCellVisualCenter( + mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell); if (willCreateUserFolder(d.dragInfo, cellLayout, mTargetCell, distance, true) || willAddToExistingUserFolder( d.dragInfo, cellLayout, mTargetCell, distance)) { @@ -2751,8 +2751,8 @@ public class Workspace extends PagedView if (touchXY != null) { mTargetCell = findNearestArea(touchXY[0], touchXY[1], spanX, spanY, cellLayout, mTargetCell); - float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0], - mDragViewVisualCenter[1], mTargetCell); + float distance = cellLayout.getDistanceFromWorkspaceCellVisualCenter( + mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell); if (createUserFolderIfNecessary(view, container, cellLayout, mTargetCell, distance, true, d)) { return; From 1278490ac03f2fd8894d05fcdbb7e20204303e6d Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Wed, 3 Nov 2021 14:02:10 -0700 Subject: [PATCH 3/3] Update reorder and folder creation radii - Before, dragging anywhere in the cell (including space between cells) would trigger a reorder. Now, we compute the radius to the closest cell edge - For creating folders on icons, we split the difference between the reorder circle (above) and the icon size Test: enable DEBUG_VISUALIZE_OCCUPIED and manually drag to ensure works as described above Bug: 204406063 Change-Id: I368714634ed42a930fd16849f2bde1589df1aa63 --- src/com/android/launcher3/CellLayout.java | 52 ++++++++++++++++++++--- src/com/android/launcher3/Workspace.java | 15 ++++--- 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index c9befe8451..adb1613e9d 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -20,6 +20,7 @@ import static android.animation.ValueAnimator.areAnimatorsEnabled; import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5; import static com.android.launcher3.dragndrop.DraggableView.DRAGGABLE_ICON; +import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -446,6 +447,7 @@ public class CellLayout extends ViewGroup { Rect cellBounds = new Rect(); // Will contain the bounds of the cell including spacing between cells. Rect cellBoundsWithSpacing = new Rect(); + int[] targetCell = new int[2]; int[] cellCenter = new int[2]; Paint debugPaint = new Paint(); debugPaint.setStrokeWidth(Utilities.dpToPx(1)); @@ -454,10 +456,10 @@ public class CellLayout extends ViewGroup { if (!mOccupied.cells[x][y]) { continue; } - View child = getChildAt(x, y); - boolean canCreateFolder = child instanceof DraggableView - && ((DraggableView) child).getViewType() == DRAGGABLE_ICON; + targetCell[0] = x; + targetCell[1] = y; + boolean canCreateFolder = canCreateFolder(getChildAt(x, y)); cellToRect(x, y, 1, 1, cellBounds); cellBoundsWithSpacing.set(cellBounds); cellBoundsWithSpacing.inset(-mBorderSpace.x / 2, -mBorderSpace.y / 2); @@ -468,13 +470,14 @@ public class CellLayout extends ViewGroup { // Draw reorder drag target. debugPaint.setColor(Color.RED); - canvas.drawRect(cellBoundsWithSpacing, debugPaint); + canvas.drawCircle(cellCenter[0], cellCenter[1], getReorderRadius(targetCell), + debugPaint); // Draw folder creation drag target. if (canCreateFolder) { debugPaint.setColor(Color.GREEN); canvas.drawCircle(cellCenter[0], cellCenter[1], - getFolderCreationRadius(), debugPaint); + getFolderCreationRadius(targetCell), debugPaint); } canvas.restore(); @@ -505,6 +508,14 @@ public class CellLayout extends ViewGroup { } } + /** + * Returns whether dropping an icon on the given View can create (or add to) a folder. + */ + private boolean canCreateFolder(View child) { + return child instanceof DraggableView + && ((DraggableView) child).getViewType() == DRAGGABLE_ICON; + } + /** * Indicates the progress of the Workspace entering the SpringLoaded state; allows the * CellLayout to update various visuals for this state. @@ -881,9 +892,36 @@ public class CellLayout extends ViewGroup { /** * Returns the max distance from the center of a cell that can accept a drop to create a folder. */ - public float getFolderCreationRadius() { + public float getFolderCreationRadius(int[] targetCell) { DeviceProfile grid = mActivity.getDeviceProfile(); - return grid.isTablet ? 0.75f * grid.iconSizePx : 0.55f * grid.iconSizePx; + float iconVisibleRadius = ICON_VISIBLE_AREA_FACTOR * grid.iconSizePx / 2; + // Halfway between reorder radius and icon. + return (getReorderRadius(targetCell) + iconVisibleRadius) / 2; + } + + /** + * Returns the max distance from the center of a cell that will start to reorder on drag over. + */ + public float getReorderRadius(int[] targetCell) { + int[] centerPoint = mTmpPoint; + getWorkspaceCellVisualCenter(targetCell[0], targetCell[1], centerPoint); + + Rect cellBoundsWithSpacing = mTempRect; + cellToRect(targetCell[0], targetCell[1], 1, 1, cellBoundsWithSpacing); + cellBoundsWithSpacing.inset(-mBorderSpace.x / 2, -mBorderSpace.y / 2); + + if (canCreateFolder(getChildAt(targetCell[0], targetCell[1]))) { + // Take only the circle in the smaller dimension, to ensure we don't start reordering + // too soon before accepting a folder drop. + int minRadius = centerPoint[0] - cellBoundsWithSpacing.left; + minRadius = Math.min(minRadius, centerPoint[1] - cellBoundsWithSpacing.top); + minRadius = Math.min(minRadius, cellBoundsWithSpacing.right - centerPoint[0]); + minRadius = Math.min(minRadius, cellBoundsWithSpacing.bottom - centerPoint[1]); + return minRadius; + } + // Take up the entire cell, including space between this cell and the adjacent ones. + return (float) Math.hypot(cellBoundsWithSpacing.width() / 2f, + cellBoundsWithSpacing.height() / 2f); } public int getCellWidth() { diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 2345f51694..00c8d8e305 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -1786,7 +1786,7 @@ public class Workspace extends PagedView boolean willCreateUserFolder(ItemInfo info, CellLayout target, int[] targetCell, float distance, boolean considerTimeout) { - if (distance > target.getFolderCreationRadius()) return false; + if (distance > target.getFolderCreationRadius(targetCell)) return false; View dropOverView = target.getChildAt(targetCell[0], targetCell[1]); return willCreateUserFolder(info, dropOverView, considerTimeout); } @@ -1821,7 +1821,7 @@ public class Workspace extends PagedView boolean willAddToExistingUserFolder(ItemInfo dragInfo, CellLayout target, int[] targetCell, float distance) { - if (distance > target.getFolderCreationRadius()) return false; + if (distance > target.getFolderCreationRadius(targetCell)) return false; View dropOverView = target.getChildAt(targetCell[0], targetCell[1]); return willAddToExistingUserFolder(dragInfo, dropOverView); @@ -1845,7 +1845,7 @@ public class Workspace extends PagedView boolean createUserFolderIfNecessary(View newView, int container, CellLayout target, int[] targetCell, float distance, boolean external, DragObject d) { - if (distance > target.getFolderCreationRadius()) return false; + if (distance > target.getFolderCreationRadius(targetCell)) return false; View v = target.getChildAt(targetCell[0], targetCell[1]); boolean hasntMoved = false; @@ -1902,7 +1902,7 @@ public class Workspace extends PagedView boolean addToExistingFolderIfNecessary(View newView, CellLayout target, int[] targetCell, float distance, DragObject d, boolean external) { - if (distance > target.getFolderCreationRadius()) return false; + if (distance > target.getFolderCreationRadius(targetCell)) return false; View dropOverView = target.getChildAt(targetCell[0], targetCell[1]); if (!mAddToExistingFolderOnDrop) return false; @@ -2408,8 +2408,9 @@ public class Workspace extends PagedView mDragTargetLayout.visualizeDropLocation(mTargetCell[0], mTargetCell[1], item.spanX, item.spanY, d); } else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER) - && !mReorderAlarm.alarmPending() && (mLastReorderX != reorderX || - mLastReorderY != reorderY)) { + && !mReorderAlarm.alarmPending() + && (mLastReorderX != reorderX || mLastReorderY != reorderY) + && targetCellDistance < mDragTargetLayout.getReorderRadius(mTargetCell)) { int[] resultSpan = new int[2]; mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0], @@ -2506,7 +2507,7 @@ public class Workspace extends PagedView } private void manageFolderFeedback(float distance, DragObject dragObject) { - if (distance > mDragTargetLayout.getFolderCreationRadius()) { + if (distance > mDragTargetLayout.getFolderCreationRadius(mTargetCell)) { if ((mDragMode == DRAG_MODE_ADD_TO_FOLDER || mDragMode == DRAG_MODE_CREATE_FOLDER)) { setDragMode(DRAG_MODE_NONE);