From 0280c3be4d9f8fc6fdf015b7ecd276eb26f76f2d Mon Sep 17 00:00:00 2001 From: Michael Jurka Date: Fri, 17 Sep 2010 15:00:07 -0700 Subject: Adding support for drag and drop of folders and shortcuts. also: - Long press on empty space on workspace now brings up customization tray - Fixed: while dragging, items appeared to be dropping on folders two cells to the right - Fixed: Disabling drops on folders when the workspace is shrunken - Fixed: account for scaling of dragged items when checking if they overlap with shrunken workspace screens - Making folder icons dimmable to match shortcuts and widgets - When deciding with shrunken workspace screen we're dragging to, we now use the closest screen rather than the one that has been overlapped the most - Refactored drag/add mechanism, removing array of occupied cells from CellInfo - Removed dead code/variables --- src/com/android/launcher2/AllAppsPagedView.java | 4 + .../launcher2/ApplicationInfoDropTarget.java | 4 + src/com/android/launcher2/CellLayout.java | 473 ++++++++++----------- src/com/android/launcher2/CustomizePagedView.java | 82 ++-- src/com/android/launcher2/DeleteZone.java | 4 + src/com/android/launcher2/DragController.java | 8 +- src/com/android/launcher2/DragView.java | 19 +- src/com/android/launcher2/DropTarget.java | 6 + src/com/android/launcher2/FolderIcon.java | 6 +- .../android/launcher2/InstallShortcutReceiver.java | 7 +- src/com/android/launcher2/Launcher.java | 306 ++++++------- src/com/android/launcher2/LauncherModel.java | 4 +- src/com/android/launcher2/LiveFolderInfo.java | 3 + src/com/android/launcher2/PagedView.java | 2 +- src/com/android/launcher2/PendingAddItemInfo.java | 29 ++ src/com/android/launcher2/UserFolder.java | 4 + src/com/android/launcher2/Workspace.java | 303 ++++++------- 17 files changed, 659 insertions(+), 605 deletions(-) create mode 100644 src/com/android/launcher2/PendingAddItemInfo.java (limited to 'src') diff --git a/src/com/android/launcher2/AllAppsPagedView.java b/src/com/android/launcher2/AllAppsPagedView.java index 04e1cd96b..3c394741c 100644 --- a/src/com/android/launcher2/AllAppsPagedView.java +++ b/src/com/android/launcher2/AllAppsPagedView.java @@ -511,4 +511,8 @@ public class AllAppsPagedView extends PagedView @Override public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset, DragView dragView, Object dragInfo) {} + + public boolean isDropEnabled() { + return true; + } } diff --git a/src/com/android/launcher2/ApplicationInfoDropTarget.java b/src/com/android/launcher2/ApplicationInfoDropTarget.java index 0e342a7b4..cb45b3aab 100644 --- a/src/com/android/launcher2/ApplicationInfoDropTarget.java +++ b/src/com/android/launcher2/ApplicationInfoDropTarget.java @@ -110,6 +110,10 @@ public class ApplicationInfoDropTarget extends ImageView implements DropTarget, } } + public boolean isDropEnabled() { + return true; + } + public void onDragEnd() { if (mActive) { mActive = false; diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java index 4d1c29961..19b58529b 100644 --- a/src/com/android/launcher2/CellLayout.java +++ b/src/com/android/launcher2/CellLayout.java @@ -40,8 +40,6 @@ import java.util.Arrays; public class CellLayout extends ViewGroup { static final String TAG = "CellLayout"; - private boolean mPortrait; - private int mCellWidth; private int mCellHeight; @@ -90,9 +88,6 @@ public class CellLayout extends ViewGroup { // When a drag operation is in progress, holds the nearest cell to the touch point private final int[] mDragCell = new int[2]; - private boolean mDirtyTag; - private boolean mLastDownOnOccupiedCell = false; - private final WallpaperManager mWallpaperManager; public CellLayout(Context context) { @@ -135,6 +130,7 @@ public class CellLayout extends ViewGroup { mCountX = LauncherModel.getCellCountX(); mCountY = LauncherModel.getCellCountY(); + mOccupied = new boolean[mCountX][mCountY]; a.recycle(); @@ -214,14 +210,59 @@ public class CellLayout extends ViewGroup { // We might be in the middle or end of shrinking/fading to a dimmed view // Make sure this view's alpha is set the same as all the rest of the views child.setAlpha(getAlpha()); - addView(child, index, lp); + markCellsAsOccupiedForView(child); + return true; } return false; } + @Override + public void removeAllViews() { + clearOccupiedCells(); + } + + @Override + public void removeAllViewsInLayout() { + clearOccupiedCells(); + } + + @Override + public void removeView(View view) { + markCellsAsUnoccupiedForView(view); + super.removeView(view); + } + + @Override + public void removeViewAt(int index) { + markCellsAsUnoccupiedForView(getChildAt(index)); + super.removeViewAt(index); + } + + @Override + public void removeViewInLayout(View view) { + markCellsAsUnoccupiedForView(view); + super.removeViewInLayout(view); + } + + @Override + public void removeViews(int start, int count) { + for (int i = start; i < start + count; i++) { + markCellsAsUnoccupiedForView(getChildAt(i)); + } + super.removeViews(start, count); + } + + @Override + public void removeViewsInLayout(int start, int count) { + for (int i = start; i < start + count; i++) { + markCellsAsUnoccupiedForView(getChildAt(i)); + } + super.removeViewsInLayout(start, count); + } + @Override public void requestChildFocus(View child, View focused) { super.requestChildFocus(child, focused); @@ -258,45 +299,24 @@ public class CellLayout extends ViewGroup { cellInfo.cellY = lp.cellY; cellInfo.spanX = lp.cellHSpan; cellInfo.spanY = lp.cellVSpan; - cellInfo.intersectX = lp.cellX; - cellInfo.intersectY = lp.cellY; cellInfo.valid = true; found = true; - mDirtyTag = false; break; } } } - mLastDownOnOccupiedCell = found; - if (!found) { final int cellXY[] = mTmpCellXY; pointToCellExact(x, y, cellXY); - final boolean portrait = mPortrait; - final int xCount = mCountX; - final int yCount = mCountY; - - final boolean[][] occupied = mOccupied; - findOccupiedCells(xCount, yCount, occupied, null, true); - cellInfo.cell = null; cellInfo.cellX = cellXY[0]; cellInfo.cellY = cellXY[1]; cellInfo.spanX = 1; cellInfo.spanY = 1; - cellInfo.intersectX = cellXY[0]; - cellInfo.intersectY = cellXY[1]; - cellInfo.valid = cellXY[0] >= 0 && cellXY[1] >= 0 && cellXY[0] < xCount && - cellXY[1] < yCount && !occupied[cellXY[0]][cellXY[1]]; - - // Instead of finding the interesting vacant cells here, wait until a - // caller invokes getTag() to retrieve the result. Finding the vacant - // cells is a bit expensive and can generate many new objects, it's - // therefore better to defer it until we know we actually need it. - - mDirtyTag = true; + cellInfo.valid = cellXY[0] >= 0 && cellXY[1] >= 0 && cellXY[0] < mCountX && + cellXY[1] < mCountY && !mOccupied[cellXY[0]][cellXY[1]]; } setTag(cellInfo); } @@ -319,7 +339,6 @@ public class CellLayout extends ViewGroup { cellInfo.spanX = 0; cellInfo.spanY = 0; cellInfo.valid = false; - mDirtyTag = false; setTag(cellInfo); } @@ -328,31 +347,7 @@ public class CellLayout extends ViewGroup { @Override public CellInfo getTag() { - final CellInfo info = (CellInfo) super.getTag(); - if (mDirtyTag && info.valid) { - final int xCount = mCountX; - final int yCount = mCountY; - - final boolean[][] occupied = mOccupied; - findOccupiedCells(xCount, yCount, occupied, null, true); - - info.updateOccupiedCells(occupied, mCountX, mCountY); - - mDirtyTag = false; - } - return info; - } - - /** - * Check if the column 'x' is empty from rows 'top' to 'bottom', inclusive. - */ - private static boolean isColumnEmpty(int x, int top, int bottom, boolean[][] occupied) { - for (int y = top; y <= bottom; y++) { - if (occupied[x][y]) { - return false; - } - } - return true; + return (CellInfo) super.getTag(); } /** @@ -367,42 +362,6 @@ public class CellLayout extends ViewGroup { return true; } - CellInfo updateOccupiedCells(boolean[] occupiedCells, View ignoreView) { - final int xCount = mCountX; - final int yCount = mCountY; - - boolean[][] occupied = mOccupied; - - if (occupiedCells != null) { - for (int y = 0; y < yCount; y++) { - for (int x = 0; x < xCount; x++) { - occupied[x][y] = occupiedCells[y * xCount + x]; - } - } - } else { - findOccupiedCells(xCount, yCount, occupied, ignoreView, true); - } - - CellInfo cellInfo = new CellInfo(); - - cellInfo.cellX = -1; - cellInfo.cellY = -1; - cellInfo.intersectX = -1; - cellInfo.intersectY = -1; - cellInfo.spanY = 0; - cellInfo.spanX = 0; - cellInfo.screen = mCellInfo.screen; - - cellInfo.updateOccupiedCells(occupied, mCountX, mCountY); - - cellInfo.valid = cellInfo.existsEmptyCell(); - - // Assume the caller will perform their own cell searching, otherwise we - // risk causing an unnecessary rebuild after findCellForSpan() - - return cellInfo; - } - /** * Given a point, return the cell that strictly encloses that point * @param x X coordinate of the point @@ -492,19 +451,13 @@ public class CellLayout extends ViewGroup { final int cellWidth = mCellWidth; final int cellHeight = mCellHeight; - if (mOccupied == null) { - mOccupied = new boolean[mCountX][mCountY]; - } - int numWidthGaps = mCountX - 1; int numHeightGaps = mCountY - 1; - int vSpaceLeft = heightSpecSize - mTopPadding - - mBottomPadding - (cellHeight * mCountY); + int vSpaceLeft = heightSpecSize - mTopPadding - mBottomPadding - (cellHeight * mCountY); mHeightGap = vSpaceLeft / numHeightGaps; - int hSpaceLeft = widthSpecSize - mLeftPadding - - mRightPadding - (cellWidth * mCountX); + int hSpaceLeft = widthSpecSize - mLeftPadding - mRightPadding - (cellWidth * mCountX); mWidthGap = hSpaceLeft / numWidthGaps; // center it around the min gaps @@ -519,8 +472,7 @@ public class CellLayout extends ViewGroup { lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap, mLeftPadding, mTopPadding); - int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, - MeasureSpec.EXACTLY); + int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY); int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY); @@ -538,7 +490,7 @@ public class CellLayout extends ViewGroup { } @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { + public void onLayout(boolean changed, int l, int t, int r, int b) { int count = getChildCount(); for (int i = 0; i < count; i++) { @@ -621,15 +573,29 @@ public class CellLayout extends ViewGroup { } } - private boolean isVacant(int originX, int originY, int spanX, int spanY) { + private boolean isVacantIgnoring( + int originX, int originY, int spanX, int spanY, View ignoreView) { + if (ignoreView != null) { + markCellsAsUnoccupiedForView(ignoreView); + } for (int i = 0; i < spanY; i++) { if (!isRowEmpty(originY + i, originX, originX + spanX - 1, mOccupied)) { + if (ignoreView != null) { + markCellsAsOccupiedForView(ignoreView); + } return false; } } + if (ignoreView != null) { + markCellsAsOccupiedForView(ignoreView); + } return true; } + private boolean isVacant(int originX, int originY, int spanX, int spanY) { + return isVacantIgnoring(originX, originY, spanX, spanY, null); + } + public View getChildAt(int x, int y) { final int count = getChildCount(); for (int i = 0; i < count; i++) { @@ -687,7 +653,8 @@ public class CellLayout extends ViewGroup { result[1] = Math.max(0, result[1]); // Snap to top } - void visualizeDropLocation(View view, int originX, int originY, int spanX, int spanY) { + void visualizeDropLocation( + View view, int originX, int originY, int spanX, int spanY, View draggedItem) { final int[] originCell = mDragCell; final int[] cellXY = mTmpCellXY; estimateDropCell(originX, originY, spanX, spanY, cellXY); @@ -709,12 +676,8 @@ public class CellLayout extends ViewGroup { bottomRight[0] += mCellWidth; bottomRight[1] += mCellHeight; - final int countX = mCountX; - final int countY = mCountY; - // TODO: It's not necessary to do this every time, but it's not especially expensive - findOccupiedCells(countX, countY, mOccupied, view, false); - - boolean vacant = isVacant(originCell[0], originCell[1], spanX, spanY); + boolean vacant = + isVacantIgnoring(originCell[0], originCell[1], spanX, spanY, draggedItem); mDragRectDrawable = vacant ? mVacantDrawable : mOccupiedDrawable; // mDragRect will be rendered in onDraw() @@ -736,8 +699,7 @@ public class CellLayout extends ViewGroup { * @return The X, Y cell of a vacant area that can contain this object, * nearest the requested location. */ - int[] findNearestVacantArea(int pixelX, int pixelY, int spanX, int spanY, - CellInfo vacantCells, int[] recycle) { + int[] findNearestVacantArea(int pixelX, int pixelY, int spanX, int spanY, int[] recycle) { // Keep track of best-scoring drop area final int[] bestXY = recycle != null ? recycle : new int[2]; @@ -777,10 +739,130 @@ public class CellLayout extends ViewGroup { } } + boolean existsEmptyCell() { + return findCellForSpan(null, 1, 1); + } + + /** + * Finds the upper-left coordinate of the first rectangle in the grid that can + * hold a cell of the specified dimensions. If intersectX and intersectY are not -1, + * then this method will only return coordinates for rectangles that contain the cell + * (intersectX, intersectY) + * + * @param cellXY The array that will contain the position of a vacant cell if such a cell + * can be found. + * @param spanX The horizontal span of the cell we want to find. + * @param spanY The vertical span of the cell we want to find. + * + * @return True if a vacant cell of the specified dimension was found, false otherwise. + */ + boolean findCellForSpan(int[] cellXY, int spanX, int spanY) { + return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1, null); + } + + /** + * Like above, but ignores any cells occupied by the item "ignoreView" + * + * @param cellXY The array that will contain the position of a vacant cell if such a cell + * can be found. + * @param spanX The horizontal span of the cell we want to find. + * @param spanY The vertical span of the cell we want to find. + * @param ignoreView The home screen item we should treat as not occupying any space + * @return + */ + boolean findCellForSpanIgnoring(int[] cellXY, int spanX, int spanY, View ignoreView) { + return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1, ignoreView); + } + + /** + * Like above, but if intersectX and intersectY are not -1, then this method will try to + * return coordinates for rectangles that contain the cell [intersectX, intersectY] + * + * @param spanX The horizontal span of the cell we want to find. + * @param spanY The vertical span of the cell we want to find. + * @param ignoreView The home screen item we should treat as not occupying any space + * @param intersectX The X coordinate of the cell that we should try to overlap + * @param intersectX The Y coordinate of the cell that we should try to overlap + * + * @return True if a vacant cell of the specified dimension was found, false otherwise. + */ + boolean findCellForSpanThatIntersects(int[] cellXY, int spanX, int spanY, + int intersectX, int intersectY) { + return findCellForSpanThatIntersectsIgnoring( + cellXY, spanX, spanY, intersectX, intersectY, null); + } + + /** + * The superset of the above two methods + */ + boolean findCellForSpanThatIntersectsIgnoring(int[] cellXY, int spanX, int spanY, + int intersectX, int intersectY, View ignoreView) { + if (ignoreView != null) { + markCellsAsUnoccupiedForView(ignoreView); + } + + while (true) { + int startX = 0; + if (intersectX >= 0) { + startX = Math.max(startX, intersectX - (spanX - 1)); + } + int endX = mCountX - (spanX - 1); + if (intersectX >= 0) { + endX = Math.min(endX, intersectX + (spanX - 1) + (spanX == 1 ? 1 : 0)); + } + int startY = 0; + if (intersectY >= 0) { + startY = Math.max(startY, intersectY - (spanY - 1)); + } + int endY = mCountY - (spanY - 1); + if (intersectY >= 0) { + endY = Math.min(endY, intersectY + (spanY - 1) + (spanY == 1 ? 1 : 0)); + } + + for (int x = startX; x < endX; x++) { + inner: + for (int y = startY; y < endY; y++) { + for (int i = 0; i < spanX; i++) { + for (int j = 0; j < spanY; j++) { + if (mOccupied[x + i][y + j]) { + // small optimization: we can skip to below the row we just found + // an occupied cell + y += j; + continue inner; + } + } + } + if (cellXY != null) { + cellXY[0] = x; + cellXY[1] = y; + } + if (ignoreView != null) { + markCellsAsOccupiedForView(ignoreView); + } + return true; + } + } + if (intersectX == -1 && intersectY == -1) { + break; + } else { + // if we failed to find anything, try again but without any requirements of + // intersecting + intersectX = -1; + intersectY = -1; + continue; + } + } + + if (ignoreView != null) { + markCellsAsOccupiedForView(ignoreView); + } + return false; + } + /** - * Called when a drag and drop operation has finished (successfully or not). + * Called when drag has left this CellLayout or has been completed (successfully or not) */ - void onDragComplete() { + void onDragExit() { // Invalidate the drag data mDragCell[0] = -1; mDragCell[1] = -1; @@ -803,14 +885,14 @@ public class CellLayout extends ViewGroup { mDragRect.setEmpty(); child.requestLayout(); } - onDragComplete(); + onDragExit(); } void onDropAborted(View child) { if (child != null) { ((LayoutParams) child.getLayoutParams()).isDragging = false; } - onDragComplete(); + onDragExit(); } /** @@ -889,13 +971,8 @@ public class CellLayout extends ViewGroup { * @return True if a vacant cell was found */ public boolean getVacantCell(int[] vacant, int spanX, int spanY) { - final int xCount = mCountX; - final int yCount = mCountY; - final boolean[][] occupied = mOccupied; - - findOccupiedCells(xCount, yCount, occupied, null, true); - return findVacantCell(vacant, spanX, spanY, xCount, yCount, occupied); + return findVacantCell(vacant, spanX, spanY, mCountX, mCountY, mOccupied); } static boolean findVacantCell(int[] vacant, int spanX, int spanY, @@ -930,8 +1007,6 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) { final int yCount = mCountY; final boolean[][] occupied = mOccupied; - findOccupiedCells(xCount, yCount, occupied, null, true); - final boolean[] flat = new boolean[xCount * yCount]; for (int y = 0; y < yCount; y++) { for (int x = 0; x < xCount; x++) { @@ -942,32 +1017,34 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) { return flat; } - /** - * Update the array of occupied cells. - * @param ignoreView If non-null, the space occupied by this View is treated as vacant - * @param ignoreFolders If true, a cell occupied by a Folder is treated as vacant - */ - private void findOccupiedCells( - int xCount, int yCount, boolean[][] occupied, View ignoreView, boolean ignoreFolders) { - - for (int x = 0; x < xCount; x++) { - for (int y = 0; y < yCount; y++) { - occupied[x][y] = false; + private void clearOccupiedCells() { + for (int x = 0; x < mCountX; x++) { + for (int y = 0; y < mCountY; y++) { + mOccupied[x][y] = false; } } + } - int count = getChildCount(); - for (int i = 0; i < count; i++) { - View child = getChildAt(i); - if ((ignoreFolders && child instanceof Folder) || child.equals(ignoreView)) { - continue; - } - LayoutParams lp = (LayoutParams) child.getLayoutParams(); + public void onMove(View view, int newCellX, int newCellY) { + LayoutParams lp = (LayoutParams) view.getLayoutParams(); + markCellsAsUnoccupiedForView(view); + markCellsForView(newCellX, newCellY, lp.cellHSpan, lp.cellVSpan, true); + } - for (int x = lp.cellX; x < lp.cellX + lp.cellHSpan && x < xCount; x++) { - for (int y = lp.cellY; y < lp.cellY + lp.cellVSpan && y < yCount; y++) { - occupied[x][y] = true; - } + private void markCellsAsOccupiedForView(View view) { + LayoutParams lp = (LayoutParams) view.getLayoutParams(); + markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, true); + } + + private void markCellsAsUnoccupiedForView(View view) { + LayoutParams lp = (LayoutParams) view.getLayoutParams(); + markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false); + } + + private void markCellsForView(int cellX, int cellY, int spanX, int spanY, boolean value) { + for (int x = cellX; x < cellX + spanX && x < mCountX; x++) { + for (int y = cellY; y < cellY + spanY && y < mCountY; y++) { + mOccupied[x][y] = value; } } } @@ -1087,117 +1164,25 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) { } } + // This class stores info for two purposes: + // 1. When dragging items (mDragInfo in Workspace), we store the View, its cellX & cellY, + // its spanX, spanY, and the screen it is on + // 2. When long clicking on an empty cell in a CellLayout, we save information about the + // cellX and cellY coordinates and which page was clicked. We then set this as a tag on + // the CellLayout that was long clicked static final class CellInfo implements ContextMenu.ContextMenuInfo { - private boolean[][] mOccupied; - private int mCountX; - private int mCountY; View cell; int cellX = -1; int cellY = -1; - // intersectX and intersectY constrain the results of findCellForSpan; any empty space - // it results must include this point (unless intersectX and intersectY are -1) - int intersectX = -1; - int intersectY = -1; int spanX; int spanY; int screen; boolean valid; - void updateOccupiedCells(boolean[][] occupied, int xCount, int yCount) { - mOccupied = occupied.clone(); - mCountX = xCount; - mCountY = yCount; - } - - void updateOccupiedCells(boolean[] occupied, int xCount, int yCount) { - if (mOccupied == null || mCountX != xCount || mCountY != yCount) { - mOccupied = new boolean[xCount][yCount]; - } - mCountX = xCount; - mCountY = yCount; - for (int y = 0; y < yCount; y++) { - for (int x = 0; x < xCount; x++) { - mOccupied[x][y] = occupied[y * xCount + x]; - } - } - } - - boolean existsEmptyCell() { - return findCellForSpan(null, 1, 1); - } - /** - * Finds the upper-left coordinate of the first rectangle in the grid that can - * hold a cell of the specified dimensions. If intersectX and intersectY are not -1, - * then this method will only return coordinates for rectangles that contain the cell - * (intersectX, intersectY) - * - * @param cellXY The array that will contain the position of a vacant cell if such a cell - * can be found. - * @param spanX The horizontal span of the cell we want to find. - * @param spanY The vertical span of the cell we want to find. - * - * @return True if a vacant cell of the specified dimension was found, false otherwise. - */ - boolean findCellForSpan(int[] cellXY, int spanX, int spanY) { - // return the span represented by the CellInfo only there is no view there - // (this.cell == null) and there is enough space - - if (this.cell == null && this.spanX >= spanX && this.spanY >= spanY) { - if (cellXY != null) { - cellXY[0] = cellX; - cellXY[1] = cellY; - } - return true; - } - - int startX = 0; - if (intersectX >= 0) { - startX = Math.max(startX, intersectX - (spanX - 1)); - } - int endX = mCountX - (spanX - 1); - if (intersectX >= 0) { - endX = Math.min(endX, intersectX + (spanX - 1)); - } - int startY = 0; - if (intersectY >= 0) { - startY = Math.max(startY, intersectY - (spanY - 1)); - } - int endY = mCountY - (spanY - 1); - if (intersectY >= 0) { - endY = Math.min(endY, intersectY + (spanY - 1)); - } - - for (int x = startX; x < endX; x++) { - inner: - for (int y = startY; y < endY; y++) { - for (int i = 0; i < spanX; i++) { - for (int j = 0; j < spanY; j++) { - if (mOccupied[x + i][y + j]) { - // small optimization: we can skip to below the row we just found - // an occupied cell - y += j; - continue inner; - } - } - } - if (cellXY != null) { - cellXY[0] = x; - cellXY[1] = y; - } - return true; - } - } - return false; - } - @Override public String toString() { return "Cell[view=" + (cell == null ? "null" : cell.getClass()) + ", x=" + cellX + ", y=" + cellY + "]"; } } - - public boolean lastDownOnOccupiedCell() { - return mLastDownOnOccupiedCell; - } } diff --git a/src/com/android/launcher2/CustomizePagedView.java b/src/com/android/launcher2/CustomizePagedView.java index ead258c8c..79b3e6f21 100644 --- a/src/com/android/launcher2/CustomizePagedView.java +++ b/src/com/android/launcher2/CustomizePagedView.java @@ -16,10 +16,7 @@ package com.android.launcher2; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; +import com.android.launcher.R; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; @@ -28,13 +25,12 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; import android.graphics.Canvas; import android.graphics.Rect; +import android.graphics.Bitmap.Config; import android.graphics.Region.Op; import android.graphics.drawable.Drawable; import android.provider.LiveFolders; @@ -44,12 +40,14 @@ import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; -import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; -import com.android.launcher.R; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; public class CustomizePagedView extends PagedView implements View.OnLongClickListener, View.OnClickListener, @@ -354,6 +352,7 @@ public class CustomizePagedView extends PagedView } final View animView = v; + PendingAddItemInfo createItemInfo = new PendingAddItemInfo(); switch (mCustomizationType) { case WidgetCustomization: // Get the icon as the drag representation @@ -365,58 +364,35 @@ public class CustomizePagedView extends PagedView icon.draw(c); AppWidgetProviderInfo appWidgetInfo = (AppWidgetProviderInfo) v.getTag(); - LauncherAppWidgetInfo dragInfo = new LauncherAppWidgetInfo(appWidgetInfo.provider); - dragInfo.minWidth = appWidgetInfo.minWidth; - dragInfo.minHeight = appWidgetInfo.minHeight; - mDragController.startDrag(v, b, this, dragInfo, DragController.DRAG_ACTION_COPY, null); + createItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET; + createItemInfo.componentName = appWidgetInfo.provider; + mDragController.startDrag(v, b, this, createItemInfo, DragController.DRAG_ACTION_COPY, null); // Cleanup the icon b.recycle(); return true; case FolderCustomization: - // animate some feedback to the long press - animateClickFeedback(v, new Runnable() { - @Override - public void run() { - // add the folder - ResolveInfo resolveInfo = (ResolveInfo) animView.getTag(); - Intent createFolderIntent = new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER); - if (resolveInfo.labelRes == R.string.group_folder) { - // Create app shortcuts is a special built-in case of shortcuts - createFolderIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, - getContext().getString(R.string.group_folder)); - } else { - ComponentName name = new ComponentName(resolveInfo.activityInfo.packageName, - resolveInfo.activityInfo.name); - createFolderIntent.setComponent(name); - } - mLauncher.prepareAddItemFromHomeCustomizationDrawer(); - mLauncher.addLiveFolder(createFolderIntent); - } - }); + ResolveInfo resolveInfo = (ResolveInfo) animView.getTag(); + if (resolveInfo.labelRes == R.string.group_folder) { + UserFolderInfo folderInfo = new UserFolderInfo(); + folderInfo.title = getResources().getText(R.string.folder_name); + mDragController.startDrag( + v, this, folderInfo, DragController.DRAG_ACTION_COPY, null); + } else { + createItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER; + createItemInfo.componentName = new ComponentName( + resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name); + mDragController.startDrag( + v, this, createItemInfo, DragController.DRAG_ACTION_COPY, null); + } return true; case ShortcutCustomization: - // animate some feedback to the long press - animateClickFeedback(v, new Runnable() { - @Override - public void run() { - // add the shortcut - ResolveInfo info = (ResolveInfo) animView.getTag(); - Intent createShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT); - if (info.labelRes == R.string.group_applications) { - // Create app shortcuts is a special built-in case of shortcuts - createShortcutIntent.putExtra( - Intent.EXTRA_SHORTCUT_NAME,getContext().getString( - R.string.group_applications)); - } else { - ComponentName name = new ComponentName(info.activityInfo.packageName, - info.activityInfo.name); - createShortcutIntent.setComponent(name); - } - mLauncher.prepareAddItemFromHomeCustomizationDrawer(); - mLauncher.processShortcut(createShortcutIntent); - } - }); + ResolveInfo info = (ResolveInfo) animView.getTag(); + createItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; + createItemInfo.componentName = new ComponentName( + info.activityInfo.packageName, info.activityInfo.name); + mDragController.startDrag( + v, this, createItemInfo, DragController.DRAG_ACTION_COPY, null); return true; case ApplicationCustomization: // Pick up the application for dropping diff --git a/src/com/android/launcher2/DeleteZone.java b/src/com/android/launcher2/DeleteZone.java index aeaf5a37e..1f54b366b 100644 --- a/src/com/android/launcher2/DeleteZone.java +++ b/src/com/android/launcher2/DeleteZone.java @@ -190,6 +190,10 @@ public class DeleteZone extends ImageView implements DropTarget, DragController. } } + public boolean isDropEnabled() { + return true; + } + private void createAnimations() { if (mInAnimation == null) { mInAnimation = new FastAnimationSet(); diff --git a/src/com/android/launcher2/DragController.java b/src/com/android/launcher2/DragController.java index 7fc905bb5..87b3473a1 100644 --- a/src/com/android/launcher2/DragController.java +++ b/src/com/android/launcher2/DragController.java @@ -18,16 +18,17 @@ package com.android.launcher2; import android.content.Context; import android.graphics.Bitmap; +import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.RectF; -import android.os.IBinder; import android.os.Handler; +import android.os.IBinder; import android.os.Vibrator; import android.util.DisplayMetrics; import android.util.Log; -import android.view.View; import android.view.KeyEvent; import android.view.MotionEvent; +import android.view.View; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; @@ -568,6 +569,9 @@ public class DragController { final int count = dropTargets.size(); for (int i=count-1; i>=0; i--) { DropTarget target = dropTargets.get(i); + if (!target.isDropEnabled()) + continue; + target.getHitRect(r); // Convert the hit rect to screen coordinates diff --git a/src/com/android/launcher2/DragView.java b/src/com/android/launcher2/DragView.java index 41e76f0e1..d14f5f756 100644 --- a/src/com/android/launcher2/DragView.java +++ b/src/com/android/launcher2/DragView.java @@ -76,8 +76,7 @@ public class DragView extends View implements TweenCallback { scale.setScale(scaleFactor, scaleFactor); mBitmap = Bitmap.createBitmap(bitmap, left, top, width, height, scale, true); - mDragRegionWidth = width; - mDragRegionHeight = height; + setDragRegion(0, 0, width, height); // The point in our scaled bitmap that the touch events are located mRegistrationX = registrationX + (DRAG_SCALE / 2); @@ -91,6 +90,22 @@ public class DragView extends View implements TweenCallback { mDragRegionHeight = height; } + public int getScaledDragRegionXOffset() { + return -(int)((mScale - 1.0f) * mDragRegionWidth / 2); + } + + public int getScaledDragRegionWidth() { + return (int)(mScale * mDragRegionWidth); + } + + public int getScaledDragRegionYOffset() { + return -(int)((mScale - 1.0f) * mDragRegionHeight / 2); + } + + public int getScaledDragRegionHeight() { + return (int)(mScale * mDragRegionWidth); + } + public int getDragRegionLeft() { return mDragRegionLeft; } diff --git a/src/com/android/launcher2/DropTarget.java b/src/com/android/launcher2/DropTarget.java index d2e3ace90..308dbbee3 100644 --- a/src/com/android/launcher2/DropTarget.java +++ b/src/com/android/launcher2/DropTarget.java @@ -23,6 +23,12 @@ import android.graphics.Rect; * */ public interface DropTarget { + /** + * Used to temporarily disable certain drop targets + * + * @return boolean specifying whether this drop target is currently enabled + */ + boolean isDropEnabled(); /** * Handle an object being dropped on the DropTarget diff --git a/src/com/android/launcher2/FolderIcon.java b/src/com/android/launcher2/FolderIcon.java index e2cef0e69..e692d2058 100644 --- a/src/com/android/launcher2/FolderIcon.java +++ b/src/com/android/launcher2/FolderIcon.java @@ -29,7 +29,7 @@ import com.android.launcher.R; /** * An icon that can appear on in the workspace representing an {@link UserFolder}. */ -public class FolderIcon extends BubbleTextView implements DropTarget { +public class FolderIcon extends DimmableBubbleTextView implements DropTarget { private UserFolderInfo mInfo; private Launcher mLauncher; private Drawable mCloseIcon; @@ -43,6 +43,10 @@ public class FolderIcon extends BubbleTextView implements DropTarget { super(context); } + public boolean isDropEnabled() { + return !((Workspace)getParent().getParent()).isSmall(); + } + static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group, UserFolderInfo folderInfo) { diff --git a/src/com/android/launcher2/InstallShortcutReceiver.java b/src/com/android/launcher2/InstallShortcutReceiver.java index 992bab151..caeb12b4a 100644 --- a/src/com/android/launcher2/InstallShortcutReceiver.java +++ b/src/com/android/launcher2/InstallShortcutReceiver.java @@ -50,11 +50,6 @@ public class InstallShortcutReceiver extends BroadcastReceiver { String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); if (findEmptyCell(context, mCoordinates, screen)) { - CellLayout.CellInfo cell = new CellLayout.CellInfo(); - cell.cellX = mCoordinates[0]; - cell.cellY = mCoordinates[1]; - cell.screen = screen; - Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT); if (intent.getAction() == null) { @@ -66,7 +61,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver { boolean duplicate = data.getBooleanExtra(Launcher.EXTRA_SHORTCUT_DUPLICATE, true); if (duplicate || !LauncherModel.shortcutExists(context, name, intent)) { ((LauncherApplication)context.getApplicationContext()).getModel() - .addShortcut(context, data, cell, true); + .addShortcut(context, data, screen, mCoordinates[0], mCoordinates[1], true); Toast.makeText(context, context.getString(R.string.shortcut_installed, name), Toast.LENGTH_SHORT).show(); } else { diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java index c82f99828..5be78f97f 100644 --- a/src/com/android/launcher2/Launcher.java +++ b/src/com/android/launcher2/Launcher.java @@ -118,8 +118,6 @@ public final class Launcher extends Activity static final boolean DEBUG_WIDGETS = false; static final boolean DEBUG_USER_INTERFACE = false; - private static final int WALLPAPER_SCREENS_SPAN = 2; - private static final int MENU_GROUP_ADD = 1; private static final int MENU_GROUP_WALLPAPER = MENU_GROUP_ADD + 1; @@ -160,16 +158,6 @@ public final class Launcher extends Activity private static final String RUNTIME_STATE_PENDING_ADD_CELL_X = "launcher.add_cellX"; // Type: int private static final String RUNTIME_STATE_PENDING_ADD_CELL_Y = "launcher.add_cellY"; - // Type: int - private static final String RUNTIME_STATE_PENDING_ADD_SPAN_X = "launcher.add_spanX"; - // Type: int - private static final String RUNTIME_STATE_PENDING_ADD_SPAN_Y = "launcher.add_spanY"; - // Type: int - private static final String RUNTIME_STATE_PENDING_ADD_COUNT_X = "launcher.add_countX"; - // Type: int - private static final String RUNTIME_STATE_PENDING_ADD_COUNT_Y = "launcher.add_countY"; - // Type: int[] - private static final String RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS = "launcher.add_occupied_cells"; // Type: boolean private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME = "launcher.rename_folder"; // Type: long @@ -204,10 +192,12 @@ public final class Launcher extends Activity private AppWidgetManager mAppWidgetManager; private LauncherAppWidgetHost mAppWidgetHost; - private CellLayout.CellInfo mAddItemCellInfo; - private int[] mAddItemCoordinates; - private CellLayout.CellInfo mMenuAddInfo; - private final int[] mCellCoordinates = new int[2]; + private int mAddScreen = -1; + private int mAddIntersectCellX = -1; + private int mAddIntersectCellY = -1; + private int[] mAddDropPosition; + private int[] mTmpAddItemCellCoordinates = new int[2]; + private FolderInfo mFolderInfo; private DeleteZone mDeleteZone; @@ -662,29 +652,29 @@ public final class Launcher extends Activity // For example, the user would PICK_SHORTCUT for "Music playlist", and we // launch over to the Music app to actually CREATE_SHORTCUT. - if (resultCode == RESULT_OK && mAddItemCellInfo != null) { + if (resultCode == RESULT_OK && mAddScreen != -1) { switch (requestCode) { case REQUEST_PICK_APPLICATION: - completeAddApplication(this, data, mAddItemCellInfo); + completeAddApplication(this, data, mAddScreen, mAddIntersectCellX, mAddIntersectCellY); break; case REQUEST_PICK_SHORTCUT: processShortcut(data); break; case REQUEST_CREATE_SHORTCUT: - completeAddShortcut(data, mAddItemCellInfo); + completeAddShortcut(data, mAddScreen, mAddIntersectCellX, mAddIntersectCellY); break; case REQUEST_PICK_LIVE_FOLDER: addLiveFolder(data); break; case REQUEST_CREATE_LIVE_FOLDER: - completeAddLiveFolder(data, mAddItemCellInfo); + completeAddLiveFolder(data, mAddScreen, mAddIntersectCellX, mAddIntersectCellY); break; case REQUEST_PICK_APPWIDGET: addAppWidgetFromPick(data); break; case REQUEST_CREATE_APPWIDGET: int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); - completeAddAppWidget(appWidgetId, mAddItemCellInfo); + completeAddAppWidget(appWidgetId, mAddScreen); break; case REQUEST_PICK_WALLPAPER: // We just wanted the activity result here so we can clear mWaitingForResult @@ -821,20 +811,11 @@ public final class Launcher extends Activity } final int addScreen = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SCREEN, -1); + if (addScreen > -1) { - mAddItemCoordinates = null; - mAddItemCellInfo = new CellLayout.CellInfo(); - final CellLayout.CellInfo addItemCellInfo = mAddItemCellInfo; - addItemCellInfo.valid = true; - addItemCellInfo.screen = addScreen; - addItemCellInfo.cellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X); - addItemCellInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y); - addItemCellInfo.spanX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_X); - addItemCellInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y); - addItemCellInfo.updateOccupiedCells( - savedState.getBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS), - savedState.getInt(RUNTIME_STATE_PENDING_ADD_COUNT_X), - savedState.getInt(RUNTIME_STATE_PENDING_ADD_COUNT_Y)); + mAddScreen = addScreen; + mAddIntersectCellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X); + mAddIntersectCellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y); mRestoring = true; } @@ -1021,9 +1002,15 @@ public final class Launcher extends Activity * @param data The intent describing the application. * @param cellInfo The position on screen where to create the shortcut. */ - void completeAddApplication(Context context, Intent data, CellLayout.CellInfo cellInfo) { - cellInfo.screen = mWorkspace.getCurrentPage(); - if (!findSingleSlot(cellInfo)) return; + void completeAddApplication(Context context, Intent data, int screen, + int intersectCellX, int intersectCellY) { + final int[] cellXY = mTmpAddItemCellCoordinates; + final CellLayout layout = (CellLayout) mWorkspace.getChildAt(screen); + + if (!layout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectCellX, intersectCellY)) { + showOutOfSpaceMessage(); + return; + } final ShortcutInfo info = mModel.getShortcutInfo(context.getPackageManager(), data, context); @@ -1032,7 +1019,8 @@ public final class Launcher extends Activity info.setActivity(data.getComponent(), Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); info.container = ItemInfo.NO_ID; - mWorkspace.addApplicationShortcut(info, cellInfo, isWorkspaceLocked()); + mWorkspace.addApplicationShortcut(info, screen, cellXY[0], cellXY[1], + isWorkspaceLocked(), mAddIntersectCellX, mAddIntersectCellY); } else { Log.e(TAG, "Couldn't find ActivityInfo for selected application: " + data); } @@ -1044,16 +1032,22 @@ public final class Launcher extends Activity * @param data The intent describing the shortcut. * @param cellInfo The position on screen where to create the shortcut. */ - private void completeAddShortcut(Intent data, CellLayout.CellInfo cellInfo) { - cellInfo.screen = mWorkspace.getCurrentPage(); - if (!findSingleSlot(cellInfo)) return; + private void completeAddShortcut(Intent data, int screen, + int intersectCellX, int intersectCellY) { + final int[] cellXY = mTmpAddItemCellCoordinates; + final CellLayout layout = (CellLayout) mWorkspace.getChildAt(screen); + + if (!layout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectCellX, intersectCellY)) { + showOutOfSpaceMessage(); + return; + } - final ShortcutInfo info = mModel.addShortcut(this, data, cellInfo, false); + final ShortcutInfo info = mModel.addShortcut( + this, data, screen, cellXY[0], cellXY[1], false); if (!mRestoring) { final View view = createShortcut(info); - mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1, - isWorkspaceLocked()); + mWorkspace.addInScreen(view, screen, cellXY[0], cellXY[1], 1, 1, isWorkspaceLocked()); } } @@ -1064,46 +1058,58 @@ public final class Launcher extends Activity * @param appWidgetId The app widget id * @param cellInfo The position on screen where to create the widget. */ - private void completeAddAppWidget(int appWidgetId, CellLayout.CellInfo cellInfo) { + private void completeAddAppWidget(int appWidgetId, int screen) { AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId); // Calculate the grid spans needed to fit this widget - CellLayout layout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen); - int[] spans = layout.rectToCell(appWidgetInfo.minWidth, appWidgetInfo.minHeight, null); + CellLayout layout = (CellLayout) mWorkspace.getChildAt(screen); + int[] spanXY = layout.rectToCell(appWidgetInfo.minWidth, appWidgetInfo.minHeight, null); // Try finding open space on Launcher screen // We have saved the position to which the widget was dragged-- this really only matters // if we are placing widgets on a "spring-loaded" screen - final int[] xy = mCellCoordinates; + final int[] cellXY = mTmpAddItemCellCoordinates; // For now, we don't save the coordinate where we dropped the icon because we're not // supporting spring-loaded mini-screens; however, leaving the ability to directly place // a widget on the home screen in case we want to add it in the future - final int[] xyTouch = null; - //final int[] xyTouch = mAddItemCoordinates; + final int[] touchXY = null; + //final int[] touchXY = mAddDropPosition; boolean findNearestVacantAreaFailed = false; - if (xyTouch != null) { - CellLayout screen = (CellLayout) mWorkspace.getChildAt(cellInfo.screen); - int[] result = screen.findNearestVacantArea( - mAddItemCoordinates[0], mAddItemCoordinates[1], - spans[0], spans[1], cellInfo, xy); + boolean foundCellSpan = false; + if (touchXY != null) { + // when dragging and dropping, just find the closest free spot + CellLayout screenLayout = (CellLayout) mWorkspace.getChildAt(screen); + int[] result = screenLayout.findNearestVacantArea( + touchXY[0], touchXY[1], spanXY[0], spanXY[1], cellXY); findNearestVacantAreaFailed = (result == null); + foundCellSpan = !findNearestVacantAreaFailed; + } else { + if (mAddIntersectCellX != -1 && mAddIntersectCellY != -1) { + // if we long pressed on an empty cell to bring up a menu, + // make sure we intersect the empty cell + foundCellSpan = layout.findCellForSpanThatIntersects(cellXY, spanXY[0], spanXY[1], + mAddIntersectCellX, mAddIntersectCellY); + } else { + // if we went through the menu -> add, just find any spot + foundCellSpan = layout.findCellForSpan(cellXY, spanXY[0], spanXY[1]); + } } - if (findNearestVacantAreaFailed || - (xyTouch == null && !findSlot(cellInfo, xy, spans[0], spans[1]))) { + if (!foundCellSpan) { if (appWidgetId != -1) mAppWidgetHost.deleteAppWidgetId(appWidgetId); + showOutOfSpaceMessage(); return; } // Build Launcher-specific widget info and save to database LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId); - launcherInfo.spanX = spans[0]; - launcherInfo.spanY = spans[1]; + launcherInfo.spanX = spanXY[0]; + launcherInfo.spanY = spanXY[1]; LauncherModel.addItemToDatabase(this, launcherInfo, LauncherSettings.Favorites.CONTAINER_DESKTOP, - cellInfo.screen, xy[0], xy[1], false); + screen, cellXY[0], cellXY[1], false); if (!mRestoring) { mDesktopItems.add(launcherInfo); @@ -1114,11 +1120,15 @@ public final class Launcher extends Activity launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo); launcherInfo.hostView.setTag(launcherInfo); - mWorkspace.addInScreen(launcherInfo.hostView, cellInfo.screen, xy[0], xy[1], + mWorkspace.addInScreen(launcherInfo.hostView, screen, cellXY[0], cellXY[1], launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked()); } } + void showOutOfSpaceMessage() { + Toast.makeText(this, getString(R.string.out_of_space), Toast.LENGTH_SHORT).show(); + } + public void removeAppWidget(LauncherAppWidgetInfo launcherInfo) { mDesktopItems.remove(launcherInfo); launcherInfo.hostView = null; @@ -1219,19 +1229,10 @@ public final class Launcher extends Activity outState.putBoolean(RUNTIME_STATE_ALL_APPS_FOLDER, true); } - if (mAddItemCellInfo != null && mAddItemCellInfo.valid && mWaitingForResult) { - final CellLayout.CellInfo addItemCellInfo = mAddItemCellInfo; - final CellLayout layout = (CellLayout) mWorkspace.getChildAt(addItemCellInfo.screen); - - outState.putInt(RUNTIME_STATE_PENDING_ADD_SCREEN, addItemCellInfo.screen); - outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_X, addItemCellInfo.cellX); - outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, addItemCellInfo.cellY); - outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_X, addItemCellInfo.spanX); - outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y, addItemCellInfo.spanY); - outState.putInt(RUNTIME_STATE_PENDING_ADD_COUNT_X, layout.getCountX()); - outState.putInt(RUNTIME_STATE_PENDING_ADD_COUNT_Y, layout.getCountY()); - outState.putBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS, - layout.getOccupiedCellsFlattened()); + if (mAddScreen > -1 && mWaitingForResult) { + outState.putInt(RUNTIME_STATE_PENDING_ADD_SCREEN, mAddScreen); + outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_X, mAddIntersectCellX); + outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, mAddIntersectCellY); } if (mFolderInfo != null && mWaitingForResult) { @@ -1353,20 +1354,13 @@ public final class Launcher extends Activity // Disable add if the workspace is full. if (visible) { - mMenuAddInfo = mWorkspace.updateOccupiedCellsForCurrentScreen(null); - menu.setGroupEnabled(MENU_GROUP_ADD, mMenuAddInfo != null && mMenuAddInfo.valid); + CellLayout layout = (CellLayout) mWorkspace.getChildAt(mWorkspace.getCurrentPage()); + menu.setGroupEnabled(MENU_GROUP_ADD, layout.existsEmptyCell()); } return true; } - // we need to initialize mAddItemCellInfo before adding something to the homescreen -- when - // using the settings menu to add an item, something similar happens in showAddDialog - public void prepareAddItemFromHomeCustomizationDrawer() { - mMenuAddInfo = mWorkspace.updateOccupiedCellsForCurrentScreen(null); - mAddItemCellInfo = mMenuAddInfo; - } - @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { @@ -1410,16 +1404,24 @@ public final class Launcher extends Activity } } else { closeAllApps(true); - showAddDialog(mMenuAddInfo); + showAddDialog(-1, -1); } } - void addAppWidgetFromDrop(ComponentName appWidgetProvider, CellLayout.CellInfo cellInfo, - int[] position) { - mAddItemCellInfo = cellInfo; + private void resetAddInfo() { + mAddScreen = -1; + mAddIntersectCellX = -1; + mAddIntersectCellY = -1; + mAddDropPosition = null; + } + + void addAppWidgetFromDrop(ComponentName appWidgetProvider, int screen, int[] position) { + resetAddInfo(); + mAddScreen = screen; + + // only set mAddDropPosition if we dropped on home screen in "spring-loaded" manner + mAddDropPosition = position; - // only set mAddItemCoordinates if we dropped on home screen in "spring-loaded" manner - mAddItemCoordinates = position; int appWidgetId = getAppWidgetHost().allocateAppWidgetId(); AppWidgetManager.getInstance(this).bindAppWidgetId(appWidgetId, appWidgetProvider); addAppWidgetImpl(appWidgetId); @@ -1445,10 +1447,20 @@ public final class Launcher extends Activity startActivityForResultSafely(intent, REQUEST_CREATE_APPWIDGET); } else { // Otherwise just add it - completeAddAppWidget(appWidgetId, mAddItemCellInfo); + completeAddAppWidget(appWidgetId, mAddScreen); } } + void processShortcutFromDrop(ComponentName componentName, int screen, int[] position) { + resetAddInfo(); + mAddScreen = screen; + mAddDropPosition = position; + + Intent createShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT); + createShortcutIntent.setComponent(componentName); + processShortcut(createShortcutIntent); + } + void processShortcut(Intent intent) { // Handle case where user selected "Applications" String applicationName = getResources().getString(R.string.group_applications); @@ -1470,59 +1482,75 @@ public final class Launcher extends Activity startActivityForResult(intent, REQUEST_PICK_WALLPAPER); } - void addLiveFolder(Intent intent) { + void addLiveFolderFromDrop(ComponentName componentName, int screen, int[] position) { + resetAddInfo(); + mAddScreen = screen; + mAddDropPosition = position; + + Intent createFolderIntent = new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER); + createFolderIntent.setComponent(componentName); + + addLiveFolder(createFolderIntent); + } + + void addLiveFolder(Intent intent) { // YYY add screen intersect etc. parameters here // Handle case where user selected "Folder" String folderName = getResources().getString(R.string.group_folder); String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); if (folderName != null && folderName.equals(shortcutName)) { - addFolder(); + addFolder(mAddScreen, mAddIntersectCellX, mAddIntersectCellY); } else { startActivityForResult(intent, REQUEST_CREATE_LIVE_FOLDER); } } - void addFolder() { + void addFolder(int screen, int intersectCellX, int intersectCellY) { UserFolderInfo folderInfo = new UserFolderInfo(); folderInfo.title = getText(R.string.folder_name); - CellLayout.CellInfo cellInfo = mAddItemCellInfo; - cellInfo.screen = mWorkspace.getCurrentPage(); - if (!findSingleSlot(cellInfo)) return; + final CellLayout layout = (CellLayout) mWorkspace.getChildAt(screen); + final int[] cellXY = mTmpAddItemCellCoordinates; + if (!layout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectCellX, intersectCellY)) { + showOutOfSpaceMessage(); + return; + } // Update the model LauncherModel.addItemToDatabase(this, folderInfo, LauncherSettings.Favorites.CONTAINER_DESKTOP, - mWorkspace.getCurrentPage(), cellInfo.cellX, cellInfo.cellY, false); + screen, cellXY[0], cellXY[1], false); sFolders.put(folderInfo.id, folderInfo); // Create the view FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this, (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), folderInfo); - mWorkspace.addInCurrentScreen(newFolder, - cellInfo.cellX, cellInfo.cellY, 1, 1, isWorkspaceLocked()); + mWorkspace.addInScreen(newFolder, screen, cellXY[0], cellXY[1], 1, 1, isWorkspaceLocked()); } void removeFolder(FolderInfo folder) { sFolders.remove(folder.id); } - private void completeAddLiveFolder(Intent data, CellLayout.CellInfo cellInfo) { - cellInfo.screen = mWorkspace.getCurrentPage(); - if (!findSingleSlot(cellInfo)) return; + private void completeAddLiveFolder(Intent data, int screen, int intersectCellX, int intersectCellY) { + final CellLayout layout = (CellLayout) mWorkspace.getChildAt(screen); + final int[] cellXY = mTmpAddItemCellCoordinates; + if (!layout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectCellX, intersectCellY)) { + showOutOfSpaceMessage(); + return; + } - final LiveFolderInfo info = addLiveFolder(this, data, cellInfo, false); + final LiveFolderInfo info = addLiveFolder(this, data, screen, cellXY[0], cellXY[1], false); if (!mRestoring) { final View view = LiveFolderIcon.fromXml(R.layout.live_folder_icon, this, (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info); - mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1, - isWorkspaceLocked()); + mWorkspace.addInScreen(view, screen, cellXY[0], cellXY[1], 1, 1, isWorkspaceLocked()); } } static LiveFolderInfo addLiveFolder(Context context, Intent data, - CellLayout.CellInfo cellInfo, boolean notify) { + int screen, int cellX, int cellY, boolean notify) { Intent baseIntent = data.getParcelableExtra(LiveFolders.EXTRA_LIVE_FOLDER_BASE_INTENT); String name = data.getStringExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME); @@ -1558,34 +1586,12 @@ public final class Launcher extends Activity LiveFolders.DISPLAY_MODE_GRID); LauncherModel.addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP, - cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify); + screen, cellX, cellY, notify); sFolders.put(info.id, info); return info; } - private boolean findSingleSlot(CellLayout.CellInfo cellInfo) { - final int[] xy = new int[2]; - if (findSlot(cellInfo, xy, 1, 1)) { - cellInfo.cellX = xy[0]; - cellInfo.cellY = xy[1]; - return true; - } - return false; - } - - private boolean findSlot(CellLayout.CellInfo cellInfo, int[] xy, int spanX, int spanY) { - if (!cellInfo.findCellForSpan(xy, spanX, spanY)) { - CellLayout targetLayout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen); - cellInfo = targetLayout.updateOccupiedCells(null, null); - if (!cellInfo.findCellForSpan(xy, spanX, spanY)) { - Toast.makeText(this, getString(R.string.out_of_space), Toast.LENGTH_SHORT).show(); - return false; - } - } - return true; - } - private void showNotifications() { final StatusBarManager statusBar = (StatusBarManager) getSystemService(STATUS_BAR_SERVICE); if (statusBar != null) { @@ -1907,28 +1913,38 @@ public final class Launcher extends Activity v = (View) v.getParent(); } - CellLayout.CellInfo cellInfo = (CellLayout.CellInfo) v.getTag(); + resetAddInfo(); + CellLayout.CellInfo longClickCellInfo = (CellLayout.CellInfo) v.getTag(); // This happens when long clicking an item with the dpad/trackball - if (cellInfo == null) { + if (longClickCellInfo == null || !longClickCellInfo.valid) { return true; } + final View itemUnderLongClick = longClickCellInfo.cell; + if (mWorkspace.allowLongPress()) { - if (cellInfo.cell == null) { - if (cellInfo.valid) { - // User long pressed on empty space - mWorkspace.setAllowLongPress(false); - mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, - HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); - showAddDialog(cellInfo); + if (itemUnderLongClick == null) { + // User long pressed on empty space + mWorkspace.setAllowLongPress(false); + mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, + HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + if (LauncherApplication.isScreenXLarge()) { + // Animate the widget chooser up from the bottom of the screen + if (!isCustomizationDrawerVisible()) { + showCustomizationDrawer(true); + } + } else { + showAddDialog(longClickCellInfo.cellX, longClickCellInfo.cellY); } } else { - if (!(cellInfo.cell instanceof Folder)) { + if (!(itemUnderLongClick instanceof Folder)) { // User long pressed on an item mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); - mWorkspace.startDrag(cellInfo); + mAddIntersectCellX = longClickCellInfo.cellX; + mAddIntersectCellY = longClickCellInfo.cellY; + mWorkspace.startDrag(longClickCellInfo); } } } @@ -2106,9 +2122,11 @@ public final class Launcher extends Activity showDialog(DIALOG_RENAME_FOLDER); } - private void showAddDialog(CellLayout.CellInfo cellInfo) { - mAddItemCellInfo = cellInfo; - mAddItemCoordinates = null; + private void showAddDialog(int intersectX, int intersectY) { + resetAddInfo(); + mAddIntersectCellX = intersectX; + mAddIntersectCellY = intersectY; + mAddScreen = mWorkspace.getCurrentPage(); mWaitingForResult = true; showDialog(DIALOG_CREATE_SHORTCUT); } @@ -2634,7 +2652,7 @@ public final class Launcher extends Activity mAllAppsPagedView.endChoiceMode(); mCustomizePagedView.endChoiceMode(); } else { - Toast.makeText(this, getString(R.string.out_of_space), Toast.LENGTH_SHORT).show(); + showOutOfSpaceMessage(); } } } diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java index 4ad31b1ad..7c1fa2114 100644 --- a/src/com/android/launcher2/LauncherModel.java +++ b/src/com/android/launcher2/LauncherModel.java @@ -1478,11 +1478,11 @@ public class LauncherModel extends BroadcastReceiver { } ShortcutInfo addShortcut(Context context, Intent data, - CellLayout.CellInfo cellInfo, boolean notify) { + int screen, int cellX, int cellY, boolean notify) { final ShortcutInfo info = infoFromShortcutIntent(context, data); addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP, - cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify); + screen, cellX, cellY, notify); return info; } diff --git a/src/com/android/launcher2/LiveFolderInfo.java b/src/com/android/launcher2/LiveFolderInfo.java index 74b021758..7db321b59 100644 --- a/src/com/android/launcher2/LiveFolderInfo.java +++ b/src/com/android/launcher2/LiveFolderInfo.java @@ -16,6 +16,7 @@ package com.android.launcher2; +import android.content.ComponentName; import android.content.ContentValues; import android.content.Intent; import android.graphics.Bitmap; @@ -28,6 +29,8 @@ class LiveFolderInfo extends FolderInfo { */ Intent baseIntent; + ComponentName componentName; + /** * The live folder's content uri. */ diff --git a/src/com/android/launcher2/PagedView.java b/src/com/android/launcher2/PagedView.java index 9dbe61d11..f9fcd0291 100644 --- a/src/com/android/launcher2/PagedView.java +++ b/src/com/android/launcher2/PagedView.java @@ -334,7 +334,7 @@ public abstract class PagedView extends ViewGroup { } @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + public void onLayout(boolean changed, int left, int top, int right, int bottom) { if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) { setHorizontalScrollBarEnabled(false); int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage); diff --git a/src/com/android/launcher2/PendingAddItemInfo.java b/src/com/android/launcher2/PendingAddItemInfo.java new file mode 100644 index 000000000..23e23308d --- /dev/null +++ b/src/com/android/launcher2/PendingAddItemInfo.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher2; + +import android.content.ComponentName; + +/** + * We pass this object with a drag from the customization tray + */ +class PendingAddItemInfo extends ItemInfo { + /** + * The component that will be created. + */ + ComponentName componentName; +} \ No newline at end of file diff --git a/src/com/android/launcher2/UserFolder.java b/src/com/android/launcher2/UserFolder.java index c7466b8f2..d6799f75e 100644 --- a/src/com/android/launcher2/UserFolder.java +++ b/src/com/android/launcher2/UserFolder.java @@ -72,6 +72,10 @@ public class UserFolder extends Folder implements DropTarget { } } + public boolean isDropEnabled() { + return true; + } + void bind(FolderInfo info) { super.bind(info); setContentAdapter(new ShortcutsAdapter(mContext, ((UserFolderInfo) info).contents)); diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java index 599fbdabe..8f1630076 100644 --- a/src/com/android/launcher2/Workspace.java +++ b/src/com/android/launcher2/Workspace.java @@ -16,12 +16,12 @@ package com.android.launcher2; -import android.animation.Animator; -import android.animation.ObjectAnimator; import com.android.launcher.R; -import android.animation.PropertyValuesHolder; +import android.animation.Animator; import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; import android.animation.Animator.AnimatorListener; import android.app.WallpaperManager; import android.appwidget.AppWidgetManager; @@ -46,7 +46,6 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; -import android.widget.Toast; import java.util.ArrayList; import java.util.HashSet; @@ -103,8 +102,10 @@ public class Workspace extends SmoothPagedView private int[] mTempCell = new int[2]; private int[] mTempEstimate = new int[2]; + private float[] mTempOriginXY = new float[2]; private float[] mTempDragCoordinates = new float[2]; private float[] mTempDragBottomRightCoordinates = new float[2]; + private Matrix mTempInverseMatrix = new Matrix(); private static final int DEFAULT_CELL_COUNT_X = 4; private static final int DEFAULT_CELL_COUNT_Y = 4; @@ -117,7 +118,6 @@ public class Workspace extends SmoothPagedView private boolean mIsSmall; private AnimatorListener mUnshrinkAnimationListener; - /** * Used to inflate the Workspace from XML. * @@ -273,35 +273,6 @@ public class Workspace extends SmoothPagedView updateWallpaperOffset(mScrollX); } - /** - * Adds the specified child in the current screen. The position and dimension of - * the child are defined by x, y, spanX and spanY. - * - * @param child The child to add in one of the workspace's screens. - * @param x The X position of the child in the screen's grid. - * @param y The Y position of the child in the screen's grid. - * @param spanX The number of cells spanned horizontally by the child. - * @param spanY The number of cells spanned vertically by the child. - */ - void addInCurrentScreen(View child, int x, int y, int spanX, int spanY) { - addInScreen(child, mCurrentPage, x, y, spanX, spanY, false); - } - - /** - * Adds the specified child in the current screen. The position and dimension of - * the child are defined by x, y, spanX and spanY. - * - * @param child The child to add in one of the workspace's screens. - * @param x The X position of the child in the screen's grid. - * @param y The Y position of the child in the screen's grid. - * @param spanX The number of cells spanned horizontally by the child. - * @param spanY The number of cells spanned vertically by the child. - * @param insert When true, the child is inserted at the beginning of the children list. - */ - void addInCurrentScreen(View child, int x, int y, int spanX, int spanY, boolean insert) { - addInScreen(child, mCurrentPage, x, y, spanX, spanY, insert); - } - /** * Adds the specified child in the specified screen. The position and dimension of * the child are defined by x, y, spanX and spanY. @@ -369,14 +340,6 @@ public class Workspace extends SmoothPagedView } } - CellLayout.CellInfo updateOccupiedCellsForCurrentScreen(boolean[] occupied) { - CellLayout group = (CellLayout) getChildAt(mCurrentPage); - if (group != null) { - return group.updateOccupiedCells(occupied, null); - } - return null; - } - public boolean onTouch(View v, MotionEvent event) { // this is an intercepted event being forwarded from a cell layout if (mIsSmall) { @@ -469,7 +432,7 @@ public class Workspace extends SmoothPagedView } @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + public void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); // if shrinkToBottom() is called on initialization, it has to be deferred @@ -776,43 +739,40 @@ public class Workspace extends SmoothPagedView invalidate(); } - void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo) { - addApplicationShortcut(info, cellInfo, false); - } - - void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo, - boolean insertAtFirst) { - final CellLayout layout = (CellLayout) getChildAt(cellInfo.screen); - final int[] result = new int[2]; + void addApplicationShortcut(ShortcutInfo info, int screen, int cellX, int cellY, + boolean insertAtFirst, int intersectX, int intersectY) { + final CellLayout cellLayout = (CellLayout) getChildAt(screen); + View view = mLauncher.createShortcut(R.layout.application, cellLayout, (ShortcutInfo) info); - layout.cellToPoint(cellInfo.cellX, cellInfo.cellY, result); - onDropExternal(result[0], result[1], info, layout, insertAtFirst); + final int[] cellXY = new int[2]; + cellLayout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectX, intersectY); + addInScreen(view, screen, cellXY[0], cellXY[1], 1, 1, insertAtFirst); + LauncherModel.addOrMoveItemInDatabase(mLauncher, info, + LauncherSettings.Favorites.CONTAINER_DESKTOP, screen, + cellXY[0], cellXY[1]); } + public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset, DragView dragView, Object dragInfo) { - CellLayout cellLayout = getCurrentDropLayout(); + CellLayout cellLayout; int originX = x - xOffset; int originY = y - yOffset; if (mIsSmall) { - // find out which target layout is over - final float[] localXY = mTempDragCoordinates; - localXY[0] = originX; - localXY[1] = originY; - final float[] localBottomRightXY = mTempDragBottomRightCoordinates; - // we need to subtract left/top here because DragController already adds - // dragRegionLeft/Top to xOffset and yOffset - localBottomRightXY[0] = originX + dragView.getDragRegionWidth(); - localBottomRightXY[1] = originY + dragView.getDragRegionHeight(); - cellLayout = findMatchingPageForDragOver(localXY, localBottomRightXY); + cellLayout = findMatchingPageForDragOver(dragView, originX, originY); if (cellLayout == null) { // cancel the drag if we're not over a mini-screen at time of drop // TODO: maybe add a nice fade here? return; } - // localXY will be transformed into the local screen's coordinate space; save that info - originX = (int)localXY[0]; - originY = (int)localXY[1]; + // get originX and originY in the local coordinate system of the screen + mTempOriginXY[0] = originX; + mTempOriginXY[1] = originY; + mapPointGlobalToLocal(cellLayout, mTempOriginXY); + originX = (int)mTempOriginXY[0]; + originY = (int)mTempOriginXY[1]; + } else { + cellLayout = getCurrentDropLayout(); } if (source != this) { onDropExternal(originX, originY, dragInfo, cellLayout); @@ -835,8 +795,8 @@ public class Workspace extends SmoothPagedView // update the item's position after drop final ItemInfo info = (ItemInfo) cell.getTag(); - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell - .getLayoutParams(); + CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams(); + cellLayout.onMove(cell, mTargetCell[0], mTargetCell[1]); lp.cellX = mTargetCell[0]; lp.cellY = mTargetCell[1]; @@ -854,6 +814,10 @@ public class Workspace extends SmoothPagedView public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset, DragView dragView, Object dragInfo) { + if (mIsSmall) { + // If we're shrunken, don't let anyone drag on folders/etc that are on the mini-screens + return null; + } // We may need to delegate the drag to a child view. If a 1x1 item // would land in a cell occupied by a DragTarget (e.g. a Folder), // then drag events should be handled by that child. @@ -871,12 +835,12 @@ public class Workspace extends SmoothPagedView dragPointX = x; dragPointY = y; } + dragPointX += mScrollX - currentLayout.getLeft(); + dragPointY += mScrollY - currentLayout.getTop(); // If we are dragging over a cell that contains a DropTarget that will // accept the drop, delegate to that DropTarget. final int[] cellXY = mTempCell; - int localDragPointX = dragPointX - (currentLayout.getLeft() - mScrollX); - int localDragPointY = dragPointY - (currentLayout.getTop() - mScrollY); currentLayout.estimateDropCell(dragPointX, dragPointY, item.spanX, item.spanY, cellXY); View child = currentLayout.getChildAt(cellXY[0], cellXY[1]); if (child instanceof DropTarget) { @@ -888,29 +852,32 @@ public class Workspace extends SmoothPagedView return null; } + + private void mapPointGlobalToLocal(View v, float[] xy) { + xy[0] = xy[0] + mScrollX - v.getLeft(); + xy[1] = xy[1] + mScrollY - v.getTop(); + v.getMatrix().invert(mTempInverseMatrix); + mTempInverseMatrix.mapPoints(xy); + } + // xy = upper left corner of item being dragged // bottomRightXy = lower right corner of item being dragged // This method will see which mini-screen is most overlapped by the item being dragged, and // return it. It will also transform the parameters xy and bottomRightXy into the local // coordinate space of the returned screen - private CellLayout findMatchingPageForDragOver(float[] xy, float[] bottomRightXy) { - float x = xy[0]; - float y = xy[1]; - float right = bottomRightXy[0]; - float bottom = bottomRightXy[1]; - - float bestX = 0; - float bestY = 0; - float bestRight = 0; - float bestBottom = 0; - - Matrix inverseMatrix = new Matrix(); + private CellLayout findMatchingPageForDragOver(DragView dragView, int originX, int originY) { + float x = originX + dragView.getScaledDragRegionXOffset(); + float y = originY + dragView.getScaledDragRegionYOffset(); + float right = x + dragView.getScaledDragRegionWidth(); + float bottom = y + dragView.getScaledDragRegionHeight(); // We loop through all the screens (ie CellLayouts) and see which one overlaps the most // with the item being dragged. final int screenCount = getChildCount(); CellLayout bestMatchingScreen = null; - float bestOverlapSoFar = 0; + float smallestDistSoFar = Float.MAX_VALUE; + final float[] xy = mTempDragCoordinates; + final float[] bottomRightXy = mTempDragBottomRightCoordinates; for (int i = 0; i < screenCount; i++) { CellLayout cl = (CellLayout)getChildAt(i); // Transform the coordinates of the item being dragged to the CellLayout's coordinates @@ -918,57 +885,76 @@ public class Workspace extends SmoothPagedView float top = cl.getTop(); xy[0] = x + mScrollX - left; xy[1] = y + mScrollY - top; - cl.getMatrix().invert(inverseMatrix); bottomRightXy[0] = right + mScrollX - left; bottomRightXy[1] = bottom + mScrollY - top; - inverseMatrix.mapPoints(xy); - inverseMatrix.mapPoints(bottomRightXy); + cl.getMatrix().invert(mTempInverseMatrix); + mTempInverseMatrix.mapPoints(xy); + mTempInverseMatrix.mapPoints(bottomRightXy); float dragRegionX = xy[0]; float dragRegionY = xy[1]; float dragRegionRight = bottomRightXy[0]; float dragRegionBottom = bottomRightXy[1]; + float dragRegionCenterX = (dragRegionX + dragRegionRight) / 2.0f; + float dragRegionCenterY = (dragRegionY + dragRegionBottom) / 2.0f; // Find the overlapping region float overlapLeft = Math.max(0f, dragRegionX); float overlapTop = Math.max(0f, dragRegionY); float overlapBottom = Math.min(cl.getHeight(), dragRegionBottom); float overlapRight = Math.min(cl.getWidth(), dragRegionRight); - if (overlapRight >= 0 && overlapLeft <= cl.getWidth() && - overlapTop >= 0 && overlapBottom <= cl.getHeight()) { - // Calculate the size of the overlapping region + (overlapTop >= 0 && overlapBottom <= cl.getHeight())) { + // Calculate the distance between the two centers + float distX = dragRegionCenterX - cl.getWidth()/2; + float distY = dragRegionCenterY - cl.getHeight()/2; + float dist = distX * distX + distY * distY; + float overlap = (overlapRight - overlapLeft) * (overlapBottom - overlapTop); - if (overlap > bestOverlapSoFar) { - bestOverlapSoFar = overlap; + + // Calculate the closest overlapping region + if (overlap > 0 && dist < smallestDistSoFar) { + smallestDistSoFar = dist; bestMatchingScreen = cl; - bestX = xy[0]; - bestY = xy[1]; - bestRight = bottomRightXy[0]; - bestBottom = bottomRightXy[1]; } } } - if (bestMatchingScreen != null && bestMatchingScreen != mDragTargetLayout) { + + if (bestMatchingScreen != mDragTargetLayout) { if (mDragTargetLayout != null) { - mDragTargetLayout.onDragComplete(); + mDragTargetLayout.onDragExit(); } mDragTargetLayout = bestMatchingScreen; } - xy[0] = bestX; - xy[1] = bestY; - bottomRightXy[0] = bestRight; - bottomRightXy[1] = bestBottom; return bestMatchingScreen; } public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset, DragView dragView, Object dragInfo) { + CellLayout currentLayout; + int originX = x - xOffset; + int originY = y - yOffset; + if (mIsSmall) { + currentLayout = findMatchingPageForDragOver(dragView, originX, originY); + + if (currentLayout == null) { + return; + } + + currentLayout.setHover(true); + // get originX and originY in the local coordinate system of the screen + mTempOriginXY[0] = originX; + mTempOriginXY[1] = originY; + mapPointGlobalToLocal(currentLayout, mTempOriginXY); + originX = (int)mTempOriginXY[0]; + originY = (int)mTempOriginXY[1]; + } else { + currentLayout = getCurrentDropLayout(); + } final ItemInfo item = (ItemInfo)dragInfo; - CellLayout currentLayout = getCurrentDropLayout(); if (dragInfo instanceof LauncherAppWidgetInfo) { LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo)dragInfo; @@ -980,25 +966,6 @@ public class Workspace extends SmoothPagedView item.spanY = spans[1]; } } - int originX = x - xOffset; - int originY = y - yOffset; - if (mIsSmall) { - // find out which mini screen the dragged item is over - final float[] localXY = mTempDragCoordinates; - localXY[0] = originX; - localXY[1] = originY; - final float[] localBottomRightXY = mTempDragBottomRightCoordinates; - - localBottomRightXY[0] = originX + dragView.getDragRegionWidth(); - localBottomRightXY[1] = originY + dragView.getDragRegionHeight(); - currentLayout = findMatchingPageForDragOver(localXY, localBottomRightXY); - if (currentLayout != null) { - currentLayout.setHover(true); - } - - originX = (int)localXY[0]; - originY = (int)localXY[1]; - } if (source != this) { // This is a hack to fix the point used to determine which cell an icon from the all @@ -1015,7 +982,7 @@ public class Workspace extends SmoothPagedView } if (currentLayout != mDragTargetLayout) { if (mDragTargetLayout != null) { - mDragTargetLayout.onDragComplete(); + mDragTargetLayout.onDragExit(); } mDragTargetLayout = currentLayout; } @@ -1028,14 +995,14 @@ public class Workspace extends SmoothPagedView int localOriginX = originX - (mDragTargetLayout.getLeft() - mScrollX); int localOriginY = originY - (mDragTargetLayout.getTop() - mScrollY); mDragTargetLayout.visualizeDropLocation( - child, localOriginX, localOriginY, item.spanX, item.spanY); + child, localOriginX, localOriginY, item.spanX, item.spanY, child); } } public void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset, DragView dragView, Object dragInfo) { if (mDragTargetLayout != null) { - mDragTargetLayout.onDragComplete(); + mDragTargetLayout.onDragExit(); mDragTargetLayout = null; } } @@ -1055,17 +1022,43 @@ public class Workspace extends SmoothPagedView CellLayout cl = (CellLayout) layout; ItemInfo info = (ItemInfo) dragInfo; - final CellLayout.CellInfo cellInfo = cl.updateOccupiedCells(null, null); - if (cellInfo.findCellForSpan(mTempEstimate, info.spanX, info.spanY)) { + if (cl.findCellForSpan(mTempEstimate, info.spanX, info.spanY)) { onDropExternal(0, 0, dragInfo, cl, false); return true; } + mLauncher.showOutOfSpaceMessage(); return false; } + // Drag from somewhere else private void onDropExternal(int x, int y, Object dragInfo, CellLayout cellLayout, boolean insertAtFirst) { - // Drag from somewhere else + int screen = indexOfChild(cellLayout); + if (dragInfo instanceof PendingAddItemInfo) { + PendingAddItemInfo info = (PendingAddItemInfo) dragInfo; + // When dragging and dropping from customization tray, we deal with creating + // widgets/shortcuts/folders in a slightly different way + int[] touchXY = new int[2]; + touchXY[0] = x; + touchXY[1] = y; + switch (info.itemType) { + case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: + mLauncher.addAppWidgetFromDrop(info.componentName, screen, touchXY); + break; + case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER: + mLauncher.addLiveFolderFromDrop(info.componentName, screen, touchXY); + break; + case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: + mLauncher.processShortcutFromDrop(info.componentName, screen, touchXY); + break; + default: + throw new IllegalStateException("Unknown item type: " + info.itemType); + } + cellLayout.onDragExit(); + return; + } + + // This is for other drag/drop cases, like dragging from All Apps ItemInfo info = (ItemInfo) dragInfo; View view = null; @@ -1082,25 +1075,15 @@ public class Workspace extends SmoothPagedView break; case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER: view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, - (ViewGroup) getChildAt(mCurrentPage), - ((UserFolderInfo) info)); - break; - case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: - cellLayout.setTagToCellInfoForPoint(x, y); - int[] position = new int[2]; - position[0] = x; - position[1] = y; - mLauncher.addAppWidgetFromDrop(((LauncherAppWidgetInfo)dragInfo).providerName, - cellLayout.getTag(), position); + cellLayout, ((UserFolderInfo) info)); break; default: - throw new IllegalStateException("Unknown item type: " - + info.itemType); + throw new IllegalStateException("Unknown item type: " + info.itemType); } // If the view is null, it has already been added. if (view == null) { - cellLayout.onDragComplete(); + cellLayout.onDragExit(); } else { mTargetCell = estimateDropCell(x, y, 1, 1, view, cellLayout, mTargetCell); addInScreen(view, indexOfChild(cellLayout), mTargetCell[0], @@ -1109,7 +1092,7 @@ public class Workspace extends SmoothPagedView CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); LauncherModel.addOrMoveItemInDatabase(mLauncher, info, - LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentPage, + LauncherSettings.Favorites.CONTAINER_DESKTOP, screen, lp.cellX, lp.cellY); } } @@ -1123,23 +1106,41 @@ public class Workspace extends SmoothPagedView return (CellLayout) getChildAt(index); } + /** + * Return the current CellInfo describing our current drag; this method exists + * so that Launcher can sync this object with the correct info when the activity is created/ + * destroyed + * + */ + public CellLayout.CellInfo getDragInfo() { + return mDragInfo; + } + /** * {@inheritDoc} */ public boolean acceptDrop(DragSource source, int x, int y, int xOffset, int yOffset, DragView dragView, Object dragInfo) { - final CellLayout layout = getCurrentDropLayout(); + CellLayout layout; + if (mIsSmall) { + layout = findMatchingPageForDragOver(dragView, x - xOffset, y - yOffset); + if (layout == null) { + // cancel the drag if we're not over a mini-screen at time of drop + return false; + } + } else { + layout = getCurrentDropLayout(); + } final CellLayout.CellInfo dragCellInfo = mDragInfo; final int spanX = dragCellInfo == null ? 1 : dragCellInfo.spanX; final int spanY = dragCellInfo == null ? 1 : dragCellInfo.spanY; final View ignoreView = dragCellInfo == null ? null : dragCellInfo.cell; - final CellLayout.CellInfo cellInfo = layout.updateOccupiedCells(null, ignoreView); - if (cellInfo.findCellForSpan(mTempEstimate, spanX, spanY)) { + if (layout.findCellForSpanIgnoring(null, spanX, spanY, ignoreView)) { return true; } else { - Toast.makeText(getContext(), getContext().getString(R.string.out_of_space), Toast.LENGTH_SHORT).show(); + mLauncher.showOutOfSpaceMessage(); return false; } } @@ -1156,9 +1157,9 @@ public class Workspace extends SmoothPagedView layout.estimateDropCell(localPixelX, localPixelY, spanX, spanY, cellXY); layout.cellToPoint(cellXY[0], cellXY[1], mTempEstimate); - final CellLayout.CellInfo cellInfo = layout.updateOccupiedCells(null, ignoreView); // Find the best target drop location - return layout.findNearestVacantArea(mTempEstimate[0], mTempEstimate[1], spanX, spanY, cellInfo, recycle); + return layout.findNearestVacantArea( + mTempEstimate[0], mTempEstimate[1], spanX, spanY, recycle); } /** @@ -1196,6 +1197,10 @@ public class Workspace extends SmoothPagedView mDragInfo = null; } + public boolean isDropEnabled() { + return true; + } + @Override protected void onRestoreInstanceState(Parcelable state) { super.onRestoreInstanceState(state); @@ -1366,8 +1371,6 @@ public class Workspace extends SmoothPagedView } void updateShortcuts(ArrayList apps) { - final PackageManager pm = mLauncher.getPackageManager(); - final int screenCount = getChildCount(); for (int i = 0; i < screenCount; i++) { final CellLayout layout = (CellLayout) getChildAt(i); -- cgit v1.2.3