Refactoring out grid occupancy management in a separate class
Change-Id: I37a830c0f2eb0a0dd4f5fc78fa29127cb18cb3c2
This commit is contained in:
@@ -32,7 +32,6 @@ import android.graphics.Point;
|
|||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.drawable.ColorDrawable;
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.GradientDrawable;
|
|
||||||
import android.graphics.drawable.TransitionDrawable;
|
import android.graphics.drawable.TransitionDrawable;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
@@ -55,6 +54,8 @@ import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
|
|||||||
import com.android.launcher3.config.FeatureFlags;
|
import com.android.launcher3.config.FeatureFlags;
|
||||||
import com.android.launcher3.config.ProviderConfig;
|
import com.android.launcher3.config.ProviderConfig;
|
||||||
import com.android.launcher3.folder.FolderIcon;
|
import com.android.launcher3.folder.FolderIcon;
|
||||||
|
import com.android.launcher3.util.CellAndSpan;
|
||||||
|
import com.android.launcher3.util.GridOccupancy;
|
||||||
import com.android.launcher3.util.ParcelableSparseArray;
|
import com.android.launcher3.util.ParcelableSparseArray;
|
||||||
import com.android.launcher3.util.Thunk;
|
import com.android.launcher3.util.Thunk;
|
||||||
|
|
||||||
@@ -81,9 +82,9 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
private int mFixedCellHeight;
|
private int mFixedCellHeight;
|
||||||
|
|
||||||
@ViewDebug.ExportedProperty(category = "launcher")
|
@ViewDebug.ExportedProperty(category = "launcher")
|
||||||
@Thunk int mCountX;
|
private int mCountX;
|
||||||
@ViewDebug.ExportedProperty(category = "launcher")
|
@ViewDebug.ExportedProperty(category = "launcher")
|
||||||
@Thunk int mCountY;
|
private int mCountY;
|
||||||
|
|
||||||
private int mOriginalWidthGap;
|
private int mOriginalWidthGap;
|
||||||
private int mOriginalHeightGap;
|
private int mOriginalHeightGap;
|
||||||
@@ -101,8 +102,8 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
@Thunk final int[] mTmpPoint = new int[2];
|
@Thunk final int[] mTmpPoint = new int[2];
|
||||||
@Thunk final int[] mTempLocation = new int[2];
|
@Thunk final int[] mTempLocation = new int[2];
|
||||||
|
|
||||||
boolean[][] mOccupied;
|
private GridOccupancy mOccupied;
|
||||||
boolean[][] mTmpOccupied;
|
private GridOccupancy mTmpOccupied;
|
||||||
|
|
||||||
private OnTouchListener mInterceptTouchListener;
|
private OnTouchListener mInterceptTouchListener;
|
||||||
private StylusEventHelper mStylusEventHelper;
|
private StylusEventHelper mStylusEventHelper;
|
||||||
@@ -203,10 +204,12 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
mWidthGap = mOriginalWidthGap = 0;
|
mWidthGap = mOriginalWidthGap = 0;
|
||||||
mHeightGap = mOriginalHeightGap = 0;
|
mHeightGap = mOriginalHeightGap = 0;
|
||||||
mMaxGap = Integer.MAX_VALUE;
|
mMaxGap = Integer.MAX_VALUE;
|
||||||
mCountX = (int) grid.inv.numColumns;
|
|
||||||
mCountY = (int) grid.inv.numRows;
|
mCountX = grid.inv.numColumns;
|
||||||
mOccupied = new boolean[mCountX][mCountY];
|
mCountY = grid.inv.numRows;
|
||||||
mTmpOccupied = new boolean[mCountX][mCountY];
|
mOccupied = new GridOccupancy(mCountX, mCountY);
|
||||||
|
mTmpOccupied = new GridOccupancy(mCountX, mCountY);
|
||||||
|
|
||||||
mPreviousReorderDirection[0] = INVALID_DIRECTION;
|
mPreviousReorderDirection[0] = INVALID_DIRECTION;
|
||||||
mPreviousReorderDirection[1] = INVALID_DIRECTION;
|
mPreviousReorderDirection[1] = INVALID_DIRECTION;
|
||||||
|
|
||||||
@@ -375,8 +378,8 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
public void setGridSize(int x, int y) {
|
public void setGridSize(int x, int y) {
|
||||||
mCountX = x;
|
mCountX = x;
|
||||||
mCountY = y;
|
mCountY = y;
|
||||||
mOccupied = new boolean[mCountX][mCountY];
|
mOccupied = new GridOccupancy(mCountX, mCountY);
|
||||||
mTmpOccupied = new boolean[mCountX][mCountY];
|
mTmpOccupied = new GridOccupancy(mCountX, mCountY);
|
||||||
mTempRectStack.clear();
|
mTempRectStack.clear();
|
||||||
mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap,
|
mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap,
|
||||||
mCountX, mCountY);
|
mCountX, mCountY);
|
||||||
@@ -494,7 +497,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
cd.setBounds(0, 0, mCellWidth, mCellHeight);
|
cd.setBounds(0, 0, mCellWidth, mCellHeight);
|
||||||
for (int i = 0; i < mCountX; i++) {
|
for (int i = 0; i < mCountX; i++) {
|
||||||
for (int j = 0; j < mCountY; j++) {
|
for (int j = 0; j < mCountY; j++) {
|
||||||
if (mOccupied[i][j]) {
|
if (mOccupied.cells[i][j]) {
|
||||||
cellToPoint(i, j, pt);
|
cellToPoint(i, j, pt);
|
||||||
canvas.save();
|
canvas.save();
|
||||||
canvas.translate(pt[0], pt[1]);
|
canvas.translate(pt[0], pt[1]);
|
||||||
@@ -655,14 +658,14 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeAllViews() {
|
public void removeAllViews() {
|
||||||
clearOccupiedCells();
|
mOccupied.clear();
|
||||||
mShortcutsAndWidgets.removeAllViews();
|
mShortcutsAndWidgets.removeAllViews();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeAllViewsInLayout() {
|
public void removeAllViewsInLayout() {
|
||||||
if (mShortcutsAndWidgets.getChildCount() > 0) {
|
if (mShortcutsAndWidgets.getChildCount() > 0) {
|
||||||
clearOccupiedCells();
|
mOccupied.clear();
|
||||||
mShortcutsAndWidgets.removeAllViewsInLayout();
|
mShortcutsAndWidgets.removeAllViewsInLayout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -963,10 +966,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
public boolean animateChildToPosition(final View child, int cellX, int cellY, int duration,
|
public boolean animateChildToPosition(final View child, int cellX, int cellY, int duration,
|
||||||
int delay, boolean permanent, boolean adjustOccupied) {
|
int delay, boolean permanent, boolean adjustOccupied) {
|
||||||
ShortcutAndWidgetContainer clc = getShortcutsAndWidgets();
|
ShortcutAndWidgetContainer clc = getShortcutsAndWidgets();
|
||||||
boolean[][] occupied = mOccupied;
|
|
||||||
if (!permanent) {
|
|
||||||
occupied = mTmpOccupied;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clc.indexOfChild(child) != -1) {
|
if (clc.indexOfChild(child) != -1) {
|
||||||
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
||||||
@@ -981,8 +980,9 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
final int oldX = lp.x;
|
final int oldX = lp.x;
|
||||||
final int oldY = lp.y;
|
final int oldY = lp.y;
|
||||||
if (adjustOccupied) {
|
if (adjustOccupied) {
|
||||||
occupied[lp.cellX][lp.cellY] = false;
|
GridOccupancy occupied = permanent ? mOccupied : mTmpOccupied;
|
||||||
occupied[cellX][cellY] = true;
|
occupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false);
|
||||||
|
occupied.markCells(cellX, cellY, lp.cellHSpan, lp.cellVSpan, true);
|
||||||
}
|
}
|
||||||
lp.isLockedToGrid = true;
|
lp.isLockedToGrid = true;
|
||||||
if (permanent) {
|
if (permanent) {
|
||||||
@@ -1218,7 +1218,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
// First, let's see if this thing fits anywhere
|
// First, let's see if this thing fits anywhere
|
||||||
for (int i = 0; i < minSpanX; i++) {
|
for (int i = 0; i < minSpanX; i++) {
|
||||||
for (int j = 0; j < minSpanY; j++) {
|
for (int j = 0; j < minSpanY; j++) {
|
||||||
if (mOccupied[x + i][y + j]) {
|
if (mOccupied.cells[x + i][y + j]) {
|
||||||
continue inner;
|
continue inner;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1235,7 +1235,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
while (!(hitMaxX && hitMaxY)) {
|
while (!(hitMaxX && hitMaxY)) {
|
||||||
if (incX && !hitMaxX) {
|
if (incX && !hitMaxX) {
|
||||||
for (int j = 0; j < ySize; j++) {
|
for (int j = 0; j < ySize; j++) {
|
||||||
if (x + xSize > countX -1 || mOccupied[x + xSize][y + j]) {
|
if (x + xSize > countX -1 || mOccupied.cells[x + xSize][y + j]) {
|
||||||
// We can't move out horizontally
|
// We can't move out horizontally
|
||||||
hitMaxX = true;
|
hitMaxX = true;
|
||||||
}
|
}
|
||||||
@@ -1245,7 +1245,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
}
|
}
|
||||||
} else if (!hitMaxY) {
|
} else if (!hitMaxY) {
|
||||||
for (int i = 0; i < xSize; i++) {
|
for (int i = 0; i < xSize; i++) {
|
||||||
if (y + ySize > countY - 1 || mOccupied[x + i][y + ySize]) {
|
if (y + ySize > countY - 1 || mOccupied.cells[x + i][y + ySize]) {
|
||||||
// We can't move out vertically
|
// We can't move out vertically
|
||||||
hitMaxY = true;
|
hitMaxY = true;
|
||||||
}
|
}
|
||||||
@@ -1303,7 +1303,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
return bestXY;
|
return bestXY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a vacant area that will fit the given bounds nearest the requested
|
* Find a vacant area that will fit the given bounds nearest the requested
|
||||||
* cell location, and will also weigh in a suggested direction vector of the
|
* cell location, and will also weigh in a suggested direction vector of the
|
||||||
* desired location. This method computers distance based on unit grid distances,
|
* desired location. This method computers distance based on unit grid distances,
|
||||||
@@ -1377,17 +1377,18 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
int[] direction, ItemConfiguration currentState) {
|
int[] direction, ItemConfiguration currentState) {
|
||||||
CellAndSpan c = currentState.map.get(v);
|
CellAndSpan c = currentState.map.get(v);
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, false);
|
mTmpOccupied.markCells(c, false);
|
||||||
markCellsForRect(rectOccupiedByPotentialDrop, mTmpOccupied, true);
|
mTmpOccupied.markCells(rectOccupiedByPotentialDrop, true);
|
||||||
|
|
||||||
findNearestArea(c.x, c.y, c.spanX, c.spanY, direction, mTmpOccupied, null, mTempLocation);
|
findNearestArea(c.cellX, c.cellY, c.spanX, c.spanY, direction,
|
||||||
|
mTmpOccupied.cells, null, mTempLocation);
|
||||||
|
|
||||||
if (mTempLocation[0] >= 0 && mTempLocation[1] >= 0) {
|
if (mTempLocation[0] >= 0 && mTempLocation[1] >= 0) {
|
||||||
c.x = mTempLocation[0];
|
c.cellX = mTempLocation[0];
|
||||||
c.y = mTempLocation[1];
|
c.cellY = mTempLocation[1];
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, true);
|
mTmpOccupied.markCells(c, true);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1440,32 +1441,32 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
CellAndSpan cs = config.map.get(views.get(i));
|
CellAndSpan cs = config.map.get(views.get(i));
|
||||||
switch (which) {
|
switch (which) {
|
||||||
case LEFT:
|
case LEFT:
|
||||||
int left = cs.x;
|
int left = cs.cellX;
|
||||||
for (int j = cs.y; j < cs.y + cs.spanY; j++) {
|
for (int j = cs.cellY; j < cs.cellY + cs.spanY; j++) {
|
||||||
if (left < leftEdge[j] || leftEdge[j] < 0) {
|
if (left < leftEdge[j] || leftEdge[j] < 0) {
|
||||||
leftEdge[j] = left;
|
leftEdge[j] = left;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RIGHT:
|
case RIGHT:
|
||||||
int right = cs.x + cs.spanX;
|
int right = cs.cellX + cs.spanX;
|
||||||
for (int j = cs.y; j < cs.y + cs.spanY; j++) {
|
for (int j = cs.cellY; j < cs.cellY + cs.spanY; j++) {
|
||||||
if (right > rightEdge[j]) {
|
if (right > rightEdge[j]) {
|
||||||
rightEdge[j] = right;
|
rightEdge[j] = right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TOP:
|
case TOP:
|
||||||
int top = cs.y;
|
int top = cs.cellY;
|
||||||
for (int j = cs.x; j < cs.x + cs.spanX; j++) {
|
for (int j = cs.cellX; j < cs.cellX + cs.spanX; j++) {
|
||||||
if (top < topEdge[j] || topEdge[j] < 0) {
|
if (top < topEdge[j] || topEdge[j] < 0) {
|
||||||
topEdge[j] = top;
|
topEdge[j] = top;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case BOTTOM:
|
case BOTTOM:
|
||||||
int bottom = cs.y + cs.spanY;
|
int bottom = cs.cellY + cs.spanY;
|
||||||
for (int j = cs.x; j < cs.x + cs.spanX; j++) {
|
for (int j = cs.cellX; j < cs.cellX + cs.spanX; j++) {
|
||||||
if (bottom > bottomEdge[j]) {
|
if (bottom > bottomEdge[j]) {
|
||||||
bottomEdge[j] = bottom;
|
bottomEdge[j] = bottom;
|
||||||
}
|
}
|
||||||
@@ -1485,29 +1486,29 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
|
|
||||||
switch (whichEdge) {
|
switch (whichEdge) {
|
||||||
case LEFT:
|
case LEFT:
|
||||||
for (int i = cs.y; i < cs.y + cs.spanY; i++) {
|
for (int i = cs.cellY; i < cs.cellY + cs.spanY; i++) {
|
||||||
if (leftEdge[i] == cs.x + cs.spanX) {
|
if (leftEdge[i] == cs.cellX + cs.spanX) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RIGHT:
|
case RIGHT:
|
||||||
for (int i = cs.y; i < cs.y + cs.spanY; i++) {
|
for (int i = cs.cellY; i < cs.cellY + cs.spanY; i++) {
|
||||||
if (rightEdge[i] == cs.x) {
|
if (rightEdge[i] == cs.cellX) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TOP:
|
case TOP:
|
||||||
for (int i = cs.x; i < cs.x + cs.spanX; i++) {
|
for (int i = cs.cellX; i < cs.cellX + cs.spanX; i++) {
|
||||||
if (topEdge[i] == cs.y + cs.spanY) {
|
if (topEdge[i] == cs.cellY + cs.spanY) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case BOTTOM:
|
case BOTTOM:
|
||||||
for (int i = cs.x; i < cs.x + cs.spanX; i++) {
|
for (int i = cs.cellX; i < cs.cellX + cs.spanX; i++) {
|
||||||
if (bottomEdge[i] == cs.y) {
|
if (bottomEdge[i] == cs.cellY) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1521,17 +1522,17 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
CellAndSpan c = config.map.get(v);
|
CellAndSpan c = config.map.get(v);
|
||||||
switch (whichEdge) {
|
switch (whichEdge) {
|
||||||
case LEFT:
|
case LEFT:
|
||||||
c.x -= delta;
|
c.cellX -= delta;
|
||||||
break;
|
break;
|
||||||
case RIGHT:
|
case RIGHT:
|
||||||
c.x += delta;
|
c.cellX += delta;
|
||||||
break;
|
break;
|
||||||
case TOP:
|
case TOP:
|
||||||
c.y -= delta;
|
c.cellY -= delta;
|
||||||
break;
|
break;
|
||||||
case BOTTOM:
|
case BOTTOM:
|
||||||
default:
|
default:
|
||||||
c.y += delta;
|
c.cellY += delta;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1545,16 +1546,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
|
|
||||||
public Rect getBoundingRect() {
|
public Rect getBoundingRect() {
|
||||||
if (boundingRectDirty) {
|
if (boundingRectDirty) {
|
||||||
boolean first = true;
|
config.getBoundingRectForViews(views, boundingRect);
|
||||||
for (View v: views) {
|
|
||||||
CellAndSpan c = config.map.get(v);
|
|
||||||
if (first) {
|
|
||||||
boundingRect.set(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
|
|
||||||
first = false;
|
|
||||||
} else {
|
|
||||||
boundingRect.union(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return boundingRect;
|
return boundingRect;
|
||||||
}
|
}
|
||||||
@@ -1567,14 +1559,14 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
CellAndSpan r = config.map.get(right);
|
CellAndSpan r = config.map.get(right);
|
||||||
switch (whichEdge) {
|
switch (whichEdge) {
|
||||||
case LEFT:
|
case LEFT:
|
||||||
return (r.x + r.spanX) - (l.x + l.spanX);
|
return (r.cellX + r.spanX) - (l.cellX + l.spanX);
|
||||||
case RIGHT:
|
case RIGHT:
|
||||||
return l.x - r.x;
|
return l.cellX - r.cellX;
|
||||||
case TOP:
|
case TOP:
|
||||||
return (r.y + r.spanY) - (l.y + l.spanY);
|
return (r.cellY + r.spanY) - (l.cellY + l.spanY);
|
||||||
case BOTTOM:
|
case BOTTOM:
|
||||||
default:
|
default:
|
||||||
return l.y - r.y;
|
return l.cellY - r.cellY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1618,7 +1610,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
// Mark the occupied state as false for the group of views we want to move.
|
// Mark the occupied state as false for the group of views we want to move.
|
||||||
for (View v: views) {
|
for (View v: views) {
|
||||||
CellAndSpan c = currentState.map.get(v);
|
CellAndSpan c = currentState.map.get(v);
|
||||||
markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, false);
|
mTmpOccupied.markCells(c, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We save the current configuration -- if we fail to find a solution we will revert
|
// We save the current configuration -- if we fail to find a solution we will revert
|
||||||
@@ -1648,7 +1640,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
CellAndSpan c = currentState.map.get(v);
|
CellAndSpan c = currentState.map.get(v);
|
||||||
|
|
||||||
// Adding view to cluster, mark it as not occupied.
|
// Adding view to cluster, mark it as not occupied.
|
||||||
markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, false);
|
mTmpOccupied.markCells(c, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1674,7 +1666,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
// In either case, we set the occupied array as marked for the location of the views
|
// In either case, we set the occupied array as marked for the location of the views
|
||||||
for (View v: cluster.views) {
|
for (View v: cluster.views) {
|
||||||
CellAndSpan c = currentState.map.get(v);
|
CellAndSpan c = currentState.map.get(v);
|
||||||
markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, true);
|
mTmpOccupied.markCells(c, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return foundSolution;
|
return foundSolution;
|
||||||
@@ -1685,37 +1677,31 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
if (views.size() == 0) return true;
|
if (views.size() == 0) return true;
|
||||||
|
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
Rect boundingRect = null;
|
Rect boundingRect = new Rect();
|
||||||
// We construct a rect which represents the entire group of views passed in
|
// We construct a rect which represents the entire group of views passed in
|
||||||
for (View v: views) {
|
currentState.getBoundingRectForViews(views, boundingRect);
|
||||||
CellAndSpan c = currentState.map.get(v);
|
|
||||||
if (boundingRect == null) {
|
|
||||||
boundingRect = new Rect(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
|
|
||||||
} else {
|
|
||||||
boundingRect.union(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark the occupied state as false for the group of views we want to move.
|
// Mark the occupied state as false for the group of views we want to move.
|
||||||
for (View v: views) {
|
for (View v: views) {
|
||||||
CellAndSpan c = currentState.map.get(v);
|
CellAndSpan c = currentState.map.get(v);
|
||||||
markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, false);
|
mTmpOccupied.markCells(c, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean[][] blockOccupied = new boolean[boundingRect.width()][boundingRect.height()];
|
GridOccupancy blockOccupied = new GridOccupancy(boundingRect.width(), boundingRect.height());
|
||||||
int top = boundingRect.top;
|
int top = boundingRect.top;
|
||||||
int left = boundingRect.left;
|
int left = boundingRect.left;
|
||||||
// We mark more precisely which parts of the bounding rect are truly occupied, allowing
|
// We mark more precisely which parts of the bounding rect are truly occupied, allowing
|
||||||
// for interlocking.
|
// for interlocking.
|
||||||
for (View v: views) {
|
for (View v: views) {
|
||||||
CellAndSpan c = currentState.map.get(v);
|
CellAndSpan c = currentState.map.get(v);
|
||||||
markCellsForView(c.x - left, c.y - top, c.spanX, c.spanY, blockOccupied, true);
|
blockOccupied.markCells(c.cellX - left, c.cellY - top, c.spanX, c.spanY, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
markCellsForRect(rectOccupiedByPotentialDrop, mTmpOccupied, true);
|
mTmpOccupied.markCells(rectOccupiedByPotentialDrop, true);
|
||||||
|
|
||||||
findNearestArea(boundingRect.left, boundingRect.top, boundingRect.width(),
|
findNearestArea(boundingRect.left, boundingRect.top, boundingRect.width(),
|
||||||
boundingRect.height(), direction, mTmpOccupied, blockOccupied, mTempLocation);
|
boundingRect.height(), direction,
|
||||||
|
mTmpOccupied.cells, blockOccupied.cells, mTempLocation);
|
||||||
|
|
||||||
// If we successfuly found a location by pushing the block of views, we commit it
|
// If we successfuly found a location by pushing the block of views, we commit it
|
||||||
if (mTempLocation[0] >= 0 && mTempLocation[1] >= 0) {
|
if (mTempLocation[0] >= 0 && mTempLocation[1] >= 0) {
|
||||||
@@ -1723,8 +1709,8 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
int deltaY = mTempLocation[1] - boundingRect.top;
|
int deltaY = mTempLocation[1] - boundingRect.top;
|
||||||
for (View v: views) {
|
for (View v: views) {
|
||||||
CellAndSpan c = currentState.map.get(v);
|
CellAndSpan c = currentState.map.get(v);
|
||||||
c.x += deltaX;
|
c.cellX += deltaX;
|
||||||
c.y += deltaY;
|
c.cellY += deltaY;
|
||||||
}
|
}
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
@@ -1732,15 +1718,11 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
// In either case, we set the occupied array as marked for the location of the views
|
// In either case, we set the occupied array as marked for the location of the views
|
||||||
for (View v: views) {
|
for (View v: views) {
|
||||||
CellAndSpan c = currentState.map.get(v);
|
CellAndSpan c = currentState.map.get(v);
|
||||||
markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, true);
|
mTmpOccupied.markCells(c, true);
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void markCellsForRect(Rect r, boolean[][] occupied, boolean value) {
|
|
||||||
markCellsForView(r.left, r.top, r.width(), r.height(), occupied, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This method tries to find a reordering solution which satisfies the push mechanic by trying
|
// This method tries to find a reordering solution which satisfies the push mechanic by trying
|
||||||
// to push items in each of the cardinal directions, in an order based on the direction vector
|
// to push items in each of the cardinal directions, in an order based on the direction vector
|
||||||
// passed.
|
// passed.
|
||||||
@@ -1850,8 +1832,8 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
if (ignoreView != null) {
|
if (ignoreView != null) {
|
||||||
CellAndSpan c = solution.map.get(ignoreView);
|
CellAndSpan c = solution.map.get(ignoreView);
|
||||||
if (c != null) {
|
if (c != null) {
|
||||||
c.x = cellX;
|
c.cellX = cellX;
|
||||||
c.y = cellY;
|
c.cellY = cellY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Rect r0 = new Rect(cellX, cellY, cellX + spanX, cellY + spanY);
|
Rect r0 = new Rect(cellX, cellY, cellX + spanX, cellY + spanY);
|
||||||
@@ -1860,7 +1842,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
if (child == ignoreView) continue;
|
if (child == ignoreView) continue;
|
||||||
CellAndSpan c = solution.map.get(child);
|
CellAndSpan c = solution.map.get(child);
|
||||||
LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
||||||
r1.set(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
|
r1.set(c.cellX, c.cellY, c.cellX + c.spanX, c.cellY + c.spanY);
|
||||||
if (Rect.intersects(r0, r1)) {
|
if (Rect.intersects(r0, r1)) {
|
||||||
if (!lp.canReorder) {
|
if (!lp.canReorder) {
|
||||||
return false;
|
return false;
|
||||||
@@ -1911,14 +1893,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void copyOccupiedArray(boolean[][] occupied) {
|
|
||||||
for (int i = 0; i < mCountX; i++) {
|
|
||||||
for (int j = 0; j < mCountY; j++) {
|
|
||||||
occupied[i][j] = mOccupied[i][j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX, int minSpanY,
|
private ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX, int minSpanY,
|
||||||
int spanX, int spanY, int[] direction, View dragView, boolean decX,
|
int spanX, int spanY, int[] direction, View dragView, boolean decX,
|
||||||
ItemConfiguration solution) {
|
ItemConfiguration solution) {
|
||||||
@@ -1926,7 +1900,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
copyCurrentStateToSolution(solution, false);
|
copyCurrentStateToSolution(solution, false);
|
||||||
// Copy the current occupied array into the temporary occupied array. This array will be
|
// Copy the current occupied array into the temporary occupied array. This array will be
|
||||||
// manipulated as necessary to find a solution.
|
// manipulated as necessary to find a solution.
|
||||||
copyOccupiedArray(mTmpOccupied);
|
mOccupied.copyTo(mTmpOccupied);
|
||||||
|
|
||||||
// We find the nearest cell into which we would place the dragged item, assuming there's
|
// We find the nearest cell into which we would place the dragged item, assuming there's
|
||||||
// nothing in its way.
|
// nothing in its way.
|
||||||
@@ -1952,10 +1926,10 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
solution.isSolution = false;
|
solution.isSolution = false;
|
||||||
} else {
|
} else {
|
||||||
solution.isSolution = true;
|
solution.isSolution = true;
|
||||||
solution.dragViewX = result[0];
|
solution.cellX = result[0];
|
||||||
solution.dragViewY = result[1];
|
solution.cellY = result[1];
|
||||||
solution.dragViewSpanX = spanX;
|
solution.spanX = spanX;
|
||||||
solution.dragViewSpanY = spanY;
|
solution.spanY = spanY;
|
||||||
}
|
}
|
||||||
return solution;
|
return solution;
|
||||||
}
|
}
|
||||||
@@ -1976,11 +1950,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void copySolutionToTempState(ItemConfiguration solution, View dragView) {
|
private void copySolutionToTempState(ItemConfiguration solution, View dragView) {
|
||||||
for (int i = 0; i < mCountX; i++) {
|
mTmpOccupied.clear();
|
||||||
for (int j = 0; j < mCountY; j++) {
|
|
||||||
mTmpOccupied[i][j] = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int childCount = mShortcutsAndWidgets.getChildCount();
|
int childCount = mShortcutsAndWidgets.getChildCount();
|
||||||
for (int i = 0; i < childCount; i++) {
|
for (int i = 0; i < childCount; i++) {
|
||||||
@@ -1989,26 +1959,21 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
||||||
CellAndSpan c = solution.map.get(child);
|
CellAndSpan c = solution.map.get(child);
|
||||||
if (c != null) {
|
if (c != null) {
|
||||||
lp.tmpCellX = c.x;
|
lp.tmpCellX = c.cellX;
|
||||||
lp.tmpCellY = c.y;
|
lp.tmpCellY = c.cellY;
|
||||||
lp.cellHSpan = c.spanX;
|
lp.cellHSpan = c.spanX;
|
||||||
lp.cellVSpan = c.spanY;
|
lp.cellVSpan = c.spanY;
|
||||||
markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, true);
|
mTmpOccupied.markCells(c, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
markCellsForView(solution.dragViewX, solution.dragViewY, solution.dragViewSpanX,
|
mTmpOccupied.markCells(solution, true);
|
||||||
solution.dragViewSpanY, mTmpOccupied, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void animateItemsToSolution(ItemConfiguration solution, View dragView, boolean
|
private void animateItemsToSolution(ItemConfiguration solution, View dragView, boolean
|
||||||
commitDragView) {
|
commitDragView) {
|
||||||
|
|
||||||
boolean[][] occupied = DESTRUCTIVE_REORDER ? mOccupied : mTmpOccupied;
|
GridOccupancy occupied = DESTRUCTIVE_REORDER ? mOccupied : mTmpOccupied;
|
||||||
for (int i = 0; i < mCountX; i++) {
|
occupied.clear();
|
||||||
for (int j = 0; j < mCountY; j++) {
|
|
||||||
occupied[i][j] = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int childCount = mShortcutsAndWidgets.getChildCount();
|
int childCount = mShortcutsAndWidgets.getChildCount();
|
||||||
for (int i = 0; i < childCount; i++) {
|
for (int i = 0; i < childCount; i++) {
|
||||||
@@ -2016,14 +1981,13 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
if (child == dragView) continue;
|
if (child == dragView) continue;
|
||||||
CellAndSpan c = solution.map.get(child);
|
CellAndSpan c = solution.map.get(child);
|
||||||
if (c != null) {
|
if (c != null) {
|
||||||
animateChildToPosition(child, c.x, c.y, REORDER_ANIMATION_DURATION, 0,
|
animateChildToPosition(child, c.cellX, c.cellY, REORDER_ANIMATION_DURATION, 0,
|
||||||
DESTRUCTIVE_REORDER, false);
|
DESTRUCTIVE_REORDER, false);
|
||||||
markCellsForView(c.x, c.y, c.spanX, c.spanY, occupied, true);
|
occupied.markCells(c, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (commitDragView) {
|
if (commitDragView) {
|
||||||
markCellsForView(solution.dragViewX, solution.dragViewY, solution.dragViewSpanX,
|
occupied.markCells(solution, true);
|
||||||
solution.dragViewSpanY, occupied, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2042,7 +2006,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
||||||
if (c != null && !skip) {
|
if (c != null && !skip) {
|
||||||
ReorderPreviewAnimation rha = new ReorderPreviewAnimation(child, mode, lp.cellX,
|
ReorderPreviewAnimation rha = new ReorderPreviewAnimation(child, mode, lp.cellX,
|
||||||
lp.cellY, c.x, c.y, c.spanX, c.spanY);
|
lp.cellY, c.cellX, c.cellY, c.spanX, c.spanY);
|
||||||
rha.animate();
|
rha.animate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2186,11 +2150,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void commitTempPlacement() {
|
private void commitTempPlacement() {
|
||||||
for (int i = 0; i < mCountX; i++) {
|
mTmpOccupied.copyTo(mOccupied);
|
||||||
for (int j = 0; j < mCountY; j++) {
|
|
||||||
mOccupied[i][j] = mTmpOccupied[i][j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
long screenId = mLauncher.getWorkspace().getIdForScreen(this);
|
long screenId = mLauncher.getWorkspace().getIdForScreen(this);
|
||||||
int container = Favorites.CONTAINER_DESKTOP;
|
int container = Favorites.CONTAINER_DESKTOP;
|
||||||
@@ -2241,10 +2201,10 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
resultSpan);
|
resultSpan);
|
||||||
if (result[0] >= 0 && result[1] >= 0) {
|
if (result[0] >= 0 && result[1] >= 0) {
|
||||||
copyCurrentStateToSolution(solution, false);
|
copyCurrentStateToSolution(solution, false);
|
||||||
solution.dragViewX = result[0];
|
solution.cellX = result[0];
|
||||||
solution.dragViewY = result[1];
|
solution.cellY = result[1];
|
||||||
solution.dragViewSpanX = resultSpan[0];
|
solution.spanX = resultSpan[0];
|
||||||
solution.dragViewSpanY = resultSpan[1];
|
solution.spanY = resultSpan[1];
|
||||||
solution.isSolution = true;
|
solution.isSolution = true;
|
||||||
} else {
|
} else {
|
||||||
solution.isSolution = false;
|
solution.isSolution = false;
|
||||||
@@ -2432,10 +2392,10 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
if (finalSolution != null) {
|
if (finalSolution != null) {
|
||||||
beginOrAdjustReorderPreviewAnimations(finalSolution, dragView, 0,
|
beginOrAdjustReorderPreviewAnimations(finalSolution, dragView, 0,
|
||||||
ReorderPreviewAnimation.MODE_HINT);
|
ReorderPreviewAnimation.MODE_HINT);
|
||||||
result[0] = finalSolution.dragViewX;
|
result[0] = finalSolution.cellX;
|
||||||
result[1] = finalSolution.dragViewY;
|
result[1] = finalSolution.cellY;
|
||||||
resultSpan[0] = finalSolution.dragViewSpanX;
|
resultSpan[0] = finalSolution.spanX;
|
||||||
resultSpan[1] = finalSolution.dragViewSpanY;
|
resultSpan[1] = finalSolution.spanY;
|
||||||
} else {
|
} else {
|
||||||
result[0] = result[1] = resultSpan[0] = resultSpan[1] = -1;
|
result[0] = result[1] = resultSpan[0] = resultSpan[1] = -1;
|
||||||
}
|
}
|
||||||
@@ -2448,10 +2408,10 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (finalSolution != null) {
|
if (finalSolution != null) {
|
||||||
result[0] = finalSolution.dragViewX;
|
result[0] = finalSolution.cellX;
|
||||||
result[1] = finalSolution.dragViewY;
|
result[1] = finalSolution.cellY;
|
||||||
resultSpan[0] = finalSolution.dragViewSpanX;
|
resultSpan[0] = finalSolution.spanX;
|
||||||
resultSpan[1] = finalSolution.dragViewSpanY;
|
resultSpan[1] = finalSolution.spanY;
|
||||||
|
|
||||||
// If we're just testing for a possible location (MODE_ACCEPT_DROP), we don't bother
|
// If we're just testing for a possible location (MODE_ACCEPT_DROP), we don't bother
|
||||||
// committing anything or animating anything as we just want to determine if a solution
|
// committing anything or animating anything as we just want to determine if a solution
|
||||||
@@ -2493,25 +2453,24 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
return mItemPlacementDirty;
|
return mItemPlacementDirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Thunk class ItemConfiguration {
|
private static class ItemConfiguration extends CellAndSpan {
|
||||||
HashMap<View, CellAndSpan> map = new HashMap<View, CellAndSpan>();
|
HashMap<View, CellAndSpan> map = new HashMap<View, CellAndSpan>();
|
||||||
private HashMap<View, CellAndSpan> savedMap = new HashMap<View, CellAndSpan>();
|
private HashMap<View, CellAndSpan> savedMap = new HashMap<View, CellAndSpan>();
|
||||||
ArrayList<View> sortedViews = new ArrayList<View>();
|
ArrayList<View> sortedViews = new ArrayList<View>();
|
||||||
ArrayList<View> intersectingViews;
|
ArrayList<View> intersectingViews;
|
||||||
boolean isSolution = false;
|
boolean isSolution = false;
|
||||||
int dragViewX, dragViewY, dragViewSpanX, dragViewSpanY;
|
|
||||||
|
|
||||||
void save() {
|
void save() {
|
||||||
// Copy current state into savedMap
|
// Copy current state into savedMap
|
||||||
for (View v: map.keySet()) {
|
for (View v: map.keySet()) {
|
||||||
map.get(v).copy(savedMap.get(v));
|
savedMap.get(v).copyFrom(map.get(v));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void restore() {
|
void restore() {
|
||||||
// Restore current state from savedMap
|
// Restore current state from savedMap
|
||||||
for (View v: savedMap.keySet()) {
|
for (View v: savedMap.keySet()) {
|
||||||
savedMap.get(v).copy(map.get(v));
|
map.get(v).copyFrom(savedMap.get(v));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2522,35 +2481,21 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int area() {
|
int area() {
|
||||||
return dragViewSpanX * dragViewSpanY;
|
return spanX * spanY;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class CellAndSpan {
|
|
||||||
int x, y;
|
|
||||||
int spanX, spanY;
|
|
||||||
|
|
||||||
public CellAndSpan() {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void copy(CellAndSpan copy) {
|
void getBoundingRectForViews(ArrayList<View> views, Rect outRect) {
|
||||||
copy.x = x;
|
boolean first = true;
|
||||||
copy.y = y;
|
for (View v: views) {
|
||||||
copy.spanX = spanX;
|
CellAndSpan c = map.get(v);
|
||||||
copy.spanY = spanY;
|
if (first) {
|
||||||
|
outRect.set(c.cellX, c.cellY, c.cellX + c.spanX, c.cellY + c.spanY);
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
outRect.union(c.cellX, c.cellY, c.cellX + c.spanX, c.cellY + c.spanY);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public CellAndSpan(int x, int y, int spanX, int spanY) {
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
this.spanX = spanX;
|
|
||||||
this.spanY = spanY;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
return "(" + x + ", " + y + ": " + spanX + ", " + spanY + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2588,33 +2533,10 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
* @return True if a vacant cell of the specified dimension was found, false otherwise.
|
* @return True if a vacant cell of the specified dimension was found, false otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
|
public boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
|
||||||
boolean foundCell = false;
|
if (cellXY == null) {
|
||||||
final int endX = mCountX - (spanX - 1);
|
cellXY = new int[2];
|
||||||
final int endY = mCountY - (spanY - 1);
|
|
||||||
|
|
||||||
for (int y = 0; y < endY && !foundCell; y++) {
|
|
||||||
inner:
|
|
||||||
for (int x = 0; x < endX; x++) {
|
|
||||||
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 after the column we just found
|
|
||||||
// an occupied cell
|
|
||||||
x += i;
|
|
||||||
continue inner;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (cellXY != null) {
|
|
||||||
cellXY[0] = x;
|
|
||||||
cellXY[1] = y;
|
|
||||||
}
|
|
||||||
foundCell = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return mOccupied.findVacantCell(cellXY, spanX, spanY);
|
||||||
return foundCell;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2688,34 +2610,16 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
resultRect.set(x, y, x + width, y + height);
|
resultRect.set(x, y, x + width, y + height);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearOccupiedCells() {
|
|
||||||
for (int x = 0; x < mCountX; x++) {
|
|
||||||
for (int y = 0; y < mCountY; y++) {
|
|
||||||
mOccupied[x][y] = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void markCellsAsOccupiedForView(View view) {
|
public void markCellsAsOccupiedForView(View view) {
|
||||||
if (view == null || view.getParent() != mShortcutsAndWidgets) return;
|
if (view == null || view.getParent() != mShortcutsAndWidgets) return;
|
||||||
LayoutParams lp = (LayoutParams) view.getLayoutParams();
|
LayoutParams lp = (LayoutParams) view.getLayoutParams();
|
||||||
markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, mOccupied, true);
|
mOccupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void markCellsAsUnoccupiedForView(View view) {
|
public void markCellsAsUnoccupiedForView(View view) {
|
||||||
if (view == null || view.getParent() != mShortcutsAndWidgets) return;
|
if (view == null || view.getParent() != mShortcutsAndWidgets) return;
|
||||||
LayoutParams lp = (LayoutParams) view.getLayoutParams();
|
LayoutParams lp = (LayoutParams) view.getLayoutParams();
|
||||||
markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, mOccupied, false);
|
mOccupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false);
|
||||||
}
|
|
||||||
|
|
||||||
private void markCellsForView(int cellX, int cellY, int spanX, int spanY, boolean[][] occupied,
|
|
||||||
boolean value) {
|
|
||||||
if (cellX < 0 || cellY < 0) return;
|
|
||||||
for (int x = cellX; x < cellX + spanX && x < mCountX; x++) {
|
|
||||||
for (int y = cellY; y < cellY + spanY && y < mCountY; y++) {
|
|
||||||
occupied[x][y] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getDesiredWidth() {
|
public int getDesiredWidth() {
|
||||||
@@ -2730,7 +2634,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
|
|
||||||
public boolean isOccupied(int x, int y) {
|
public boolean isOccupied(int x, int y) {
|
||||||
if (x < mCountX && y < mCountY) {
|
if (x < mCountX && y < mCountY) {
|
||||||
return mOccupied[x][y];
|
return mOccupied.cells[x][y];
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException("Position exceeds the bound of this CellLayout");
|
throw new RuntimeException("Position exceeds the bound of this CellLayout");
|
||||||
}
|
}
|
||||||
@@ -2909,21 +2813,17 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
// 2. When long clicking on an empty cell in a CellLayout, we save information about the
|
// 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
|
// cellX and cellY coordinates and which page was clicked. We then set this as a tag on
|
||||||
// the CellLayout that was long clicked
|
// the CellLayout that was long clicked
|
||||||
public static final class CellInfo {
|
public static final class CellInfo extends CellAndSpan {
|
||||||
public View cell;
|
public View cell;
|
||||||
int cellX = -1;
|
|
||||||
int cellY = -1;
|
|
||||||
int spanX;
|
|
||||||
int spanY;
|
|
||||||
long screenId;
|
long screenId;
|
||||||
long container;
|
long container;
|
||||||
|
|
||||||
public CellInfo(View v, ItemInfo info) {
|
public CellInfo(View v, ItemInfo info) {
|
||||||
cell = v;
|
|
||||||
cellX = info.cellX;
|
cellX = info.cellX;
|
||||||
cellY = info.cellY;
|
cellY = info.cellY;
|
||||||
spanX = info.spanX;
|
spanX = info.spanX;
|
||||||
spanY = info.spanY;
|
spanY = info.spanY;
|
||||||
|
cell = v;
|
||||||
screenId = info.screenId;
|
screenId = info.screenId;
|
||||||
container = info.container;
|
container = info.container;
|
||||||
}
|
}
|
||||||
@@ -2935,10 +2835,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean findVacantCell(int spanX, int spanY, int[] outXY) {
|
|
||||||
return Utilities.findVacantCell(outXY, spanX, spanY, mCountX, mCountY, mOccupied);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether an item can be placed in this CellLayout (after rearranging and/or resizing
|
* Returns whether an item can be placed in this CellLayout (after rearranging and/or resizing
|
||||||
* if necessary).
|
* if necessary).
|
||||||
@@ -2960,19 +2856,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRegionVacant(int x, int y, int spanX, int spanY) {
|
public boolean isRegionVacant(int x, int y, int spanX, int spanY) {
|
||||||
int x2 = x + spanX - 1;
|
return mOccupied.isRegionVacant(x, y, spanX, spanY);
|
||||||
int y2 = y + spanY - 1;
|
|
||||||
if (x < 0 || y < 0 || x2 >= mCountX || y2 >= mCountY) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (int i = x; i <= x2; i++) {
|
|
||||||
for (int j = y; j <= y2; j++) {
|
|
||||||
if (mOccupied[i][j]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ import com.android.launcher3.model.WidgetsModel;
|
|||||||
import com.android.launcher3.util.ComponentKey;
|
import com.android.launcher3.util.ComponentKey;
|
||||||
import com.android.launcher3.util.CursorIconInfo;
|
import com.android.launcher3.util.CursorIconInfo;
|
||||||
import com.android.launcher3.util.FlagOp;
|
import com.android.launcher3.util.FlagOp;
|
||||||
|
import com.android.launcher3.util.GridOccupancy;
|
||||||
import com.android.launcher3.util.LongArrayMap;
|
import com.android.launcher3.util.LongArrayMap;
|
||||||
import com.android.launcher3.util.ManagedProfileHeuristic;
|
import com.android.launcher3.util.ManagedProfileHeuristic;
|
||||||
import com.android.launcher3.util.PackageManagerHelper;
|
import com.android.launcher3.util.PackageManagerHelper;
|
||||||
@@ -374,21 +375,14 @@ public class LauncherModel extends BroadcastReceiver
|
|||||||
int[] xy, int spanX, int spanY) {
|
int[] xy, int spanX, int spanY) {
|
||||||
LauncherAppState app = LauncherAppState.getInstance();
|
LauncherAppState app = LauncherAppState.getInstance();
|
||||||
InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
|
InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
|
||||||
final int xCount = (int) profile.numColumns;
|
|
||||||
final int yCount = (int) profile.numRows;
|
GridOccupancy occupied = new GridOccupancy(profile.numColumns, profile.numRows);
|
||||||
boolean[][] occupied = new boolean[xCount][yCount];
|
|
||||||
if (occupiedPos != null) {
|
if (occupiedPos != null) {
|
||||||
for (ItemInfo r : occupiedPos) {
|
for (ItemInfo r : occupiedPos) {
|
||||||
int right = r.cellX + r.spanX;
|
occupied.markCells(r, true);
|
||||||
int bottom = r.cellY + r.spanY;
|
|
||||||
for (int x = r.cellX; 0 <= x && x < right && x < xCount; x++) {
|
|
||||||
for (int y = r.cellY; 0 <= y && y < bottom && y < yCount; y++) {
|
|
||||||
occupied[x][y] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Utilities.findVacantCell(xy, spanX, spanY, xCount, yCount, occupied);
|
return occupied.findVacantCell(xy, spanX, spanY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1468,12 +1462,10 @@ public class LauncherModel extends BroadcastReceiver
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check & update map of what's occupied; used to discard overlapping/invalid items
|
// check & update map of what's occupied; used to discard overlapping/invalid items
|
||||||
private boolean checkItemPlacement(LongArrayMap<ItemInfo[][]> occupied, ItemInfo item,
|
private boolean checkItemPlacement(LongArrayMap<GridOccupancy> occupied, ItemInfo item,
|
||||||
ArrayList<Long> workspaceScreens) {
|
ArrayList<Long> workspaceScreens) {
|
||||||
LauncherAppState app = LauncherAppState.getInstance();
|
LauncherAppState app = LauncherAppState.getInstance();
|
||||||
InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
|
InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
|
||||||
final int countX = profile.numColumns;
|
|
||||||
final int countY = profile.numRows;
|
|
||||||
|
|
||||||
long containerIndex = item.screenId;
|
long containerIndex = item.screenId;
|
||||||
if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
|
if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
|
||||||
@@ -1486,7 +1478,7 @@ public class LauncherModel extends BroadcastReceiver
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ItemInfo[][] hotseatItems =
|
final GridOccupancy hotseatOccupancy =
|
||||||
occupied.get((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT);
|
occupied.get((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT);
|
||||||
|
|
||||||
if (item.screenId >= profile.numHotseatIcons) {
|
if (item.screenId >= profile.numHotseatIcons) {
|
||||||
@@ -1497,22 +1489,20 @@ public class LauncherModel extends BroadcastReceiver
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hotseatItems != null) {
|
if (hotseatOccupancy != null) {
|
||||||
if (hotseatItems[(int) item.screenId][0] != null) {
|
if (hotseatOccupancy.cells[(int) item.screenId][0]) {
|
||||||
Log.e(TAG, "Error loading shortcut into hotseat " + item
|
Log.e(TAG, "Error loading shortcut into hotseat " + item
|
||||||
+ " into position (" + item.screenId + ":" + item.cellX + ","
|
+ " into position (" + item.screenId + ":" + item.cellX + ","
|
||||||
+ item.cellY + ") occupied by "
|
+ item.cellY + ") already occupied");
|
||||||
+ occupied.get(LauncherSettings.Favorites.CONTAINER_HOTSEAT)
|
|
||||||
[(int) item.screenId][0]);
|
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
hotseatItems[(int) item.screenId][0] = item;
|
hotseatOccupancy.cells[(int) item.screenId][0] = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final ItemInfo[][] items = new ItemInfo[(int) profile.numHotseatIcons][1];
|
final GridOccupancy occupancy = new GridOccupancy(profile.numHotseatIcons, 1);
|
||||||
items[(int) item.screenId][0] = item;
|
occupancy.cells[(int) item.screenId][0] = true;
|
||||||
occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, items);
|
occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, occupancy);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
|
} else if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
|
||||||
@@ -1525,12 +1515,8 @@ public class LauncherModel extends BroadcastReceiver
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!occupied.containsKey(item.screenId)) {
|
final int countX = profile.numColumns;
|
||||||
ItemInfo[][] items = new ItemInfo[countX + 1][countY + 1];
|
final int countY = profile.numRows;
|
||||||
occupied.put(item.screenId, items);
|
|
||||||
}
|
|
||||||
|
|
||||||
final ItemInfo[][] screens = occupied.get(item.screenId);
|
|
||||||
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
|
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
|
||||||
item.cellX < 0 || item.cellY < 0 ||
|
item.cellX < 0 || item.cellY < 0 ||
|
||||||
item.cellX + item.spanX > countX || item.cellY + item.spanY > countY) {
|
item.cellX + item.spanX > countX || item.cellY + item.spanY > countY) {
|
||||||
@@ -1541,26 +1527,22 @@ public class LauncherModel extends BroadcastReceiver
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if any workspace icons overlap with each other
|
if (!occupied.containsKey(item.screenId)) {
|
||||||
for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
|
occupied.put(item.screenId, new GridOccupancy(countX + 1, countY + 1));
|
||||||
for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
|
|
||||||
if (screens[x][y] != null) {
|
|
||||||
Log.e(TAG, "Error loading shortcut " + item
|
|
||||||
+ " into cell (" + containerIndex + "-" + item.screenId + ":"
|
|
||||||
+ x + "," + y
|
|
||||||
+ ") occupied by "
|
|
||||||
+ screens[x][y]);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
|
|
||||||
for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
|
|
||||||
screens[x][y] = item;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
final GridOccupancy occupancy = occupied.get(item.screenId);
|
||||||
|
|
||||||
return true;
|
// Check if any workspace icons overlap with each other
|
||||||
|
if (occupancy.isRegionVacant(item.cellX, item.cellY, item.spanX, item.spanY)) {
|
||||||
|
occupancy.markCells(item, true);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Error loading shortcut " + item
|
||||||
|
+ " into cell (" + containerIndex + "-" + item.screenId + ":"
|
||||||
|
+ item.cellX + "," + item.cellX + "," + item.spanX + "," + item.spanY
|
||||||
|
+ ") already occupied");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Clears all the sBg data structures */
|
/** Clears all the sBg data structures */
|
||||||
@@ -1621,7 +1603,7 @@ public class LauncherModel extends BroadcastReceiver
|
|||||||
// +1 for the hotseat (it can be larger than the workspace)
|
// +1 for the hotseat (it can be larger than the workspace)
|
||||||
// Load workspace in reverse order to ensure that latest items are loaded first (and
|
// Load workspace in reverse order to ensure that latest items are loaded first (and
|
||||||
// before any earlier duplicates)
|
// before any earlier duplicates)
|
||||||
final LongArrayMap<ItemInfo[][]> occupied = new LongArrayMap<>();
|
final LongArrayMap<GridOccupancy> occupied = new LongArrayMap<>();
|
||||||
HashMap<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null;
|
HashMap<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -2185,14 +2167,6 @@ public class LauncherModel extends BroadcastReceiver
|
|||||||
if (screenId > 0) {
|
if (screenId > 0) {
|
||||||
line += " | ";
|
line += " | ";
|
||||||
}
|
}
|
||||||
ItemInfo[][] screen = occupied.valueAt(i);
|
|
||||||
for (int x = 0; x < countX; x++) {
|
|
||||||
if (x < screen.length && y < screen[x].length) {
|
|
||||||
line += (screen[x][y] != null) ? "#" : ".";
|
|
||||||
} else {
|
|
||||||
line += "!";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Log.d(TAG, "[ " + line + " ]");
|
Log.d(TAG, "[ " + line + " ]");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -654,39 +654,6 @@ public final class Utilities {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the first vacant cell, if there is one.
|
|
||||||
*
|
|
||||||
* @param vacant Holds the x and y coordinate of the vacant cell
|
|
||||||
* @param spanX Horizontal cell span.
|
|
||||||
* @param spanY Vertical cell span.
|
|
||||||
*
|
|
||||||
* @return true if a vacant cell was found
|
|
||||||
*/
|
|
||||||
public static boolean findVacantCell(int[] vacant, int spanX, int spanY,
|
|
||||||
int xCount, int yCount, boolean[][] occupied) {
|
|
||||||
|
|
||||||
for (int y = 0; (y + spanY) <= yCount; y++) {
|
|
||||||
for (int x = 0; (x + spanX) <= xCount; x++) {
|
|
||||||
boolean available = !occupied[x][y];
|
|
||||||
out: for (int i = x; i < x + spanX; i++) {
|
|
||||||
for (int j = y; j < y + spanY; j++) {
|
|
||||||
available = available && !occupied[i][j];
|
|
||||||
if (!available) break out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (available) {
|
|
||||||
vacant[0] = x;
|
|
||||||
vacant[1] = y;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trims the string, removing all whitespace at the beginning and end of the string.
|
* Trims the string, removing all whitespace at the beginning and end of the string.
|
||||||
* Non-breaking whitespaces are also removed.
|
* Non-breaking whitespaces are also removed.
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import com.android.launcher3.Utilities;
|
|||||||
import com.android.launcher3.backup.nano.BackupProtos;
|
import com.android.launcher3.backup.nano.BackupProtos;
|
||||||
import com.android.launcher3.compat.AppWidgetManagerCompat;
|
import com.android.launcher3.compat.AppWidgetManagerCompat;
|
||||||
import com.android.launcher3.compat.PackageInstallerCompat;
|
import com.android.launcher3.compat.PackageInstallerCompat;
|
||||||
|
import com.android.launcher3.util.GridOccupancy;
|
||||||
import com.android.launcher3.util.LongArrayMap;
|
import com.android.launcher3.util.LongArrayMap;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -220,7 +221,7 @@ public class GridSizeMigrationTask {
|
|||||||
// {@link #mCarryOver}, to prevent an infinite loop. If no item could be removed,
|
// {@link #mCarryOver}, to prevent an infinite loop. If no item could be removed,
|
||||||
// break the loop and abort migration by throwing an exception.
|
// break the loop and abort migration by throwing an exception.
|
||||||
OptimalPlacementSolution placement = new OptimalPlacementSolution(
|
OptimalPlacementSolution placement = new OptimalPlacementSolution(
|
||||||
new boolean[mTrgX][mTrgY], deepCopy(mCarryOver), true);
|
new GridOccupancy(mTrgX, mTrgY), deepCopy(mCarryOver), true);
|
||||||
placement.find();
|
placement.find();
|
||||||
if (placement.finalPlacedItems.size() > 0) {
|
if (placement.finalPlacedItems.size() > 0) {
|
||||||
long newScreenId = LauncherSettings.Settings.call(
|
long newScreenId = LauncherSettings.Settings.call(
|
||||||
@@ -336,9 +337,9 @@ public class GridSizeMigrationTask {
|
|||||||
|
|
||||||
if (!mCarryOver.isEmpty() && removeWt == 0) {
|
if (!mCarryOver.isEmpty() && removeWt == 0) {
|
||||||
// No new items were removed in this step. Try placing all the items on this screen.
|
// No new items were removed in this step. Try placing all the items on this screen.
|
||||||
boolean[][] occupied = new boolean[mTrgX][mTrgY];
|
GridOccupancy occupied = new GridOccupancy(mTrgX, mTrgY);
|
||||||
for (DbEntry item : finalItems) {
|
for (DbEntry item : finalItems) {
|
||||||
markCells(occupied, item, true);
|
occupied.markCells(item, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
OptimalPlacementSolution placement = new OptimalPlacementSolution(occupied,
|
OptimalPlacementSolution placement = new OptimalPlacementSolution(occupied,
|
||||||
@@ -376,7 +377,7 @@ public class GridSizeMigrationTask {
|
|||||||
*/
|
*/
|
||||||
private ArrayList<DbEntry> tryRemove(int col, int row, ArrayList<DbEntry> items,
|
private ArrayList<DbEntry> tryRemove(int col, int row, ArrayList<DbEntry> items,
|
||||||
float[] outLoss) {
|
float[] outLoss) {
|
||||||
boolean[][] occupied = new boolean[mTrgX][mTrgY];
|
GridOccupancy occupied = new GridOccupancy(mTrgX, mTrgY);
|
||||||
|
|
||||||
col = mShouldRemoveX ? col : Integer.MAX_VALUE;
|
col = mShouldRemoveX ? col : Integer.MAX_VALUE;
|
||||||
row = mShouldRemoveY ? row : Integer.MAX_VALUE;
|
row = mShouldRemoveY ? row : Integer.MAX_VALUE;
|
||||||
@@ -394,7 +395,7 @@ public class GridSizeMigrationTask {
|
|||||||
if (item.cellX > col) item.cellX --;
|
if (item.cellX > col) item.cellX --;
|
||||||
if (item.cellY > row) item.cellY --;
|
if (item.cellY > row) item.cellY --;
|
||||||
finalItems.add(item);
|
finalItems.add(item);
|
||||||
markCells(occupied, item, true);
|
occupied.markCells(item, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,31 +407,9 @@ public class GridSizeMigrationTask {
|
|||||||
return finalItems;
|
return finalItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void markCells(boolean[][] occupied, DbEntry item, boolean val) {
|
|
||||||
for (int i = item.cellX; i < (item.cellX + item.spanX); i++) {
|
|
||||||
for (int j = item.cellY; j < (item.cellY + item.spanY); j++) {
|
|
||||||
occupied[i][j] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isVacant(boolean[][] occupied, int x, int y, int w, int h) {
|
|
||||||
if (x + w > mTrgX) return false;
|
|
||||||
if (y + h > mTrgY) return false;
|
|
||||||
|
|
||||||
for (int i = 0; i < w; i++) {
|
|
||||||
for (int j = 0; j < h; j++) {
|
|
||||||
if (occupied[i + x][j + y]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class OptimalPlacementSolution {
|
private class OptimalPlacementSolution {
|
||||||
private final ArrayList<DbEntry> itemsToPlace;
|
private final ArrayList<DbEntry> itemsToPlace;
|
||||||
private final boolean[][] occupied;
|
private final GridOccupancy occupied;
|
||||||
|
|
||||||
// If set to true, item movement are not considered in move cost, leading to a more
|
// If set to true, item movement are not considered in move cost, leading to a more
|
||||||
// linear placement.
|
// linear placement.
|
||||||
@@ -440,11 +419,11 @@ public class GridSizeMigrationTask {
|
|||||||
float lowestMoveCost = Float.MAX_VALUE;
|
float lowestMoveCost = Float.MAX_VALUE;
|
||||||
ArrayList<DbEntry> finalPlacedItems;
|
ArrayList<DbEntry> finalPlacedItems;
|
||||||
|
|
||||||
public OptimalPlacementSolution(boolean[][] occupied, ArrayList<DbEntry> itemsToPlace) {
|
public OptimalPlacementSolution(GridOccupancy occupied, ArrayList<DbEntry> itemsToPlace) {
|
||||||
this(occupied, itemsToPlace, false);
|
this(occupied, itemsToPlace, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public OptimalPlacementSolution(boolean[][] occupied, ArrayList<DbEntry> itemsToPlace,
|
public OptimalPlacementSolution(GridOccupancy occupied, ArrayList<DbEntry> itemsToPlace,
|
||||||
boolean ignoreMove) {
|
boolean ignoreMove) {
|
||||||
this.occupied = occupied;
|
this.occupied = occupied;
|
||||||
this.itemsToPlace = itemsToPlace;
|
this.itemsToPlace = itemsToPlace;
|
||||||
@@ -513,42 +492,42 @@ public class GridSizeMigrationTask {
|
|||||||
newMoveCost = moveCost;
|
newMoveCost = moveCost;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isVacant(occupied, x, y, myW, myH)) {
|
if (occupied.isRegionVacant(x, y, myW, myH)) {
|
||||||
// place at this position and continue search.
|
// place at this position and continue search.
|
||||||
markCells(occupied, me, true);
|
occupied.markCells(me, true);
|
||||||
find(index + 1, weightLoss, newMoveCost, itemsIncludingMe);
|
find(index + 1, weightLoss, newMoveCost, itemsIncludingMe);
|
||||||
markCells(occupied, me, false);
|
occupied.markCells(me, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try resizing horizontally
|
// Try resizing horizontally
|
||||||
if (myW > me.minSpanX && isVacant(occupied, x, y, myW - 1, myH)) {
|
if (myW > me.minSpanX && occupied.isRegionVacant(x, y, myW - 1, myH)) {
|
||||||
me.spanX --;
|
me.spanX --;
|
||||||
markCells(occupied, me, true);
|
occupied.markCells(me, true);
|
||||||
// 1 extra move cost
|
// 1 extra move cost
|
||||||
find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe);
|
find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe);
|
||||||
markCells(occupied, me, false);
|
occupied.markCells(me, false);
|
||||||
me.spanX ++;
|
me.spanX ++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try resizing vertically
|
// Try resizing vertically
|
||||||
if (myH > me.minSpanY && isVacant(occupied, x, y, myW, myH - 1)) {
|
if (myH > me.minSpanY && occupied.isRegionVacant(x, y, myW, myH - 1)) {
|
||||||
me.spanY --;
|
me.spanY --;
|
||||||
markCells(occupied, me, true);
|
occupied.markCells(me, true);
|
||||||
// 1 extra move cost
|
// 1 extra move cost
|
||||||
find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe);
|
find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe);
|
||||||
markCells(occupied, me, false);
|
occupied.markCells(me, false);
|
||||||
me.spanY ++;
|
me.spanY ++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try resizing horizontally & vertically
|
// Try resizing horizontally & vertically
|
||||||
if (myH > me.minSpanY && myW > me.minSpanX &&
|
if (myH > me.minSpanY && myW > me.minSpanX &&
|
||||||
isVacant(occupied, x, y, myW - 1, myH - 1)) {
|
occupied.isRegionVacant(x, y, myW - 1, myH - 1)) {
|
||||||
me.spanX --;
|
me.spanX --;
|
||||||
me.spanY --;
|
me.spanY --;
|
||||||
markCells(occupied, me, true);
|
occupied.markCells(me, true);
|
||||||
// 2 extra move cost
|
// 2 extra move cost
|
||||||
find(index + 1, weightLoss, newMoveCost + 2, itemsIncludingMe);
|
find(index + 1, weightLoss, newMoveCost + 2, itemsIncludingMe);
|
||||||
markCells(occupied, me, false);
|
occupied.markCells(me, false);
|
||||||
me.spanX ++;
|
me.spanX ++;
|
||||||
me.spanY ++;
|
me.spanY ++;
|
||||||
}
|
}
|
||||||
@@ -570,7 +549,7 @@ public class GridSizeMigrationTask {
|
|||||||
|
|
||||||
for (int y = 0; y < mTrgY; y++) {
|
for (int y = 0; y < mTrgY; y++) {
|
||||||
for (int x = 0; x < mTrgX; x++) {
|
for (int x = 0; x < mTrgX; x++) {
|
||||||
if (!occupied[x][y]) {
|
if (!occupied.cells[x][y]) {
|
||||||
int dist = ignoreMove ? 0 :
|
int dist = ignoreMove ? 0 :
|
||||||
((me.cellX - x) * (me.cellX - x) + (me.cellY - y) * (me.cellY - y));
|
((me.cellX - x) * (me.cellX - x) + (me.cellY - y) * (me.cellY - y));
|
||||||
if (dist < newDistance) {
|
if (dist < newDistance) {
|
||||||
@@ -595,9 +574,9 @@ public class GridSizeMigrationTask {
|
|||||||
if (ignoreMove) {
|
if (ignoreMove) {
|
||||||
newMoveCost = moveCost;
|
newMoveCost = moveCost;
|
||||||
}
|
}
|
||||||
markCells(occupied, me, true);
|
occupied.markCells(me, true);
|
||||||
find(index + 1, weightLoss, newMoveCost, itemsIncludingMe);
|
find(index + 1, weightLoss, newMoveCost, itemsIncludingMe);
|
||||||
markCells(occupied, me, false);
|
occupied.markCells(me, false);
|
||||||
me.cellX = myX;
|
me.cellX = myX;
|
||||||
me.cellY = myY;
|
me.cellY = myY;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.android.launcher3.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class which represents an area on the grid.
|
||||||
|
*/
|
||||||
|
public class CellAndSpan {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates the X position of the associated cell.
|
||||||
|
*/
|
||||||
|
public int cellX = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates the Y position of the associated cell.
|
||||||
|
*/
|
||||||
|
public int cellY = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates the X cell span.
|
||||||
|
*/
|
||||||
|
public int spanX = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates the Y cell span.
|
||||||
|
*/
|
||||||
|
public int spanY = 1;
|
||||||
|
|
||||||
|
public CellAndSpan() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void copyFrom(CellAndSpan copy) {
|
||||||
|
cellX = copy.cellX;
|
||||||
|
cellY = copy.cellY;
|
||||||
|
spanX = copy.spanX;
|
||||||
|
spanY = copy.spanY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CellAndSpan(int cellX, int cellY, int spanX, int spanY) {
|
||||||
|
this.cellX = cellX;
|
||||||
|
this.cellY = cellY;
|
||||||
|
this.spanX = spanX;
|
||||||
|
this.spanY = spanY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "(" + cellX + ", " + cellY + ": " + spanX + ", " + spanY + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
package com.android.launcher3.util;
|
||||||
|
|
||||||
|
import android.graphics.Rect;
|
||||||
|
|
||||||
|
import com.android.launcher3.ItemInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility object to manage the occupancy in a grid.
|
||||||
|
*/
|
||||||
|
public class GridOccupancy {
|
||||||
|
|
||||||
|
private final int mCountX;
|
||||||
|
private final int mCountY;
|
||||||
|
|
||||||
|
public final boolean[][] cells;
|
||||||
|
|
||||||
|
public GridOccupancy(int countX, int countY) {
|
||||||
|
mCountX = countX;
|
||||||
|
mCountY = countY;
|
||||||
|
cells = new boolean[countX][countY];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the first vacant cell, if there is one.
|
||||||
|
*
|
||||||
|
* @param vacantOut Holds the x and y coordinate of the vacant cell
|
||||||
|
* @param spanX Horizontal cell span.
|
||||||
|
* @param spanY Vertical cell span.
|
||||||
|
*
|
||||||
|
* @return true if a vacant cell was found
|
||||||
|
*/
|
||||||
|
public boolean findVacantCell(int[] vacantOut, int spanX, int spanY) {
|
||||||
|
for (int y = 0; (y + spanY) <= mCountY; y++) {
|
||||||
|
for (int x = 0; (x + spanX) <= mCountX; x++) {
|
||||||
|
boolean available = !cells[x][y];
|
||||||
|
out:
|
||||||
|
for (int i = x; i < x + spanX; i++) {
|
||||||
|
for (int j = y; j < y + spanY; j++) {
|
||||||
|
available = available && !cells[i][j];
|
||||||
|
if (!available) break out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (available) {
|
||||||
|
vacantOut[0] = x;
|
||||||
|
vacantOut[1] = y;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void copyTo(GridOccupancy dest) {
|
||||||
|
for (int i = 0; i < mCountX; i++) {
|
||||||
|
for (int j = 0; j < mCountY; j++) {
|
||||||
|
dest.cells[i][j] = cells[i][j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRegionVacant(int x, int y, int spanX, int spanY) {
|
||||||
|
int x2 = x + spanX - 1;
|
||||||
|
int y2 = y + spanY - 1;
|
||||||
|
if (x < 0 || y < 0 || x2 >= mCountX || y2 >= mCountY) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = x; i <= x2; i++) {
|
||||||
|
for (int j = y; j <= y2; j++) {
|
||||||
|
if (cells[i][j]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void markCells(int cellX, int cellY, int spanX, int spanY, boolean value) {
|
||||||
|
if (cellX < 0 || cellY < 0) return;
|
||||||
|
for (int x = cellX; x < cellX + spanX && x < mCountX; x++) {
|
||||||
|
for (int y = cellY; y < cellY + spanY && y < mCountX; y++) {
|
||||||
|
cells[x][y] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void markCells(Rect r, boolean value) {
|
||||||
|
markCells(r.left, r.top, r.width(), r.height(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void markCells(CellAndSpan cell, boolean value) {
|
||||||
|
markCells(cell.cellX, cell.cellY, cell.spanX, cell.spanY, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void markCells(ItemInfo item, boolean value) {
|
||||||
|
markCells(item.cellX, item.cellY, item.spanX, item.spanY, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
markCells(0, 0, mCountX, mCountY, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.android.launcher3;
|
package com.android.launcher3.util;
|
||||||
|
|
||||||
import android.test.AndroidTestCase;
|
import android.test.AndroidTestCase;
|
||||||
import android.test.suitebuilder.annotation.SmallTest;
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package com.android.launcher3.util;
|
||||||
|
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for {@link GridOccupancy}
|
||||||
|
*/
|
||||||
|
@SmallTest
|
||||||
|
public class GridOccupancyTest extends TestCase {
|
||||||
|
|
||||||
|
public void testFindVacantCell() {
|
||||||
|
GridOccupancy grid = initGrid(4,
|
||||||
|
1, 1, 1, 0, 0,
|
||||||
|
0, 0, 1, 1, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
1, 1, 0, 0, 0
|
||||||
|
);
|
||||||
|
|
||||||
|
int[] vacant = new int[2];
|
||||||
|
assertTrue(grid.findVacantCell(vacant, 2, 2));
|
||||||
|
assertEquals(vacant[0], 0);
|
||||||
|
assertEquals(vacant[1], 1);
|
||||||
|
|
||||||
|
assertTrue(grid.findVacantCell(vacant, 3, 2));
|
||||||
|
assertEquals(vacant[0], 2);
|
||||||
|
assertEquals(vacant[1], 2);
|
||||||
|
|
||||||
|
assertFalse(grid.findVacantCell(vacant, 3, 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIsRegionVacant() {
|
||||||
|
GridOccupancy grid = initGrid(4,
|
||||||
|
1, 1, 1, 0, 0,
|
||||||
|
0, 0, 1, 1, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
1, 1, 0, 0, 0
|
||||||
|
);
|
||||||
|
|
||||||
|
assertTrue(grid.isRegionVacant(4, 0, 1, 4));
|
||||||
|
assertTrue(grid.isRegionVacant(0, 1, 2, 2));
|
||||||
|
assertTrue(grid.isRegionVacant(2, 2, 3, 2));
|
||||||
|
|
||||||
|
assertFalse(grid.isRegionVacant(3, 0, 2, 4));
|
||||||
|
assertFalse(grid.isRegionVacant(0, 0, 2, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
private GridOccupancy initGrid(int rows, int... cells) {
|
||||||
|
int cols = cells.length / rows;
|
||||||
|
int i = 0;
|
||||||
|
GridOccupancy grid = new GridOccupancy(cols, rows);
|
||||||
|
for (int y = 0; y < rows; y++) {
|
||||||
|
for (int x = 0; x < cols; x++) {
|
||||||
|
grid.cells[x][y] = cells[i] != 0;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return grid;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user