Moving classes inside of CellLayout to their own file
This is a no-op change ensure this we have ReorderAlgorithmUnitTest.
Flag: NA
Bug: 229292911
Test: ReorderAlgorithmUnitTest
Change-Id: I6ffe2a1260f869a4686a9f1e652dd1ab6d406269
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index 2064fe2..110ca16 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -44,13 +44,13 @@
import androidx.core.graphics.ColorUtils;
-import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.celllayout.CellLayoutLayoutParams;
+import com.android.launcher3.celllayout.DelegatedCellDrawing;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.icons.IconNormalizer;
@@ -418,7 +418,7 @@
/**
* Draws Predicted Icon outline on cell layout
*/
- public static class PredictedIconOutlineDrawing extends CellLayout.DelegatedCellDrawing {
+ public static class PredictedIconOutlineDrawing extends DelegatedCellDrawing {
private final PredictedAppIcon mIcon;
private final Paint mOutlinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 1a0f2cf..7257d86 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -67,7 +67,10 @@
import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.celllayout.CellPosMapper.CellPos;
+import com.android.launcher3.celllayout.DelegatedCellDrawing;
+import com.android.launcher3.celllayout.ItemConfiguration;
import com.android.launcher3.celllayout.ReorderAlgorithm;
+import com.android.launcher3.celllayout.ViewCluster;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.folder.PreviewBackground;
@@ -86,7 +89,6 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Stack;
@@ -1434,9 +1436,8 @@
View child = mShortcutsAndWidgets.getChildAt(i);
if (child == dragView) continue;
CellAndSpan c = solution.map.get(child);
- boolean skip = mode == ReorderPreviewAnimation.MODE_HINT && solution.intersectingViews
- != null && !solution.intersectingViews.contains(child);
-
+ boolean skip = mode == ReorderPreviewAnimation.MODE_HINT
+ && !solution.intersectingViews.contains(child);
CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
if (c != null && !skip && (child instanceof Reorderable)) {
@@ -1832,7 +1833,7 @@
private boolean pushViewsToTempLocation(ArrayList<View> views, Rect rectOccupiedByPotentialDrop,
int[] direction, View dragView, ItemConfiguration currentState) {
- ViewCluster cluster = new ViewCluster(views, currentState);
+ ViewCluster cluster = new ViewCluster(this, views, currentState);
Rect clusterRect = cluster.getBoundingRect();
int whichEdge;
int pushDistance;
@@ -1924,191 +1925,6 @@
return foundSolution;
}
- /**
- * This helper class defines a cluster of views. It helps with defining complex edges
- * of the cluster and determining how those edges interact with other views. The edges
- * essentially define a fine-grained boundary around the cluster of views -- like a more
- * precise version of a bounding box.
- */
- private class ViewCluster {
- final static int LEFT = 1 << 0;
- final static int TOP = 1 << 1;
- final static int RIGHT = 1 << 2;
- final static int BOTTOM = 1 << 3;
-
- final ArrayList<View> views;
- final ItemConfiguration config;
- final Rect boundingRect = new Rect();
-
- final int[] leftEdge = new int[mCountY];
- final int[] rightEdge = new int[mCountY];
- final int[] topEdge = new int[mCountX];
- final int[] bottomEdge = new int[mCountX];
- int dirtyEdges;
- boolean boundingRectDirty;
-
- @SuppressWarnings("unchecked")
- public ViewCluster(ArrayList<View> views, ItemConfiguration config) {
- this.views = (ArrayList<View>) views.clone();
- this.config = config;
- resetEdges();
- }
-
- void resetEdges() {
- for (int i = 0; i < mCountX; i++) {
- topEdge[i] = -1;
- bottomEdge[i] = -1;
- }
- for (int i = 0; i < mCountY; i++) {
- leftEdge[i] = -1;
- rightEdge[i] = -1;
- }
- dirtyEdges = LEFT | TOP | RIGHT | BOTTOM;
- boundingRectDirty = true;
- }
-
- void computeEdge(int which) {
- int count = views.size();
- for (int i = 0; i < count; i++) {
- CellAndSpan cs = config.map.get(views.get(i));
- switch (which) {
- case LEFT:
- int left = cs.cellX;
- for (int j = cs.cellY; j < cs.cellY + cs.spanY; j++) {
- if (left < leftEdge[j] || leftEdge[j] < 0) {
- leftEdge[j] = left;
- }
- }
- break;
- case RIGHT:
- int right = cs.cellX + cs.spanX;
- for (int j = cs.cellY; j < cs.cellY + cs.spanY; j++) {
- if (right > rightEdge[j]) {
- rightEdge[j] = right;
- }
- }
- break;
- case TOP:
- int top = cs.cellY;
- for (int j = cs.cellX; j < cs.cellX + cs.spanX; j++) {
- if (top < topEdge[j] || topEdge[j] < 0) {
- topEdge[j] = top;
- }
- }
- break;
- case BOTTOM:
- int bottom = cs.cellY + cs.spanY;
- for (int j = cs.cellX; j < cs.cellX + cs.spanX; j++) {
- if (bottom > bottomEdge[j]) {
- bottomEdge[j] = bottom;
- }
- }
- break;
- }
- }
- }
-
- boolean isViewTouchingEdge(View v, int whichEdge) {
- CellAndSpan cs = config.map.get(v);
-
- if ((dirtyEdges & whichEdge) == whichEdge) {
- computeEdge(whichEdge);
- dirtyEdges &= ~whichEdge;
- }
-
- switch (whichEdge) {
- case LEFT:
- for (int i = cs.cellY; i < cs.cellY + cs.spanY; i++) {
- if (leftEdge[i] == cs.cellX + cs.spanX) {
- return true;
- }
- }
- break;
- case RIGHT:
- for (int i = cs.cellY; i < cs.cellY + cs.spanY; i++) {
- if (rightEdge[i] == cs.cellX) {
- return true;
- }
- }
- break;
- case TOP:
- for (int i = cs.cellX; i < cs.cellX + cs.spanX; i++) {
- if (topEdge[i] == cs.cellY + cs.spanY) {
- return true;
- }
- }
- break;
- case BOTTOM:
- for (int i = cs.cellX; i < cs.cellX + cs.spanX; i++) {
- if (bottomEdge[i] == cs.cellY) {
- return true;
- }
- }
- break;
- }
- return false;
- }
-
- void shift(int whichEdge, int delta) {
- for (View v: views) {
- CellAndSpan c = config.map.get(v);
- switch (whichEdge) {
- case LEFT:
- c.cellX -= delta;
- break;
- case RIGHT:
- c.cellX += delta;
- break;
- case TOP:
- c.cellY -= delta;
- break;
- case BOTTOM:
- default:
- c.cellY += delta;
- break;
- }
- }
- resetEdges();
- }
-
- public void addView(View v) {
- views.add(v);
- resetEdges();
- }
-
- public Rect getBoundingRect() {
- if (boundingRectDirty) {
- config.getBoundingRectForViews(views, boundingRect);
- }
- return boundingRect;
- }
-
- final PositionComparator comparator = new PositionComparator();
- class PositionComparator implements Comparator<View> {
- int whichEdge = 0;
- public int compare(View left, View right) {
- CellAndSpan l = config.map.get(left);
- CellAndSpan r = config.map.get(right);
- switch (whichEdge) {
- case LEFT:
- return (r.cellX + r.spanX) - (l.cellX + l.spanX);
- case RIGHT:
- return l.cellX - r.cellX;
- case TOP:
- return (r.cellY + r.spanY) - (l.cellY + l.spanY);
- case BOTTOM:
- default:
- return l.cellY - r.cellY;
- }
- }
- }
-
- public void sortConfigurationForEdgePush(int edge) {
- comparator.whichEdge = edge;
- Collections.sort(config.sortedViews, comparator);
- }
- }
-
// 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
// passed.
@@ -2532,54 +2348,6 @@
}
/**
- * Represents the solution to a reorder of items in the Workspace.
- */
- public static class ItemConfiguration extends CellAndSpan {
- public final ArrayMap<View, CellAndSpan> map = new ArrayMap<>();
- private final ArrayMap<View, CellAndSpan> savedMap = new ArrayMap<>();
- public final ArrayList<View> sortedViews = new ArrayList<>();
- public ArrayList<View> intersectingViews;
- public boolean isSolution = false;
-
- public void save() {
- // Copy current state into savedMap
- for (View v: map.keySet()) {
- savedMap.get(v).copyFrom(map.get(v));
- }
- }
-
- public void restore() {
- // Restore current state from savedMap
- for (View v: savedMap.keySet()) {
- map.get(v).copyFrom(savedMap.get(v));
- }
- }
-
- public void add(View v, CellAndSpan cs) {
- map.put(v, cs);
- savedMap.put(v, new CellAndSpan());
- sortedViews.add(v);
- }
-
- public int area() {
- return spanX * spanY;
- }
-
- public void getBoundingRectForViews(ArrayList<View> views, Rect outRect) {
- boolean first = true;
- for (View v: views) {
- CellAndSpan c = map.get(v);
- 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);
- }
- }
- }
- }
-
- /**
* Find a starting cell position that will fit the given bounds nearest the requested
* cell location. Uses Euclidean distance to score multiple vacant areas.
*
@@ -2758,52 +2526,6 @@
return new CellLayoutLayoutParams(p);
}
- // 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
- public static final class CellInfo extends CellAndSpan {
- public final View cell;
- final int screenId;
- final int container;
-
- public CellInfo(View v, ItemInfo info, CellPos cellPos) {
- cellX = cellPos.cellX;
- cellY = cellPos.cellY;
- spanX = info.spanX;
- spanY = info.spanY;
- cell = v;
- screenId = cellPos.screenId;
- container = info.container;
- }
-
- @Override
- public String toString() {
- return "Cell[view=" + (cell == null ? "null" : cell.getClass())
- + ", x=" + cellX + ", y=" + cellY + "]";
- }
- }
-
- /**
- * A Delegated cell Drawing for drawing on CellLayout
- */
- public abstract static class DelegatedCellDrawing {
- public int mDelegateCellX;
- public int mDelegateCellY;
-
- /**
- * Draw under CellLayout
- */
- public abstract void drawUnderItem(Canvas canvas);
-
- /**
- * Draw over CellLayout
- */
- public abstract void drawOverItem(Canvas canvas);
- }
-
/**
* Returns whether an item can be placed in this CellLayout (after rearranging and/or resizing
* if necessary).
diff --git a/src/com/android/launcher3/MultipageCellLayout.java b/src/com/android/launcher3/MultipageCellLayout.java
index 4b5c9ef..123e8ca 100644
--- a/src/com/android/launcher3/MultipageCellLayout.java
+++ b/src/com/android/launcher3/MultipageCellLayout.java
@@ -23,6 +23,7 @@
import android.view.View;
import com.android.launcher3.celllayout.CellLayoutLayoutParams;
+import com.android.launcher3.celllayout.ItemConfiguration;
import com.android.launcher3.celllayout.MulticellReorderAlgorithm;
import com.android.launcher3.util.CellAndSpan;
import com.android.launcher3.util.GridOccupancy;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 30f3f5f..be4168d 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -74,6 +74,7 @@
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.apppairs.AppPairIcon;
+import com.android.launcher3.celllayout.CellInfo;
import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.celllayout.CellPosMapper;
import com.android.launcher3.celllayout.CellPosMapper.CellPos;
@@ -189,7 +190,7 @@
/**
* CellInfo for the cell that is currently being dragged
*/
- protected CellLayout.CellInfo mDragInfo;
+ protected CellInfo mDragInfo;
/**
* Target drop area calculated during last acceptDrop call.
@@ -1620,7 +1621,7 @@
page.setAccessibilityDelegate(null);
}
- public void startDrag(CellLayout.CellInfo cellInfo, DragOptions options) {
+ public void startDrag(CellInfo cellInfo, DragOptions options) {
View child = cellInfo.cell;
mDragInfo = cellInfo;
@@ -1784,7 +1785,7 @@
int spanX;
int spanY;
if (mDragInfo != null) {
- final CellLayout.CellInfo dragCellInfo = mDragInfo;
+ final CellInfo dragCellInfo = mDragInfo;
spanX = dragCellInfo.spanX;
spanY = dragCellInfo.spanY;
} else {
@@ -3078,7 +3079,7 @@
* so that Launcher can sync this object with the correct info when the activity is created/
* destroyed
*/
- public CellLayout.CellInfo getDragInfo() {
+ public CellInfo getDragInfo() {
return mDragInfo;
}
diff --git a/src/com/android/launcher3/celllayout/CellInfo.kt b/src/com/android/launcher3/celllayout/CellInfo.kt
new file mode 100644
index 0000000..5a3b7f7
--- /dev/null
+++ b/src/com/android/launcher3/celllayout/CellInfo.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 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.launcher3.celllayout
+
+import android.view.View
+import com.android.launcher3.celllayout.CellPosMapper.CellPos
+import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.util.CellAndSpan
+
+// 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
+class CellInfo(v: View?, info: ItemInfo, cellPos: CellPos) :
+ CellAndSpan(cellPos.cellX, cellPos.cellY, info.spanX, info.spanY) {
+ @JvmField val cell: View?
+ @JvmField val screenId: Int
+ @JvmField val container: Int
+
+ init {
+ cell = v
+ screenId = cellPos.screenId
+ container = info.container
+ }
+
+ override fun toString(): String {
+ return "CellInfo(cell=$cell, screenId=$screenId, container=$container)"
+ }
+}
diff --git a/src/com/android/launcher3/celllayout/DelegatedCellDrawing.kt b/src/com/android/launcher3/celllayout/DelegatedCellDrawing.kt
new file mode 100644
index 0000000..1703f9b
--- /dev/null
+++ b/src/com/android/launcher3/celllayout/DelegatedCellDrawing.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 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.launcher3.celllayout
+
+import android.graphics.Canvas
+
+/** A Delegated cell Drawing for drawing on CellLayout */
+abstract class DelegatedCellDrawing {
+ @JvmField var mDelegateCellX = 0
+ @JvmField var mDelegateCellY = 0
+
+ /** Draw under CellLayout */
+ abstract fun drawUnderItem(canvas: Canvas)
+
+ /** Draw over CellLayout */
+ abstract fun drawOverItem(canvas: Canvas)
+}
diff --git a/src/com/android/launcher3/celllayout/ItemConfiguration.kt b/src/com/android/launcher3/celllayout/ItemConfiguration.kt
new file mode 100644
index 0000000..e775145
--- /dev/null
+++ b/src/com/android/launcher3/celllayout/ItemConfiguration.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2023 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.launcher3.celllayout
+
+import android.graphics.Rect
+import android.util.ArrayMap
+import android.view.View
+import com.android.launcher3.util.CellAndSpan
+
+/** Represents the solution to a reorder of items in the Workspace. */
+class ItemConfiguration : CellAndSpan() {
+ @JvmField val map = ArrayMap<View, CellAndSpan>()
+ private val savedMap = ArrayMap<View, CellAndSpan>()
+
+ @JvmField val sortedViews = ArrayList<View>()
+
+ @JvmField var intersectingViews: ArrayList<View> = ArrayList()
+
+ @JvmField var isSolution = false
+ fun save() {
+ // Copy current state into savedMap
+ map.forEach { (k, v) -> savedMap[k]?.copyFrom(v) }
+ }
+
+ fun restore() {
+ // Restore current state from savedMap
+ savedMap.forEach { (k, v) -> map[k]?.copyFrom(v) }
+ }
+
+ fun add(v: View, cs: CellAndSpan) {
+ map[v] = cs
+ savedMap[v] = CellAndSpan()
+ sortedViews.add(v)
+ }
+
+ fun area(): Int {
+ return spanX * spanY
+ }
+
+ fun getBoundingRectForViews(views: ArrayList<View>, outRect: Rect) {
+ views
+ .mapNotNull { v -> map[v] }
+ .forEachIndexed { i, c ->
+ if (i == 0) outRect.set(c.cellX, c.cellY, c.cellX + c.spanX, c.cellY + c.spanY)
+ else outRect.union(c.cellX, c.cellY, c.cellX + c.spanX, c.cellY + c.spanY)
+ }
+ }
+}
diff --git a/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java b/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java
index a2e26b3..b7d8093 100644
--- a/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java
+++ b/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java
@@ -38,8 +38,8 @@
mSeam = new View(cellLayout.getContext());
}
- private CellLayout.ItemConfiguration removeSeamFromSolution(
- CellLayout.ItemConfiguration solution) {
+ private ItemConfiguration removeSeamFromSolution(
+ ItemConfiguration solution) {
solution.map.forEach((view, cell) -> cell.cellX =
cell.cellX > mCellLayout.getCountX() / 2 ? cell.cellX - 1 : cell.cellX);
solution.cellX =
@@ -48,7 +48,7 @@
}
@Override
- public CellLayout.ItemConfiguration closestEmptySpaceReorder(int pixelX, int pixelY,
+ public ItemConfiguration closestEmptySpaceReorder(int pixelX, int pixelY,
int minSpanX, int minSpanY,
int spanX, int spanY) {
return removeSeamFromSolution(simulateSeam(
@@ -57,16 +57,16 @@
}
@Override
- public CellLayout.ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX,
+ public ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX,
int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX,
- CellLayout.ItemConfiguration solution) {
+ ItemConfiguration solution) {
return removeSeamFromSolution(simulateSeam(
() -> super.findReorderSolution(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY,
direction, dragView, decX, solution)));
}
@Override
- public CellLayout.ItemConfiguration dropInPlaceSolution(int pixelX, int pixelY, int spanX,
+ public ItemConfiguration dropInPlaceSolution(int pixelX, int pixelY, int spanX,
int spanY,
View dragView) {
return removeSeamFromSolution(simulateSeam(
diff --git a/src/com/android/launcher3/celllayout/ReorderAlgorithm.java b/src/com/android/launcher3/celllayout/ReorderAlgorithm.java
index 17786f2..05bd13d 100644
--- a/src/com/android/launcher3/celllayout/ReorderAlgorithm.java
+++ b/src/com/android/launcher3/celllayout/ReorderAlgorithm.java
@@ -59,9 +59,9 @@
* @param solution variable to store the solution
* @return the same solution variable
*/
- public CellLayout.ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX,
+ public ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX,
int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX,
- CellLayout.ItemConfiguration solution) {
+ ItemConfiguration solution) {
// Copy the current state into the solution. This solution will be manipulated as necessary.
mCellLayout.copyCurrentStateToSolution(solution, false);
// Copy the current occupied array into the temporary occupied array. This array will be
@@ -110,11 +110,11 @@
* @param dragView view being dragged in reorder
* @return the configuration that represents the found reorder
*/
- public CellLayout.ItemConfiguration dropInPlaceSolution(int pixelX, int pixelY, int spanX,
+ public ItemConfiguration dropInPlaceSolution(int pixelX, int pixelY, int spanX,
int spanY, View dragView) {
int[] result = mCellLayout.findNearestAreaIgnoreOccupied(pixelX, pixelY, spanX, spanY,
new int[2]);
- CellLayout.ItemConfiguration solution = new CellLayout.ItemConfiguration();
+ ItemConfiguration solution = new ItemConfiguration();
mCellLayout.copyCurrentStateToSolution(solution, false);
solution.isSolution = !isConfigurationRegionOccupied(
@@ -133,7 +133,7 @@
}
private boolean isConfigurationRegionOccupied(Rect region,
- CellLayout.ItemConfiguration configuration, View ignoreView) {
+ ItemConfiguration configuration, View ignoreView) {
return configuration.map.entrySet()
.stream()
.filter(entry -> entry.getKey() != ignoreView)
@@ -153,9 +153,9 @@
* @param spanY vertical cell span
* @return the configuration that represents the found reorder
*/
- public CellLayout.ItemConfiguration closestEmptySpaceReorder(int pixelX, int pixelY,
+ public ItemConfiguration closestEmptySpaceReorder(int pixelX, int pixelY,
int minSpanX, int minSpanY, int spanX, int spanY) {
- CellLayout.ItemConfiguration solution = new CellLayout.ItemConfiguration();
+ ItemConfiguration solution = new ItemConfiguration();
int[] result = new int[2];
int[] resultSpan = new int[2];
mCellLayout.findNearestVacantArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, result,
@@ -188,22 +188,22 @@
* @return returns a solution for the given parameters, the solution contains all the icons and
* the locations they should be in the given solution.
*/
- public CellLayout.ItemConfiguration calculateReorder(int pixelX, int pixelY, int minSpanX,
+ public ItemConfiguration calculateReorder(int pixelX, int pixelY, int minSpanX,
int minSpanY, int spanX, int spanY, View dragView) {
mCellLayout.getDirectionVectorForDrop(pixelX, pixelY, spanX, spanY, dragView,
mCellLayout.mDirectionVector);
- CellLayout.ItemConfiguration dropInPlaceSolution = dropInPlaceSolution(pixelX, pixelY,
+ ItemConfiguration dropInPlaceSolution = dropInPlaceSolution(pixelX, pixelY,
spanX, spanY,
dragView);
// Find a solution involving pushing / displacing any items in the way
- CellLayout.ItemConfiguration swapSolution = findReorderSolution(pixelX, pixelY, minSpanX,
+ ItemConfiguration swapSolution = findReorderSolution(pixelX, pixelY, minSpanX,
minSpanY, spanX, spanY, mCellLayout.mDirectionVector, dragView, true,
- new CellLayout.ItemConfiguration());
+ new ItemConfiguration());
// We attempt the approach which doesn't shuffle views at all
- CellLayout.ItemConfiguration closestSpaceSolution = closestEmptySpaceReorder(
+ ItemConfiguration closestSpaceSolution = closestEmptySpaceReorder(
pixelX, pixelY, minSpanX, minSpanY, spanX, spanY);
// If the reorder solution requires resizing (shrinking) the item being dropped, we instead
diff --git a/src/com/android/launcher3/celllayout/ViewCluster.kt b/src/com/android/launcher3/celllayout/ViewCluster.kt
new file mode 100644
index 0000000..49693e3
--- /dev/null
+++ b/src/com/android/launcher3/celllayout/ViewCluster.kt
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2023 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.launcher3.celllayout
+
+import android.graphics.Rect
+import android.view.View
+import com.android.launcher3.CellLayout
+import java.util.Collections
+
+/**
+ * This helper class defines a cluster of views. It helps with defining complex edges of the cluster
+ * and determining how those edges interact with other views. The edges essentially define a
+ * fine-grained boundary around the cluster of views -- like a more precise version of a bounding
+ * box.
+ */
+class ViewCluster(
+ private val mCellLayout: CellLayout,
+ views: ArrayList<View>,
+ val config: ItemConfiguration
+) {
+
+ @JvmField val views = ArrayList<View>(views)
+ private val boundingRect = Rect()
+
+ private val leftEdge = IntArray(mCellLayout.countY)
+ private val rightEdge = IntArray(mCellLayout.countY)
+ private val topEdge = IntArray(mCellLayout.countX)
+ private val bottomEdge = IntArray(mCellLayout.countX)
+
+ private var dirtyEdges = 0
+ private var boundingRectDirty = false
+
+ val comparator: PositionComparator = PositionComparator()
+
+ init {
+ resetEdges()
+ }
+ private fun resetEdges() {
+ for (i in 0 until mCellLayout.countX) {
+ topEdge[i] = -1
+ bottomEdge[i] = -1
+ }
+ for (i in 0 until mCellLayout.countY) {
+ leftEdge[i] = -1
+ rightEdge[i] = -1
+ }
+ dirtyEdges = LEFT or TOP or RIGHT or BOTTOM
+ boundingRectDirty = true
+ }
+
+ private fun computeEdge(which: Int) =
+ views
+ .mapNotNull { v -> config.map[v] }
+ .forEach { cs ->
+ val left = cs.cellX
+ val right = cs.cellX + cs.spanX
+ val top = cs.cellY
+ val bottom = cs.cellY + cs.spanY
+ when (which) {
+ LEFT ->
+ for (j in top until bottom) {
+ if (left < leftEdge[j] || leftEdge[j] < 0) {
+ leftEdge[j] = left
+ }
+ }
+ RIGHT ->
+ for (j in top until bottom) {
+ if (right > rightEdge[j]) {
+ rightEdge[j] = right
+ }
+ }
+ TOP ->
+ for (j in left until right) {
+ if (top < topEdge[j] || topEdge[j] < 0) {
+ topEdge[j] = top
+ }
+ }
+ BOTTOM ->
+ for (j in left until right) {
+ if (bottom > bottomEdge[j]) {
+ bottomEdge[j] = bottom
+ }
+ }
+ }
+ }
+
+ fun isViewTouchingEdge(v: View?, whichEdge: Int): Boolean {
+ val cs = config.map[v] ?: return false
+ val left = cs.cellX
+ val right = cs.cellX + cs.spanX
+ val top = cs.cellY
+ val bottom = cs.cellY + cs.spanY
+ if ((dirtyEdges and whichEdge) == whichEdge) {
+ computeEdge(whichEdge)
+ dirtyEdges = dirtyEdges and whichEdge.inv()
+ }
+ return when (whichEdge) {
+ // In this case if any of the values of leftEdge is equal to right, which is the
+ // rightmost x value of the view, it means that the cluster is touching the view from
+ // the left the same logic applies for the other sides.
+ LEFT -> edgeContainsValue(top, bottom, leftEdge, right)
+ RIGHT -> edgeContainsValue(top, bottom, rightEdge, left)
+ TOP -> edgeContainsValue(left, right, topEdge, bottom)
+ BOTTOM -> edgeContainsValue(left, right, bottomEdge, top)
+ else -> false
+ }
+ }
+
+ private fun edgeContainsValue(start: Int, end: Int, edge: IntArray, value: Int): Boolean {
+ for (i in start until end) {
+ if (edge[i] == value) {
+ return true
+ }
+ }
+ return false
+ }
+
+ fun shift(whichEdge: Int, delta: Int) {
+ views
+ .mapNotNull { v -> config.map[v] }
+ .forEach { c ->
+ when (whichEdge) {
+ LEFT -> c.cellX -= delta
+ RIGHT -> c.cellX += delta
+ TOP -> c.cellY -= delta
+ BOTTOM -> c.cellY += delta
+ else -> c.cellY += delta
+ }
+ }
+ resetEdges()
+ }
+
+ fun addView(v: View) {
+ views.add(v)
+ resetEdges()
+ }
+
+ fun getBoundingRect(): Rect {
+ if (boundingRectDirty) {
+ config.getBoundingRectForViews(views, boundingRect)
+ }
+ return boundingRect
+ }
+
+ inner class PositionComparator : Comparator<View?> {
+ var whichEdge = 0
+ override fun compare(left: View?, right: View?): Int {
+ val l = config.map[left]
+ val r = config.map[right]
+ if (l == null || r == null) throw NullPointerException()
+ return when (whichEdge) {
+ LEFT -> r.cellX + r.spanX - (l.cellX + l.spanX)
+ RIGHT -> l.cellX - r.cellX
+ TOP -> r.cellY + r.spanY - (l.cellY + l.spanY)
+ BOTTOM -> l.cellY - r.cellY
+ else -> l.cellY - r.cellY
+ }
+ }
+ }
+
+ fun sortConfigurationForEdgePush(edge: Int) {
+ comparator.whichEdge = edge
+ Collections.sort(config.sortedViews, comparator)
+ }
+
+ companion object {
+ const val LEFT = 1 shl 0
+ const val TOP = 1 shl 1
+ const val RIGHT = 1 shl 2
+ const val BOTTOM = 1 shl 3
+ }
+}
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index b320ceb..ec03803 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -48,6 +48,7 @@
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
+import com.android.launcher3.celllayout.DelegatedCellDrawing;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
@@ -55,7 +56,7 @@
* This object represents a FolderIcon preview background. It stores drawing / measurement
* information, handles drawing, and animation (accept state <--> rest state).
*/
-public class PreviewBackground extends CellLayout.DelegatedCellDrawing {
+public class PreviewBackground extends DelegatedCellDrawing {
private static final boolean DRAW_SHADOW = false;
private static final boolean DRAW_STROKE = false;
diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java
index 0c322cc..96cc412 100644
--- a/src/com/android/launcher3/touch/ItemLongClickListener.java
+++ b/src/com/android/launcher3/touch/ItemLongClickListener.java
@@ -27,9 +27,9 @@
import android.view.View;
import android.view.View.OnLongClickListener;
-import com.android.launcher3.CellLayout;
import com.android.launcher3.DropTarget;
import com.android.launcher3.Launcher;
+import com.android.launcher3.celllayout.CellInfo;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions;
@@ -86,7 +86,7 @@
}
}
- CellLayout.CellInfo longClickCellInfo = new CellLayout.CellInfo(v, info,
+ CellInfo longClickCellInfo = new CellInfo(v, info,
launcher.getCellPosMapper().mapModelToPresenter(info));
launcher.getWorkspace().startDrag(longClickCellInfo, dragOptions);
}
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java b/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
index 91a0634..15e2d70 100644
--- a/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
+++ b/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
@@ -105,7 +105,7 @@
};
}
- public CellLayout.ItemConfiguration solve(CellLayoutBoard board, int x, int y, int spanX,
+ public ItemConfiguration solve(CellLayoutBoard board, int x, int y, int spanX,
int spanY, int minSpanX, int minSpanY) {
CellLayout cl = createCellLayout(board.getWidth(), board.getHeight());
@@ -123,17 +123,17 @@
int[] testCaseXYinPixels = new int[2];
cl.regionToCenterPoint(x, y, spanX, spanY, testCaseXYinPixels);
- CellLayout.ItemConfiguration solution = cl.createReorderAlgorithm().calculateReorder(
+ ItemConfiguration solution = cl.createReorderAlgorithm().calculateReorder(
testCaseXYinPixels[0], testCaseXYinPixels[1], minSpanX, minSpanY, spanX, spanY,
null);
if (solution == null) {
- solution = new CellLayout.ItemConfiguration();
+ solution = new ItemConfiguration();
solution.isSolution = false;
}
return solution;
}
- public CellLayoutBoard boardFromSolution(CellLayout.ItemConfiguration solution, int width,
+ public CellLayoutBoard boardFromSolution(ItemConfiguration solution, int width,
int height) {
// Update the views with solution value
solution.map.forEach((key, val) -> key.setLayoutParams(
@@ -146,7 +146,7 @@
}
public void evaluateTestCase(ReorderAlgorithmUnitTestCase testCase) {
- CellLayout.ItemConfiguration solution = solve(testCase.startBoard, testCase.x,
+ ItemConfiguration solution = solve(testCase.startBoard, testCase.x,
testCase.y, testCase.spanX, testCase.spanY, testCase.minSpanX,
testCase.minSpanY);
assertEquals("should be a valid solution", solution.isSolution,
@@ -197,7 +197,7 @@
CellLayoutBoard board = generateBoard(new CellLayoutBoard(width, height),
new Rect(0, 0, width, height), targetWidth * targetHeight);
- CellLayout.ItemConfiguration solution = solve(board, x, y, targetWidth, targetHeight,
+ ItemConfiguration solution = solve(board, x, y, targetWidth, targetHeight,
minTargetWidth, minTargetHeight);
CellLayoutBoard finishBoard = solution.isSolution ? boardFromSolution(solution,