summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJon Miranda <jonmiranda@google.com>2017-06-25 18:37:39 -0700
committerJon Miranda <jonmiranda@google.com>2017-06-28 09:52:11 -0700
commit69c340a05e846b4b1b2d457164fd313e249df353 (patch)
tree5bdc2b4185a2f9a8f886053ab81cca9f8d056c55 /src
parentaa2272f81cc4a62f847db777efda7959cc2b9979 (diff)
downloadandroid_packages_apps_Trebuchet-69c340a05e846b4b1b2d457164fd313e249df353.tar.gz
android_packages_apps_Trebuchet-69c340a05e846b4b1b2d457164fd313e249df353.tar.bz2
android_packages_apps_Trebuchet-69c340a05e846b4b1b2d457164fd313e249df353.zip
Match items in icon preview with items in Folder using permutations.
Before, with the FolderIconPreviewVerifier, we would adjust which items are displayed in the FolderIcon. This caused some issues where the apps in the folder icon would jump to whatever was in the upper left quadrant. Now, we always display the 4 first items in the icon by modifying the XY positions of the items within the Folder. Bug: 27944225 Bug: 35064148 Change-Id: I46c0fbb064d4da4da155e29963bfb92b14e40f07
Diffstat (limited to 'src')
-rw-r--r--src/com/android/launcher3/Workspace.java2
-rw-r--r--src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java2
-rw-r--r--src/com/android/launcher3/folder/Folder.java80
-rw-r--r--src/com/android/launcher3/folder/FolderIcon.java2
-rw-r--r--src/com/android/launcher3/folder/FolderIconPreviewVerifier.java29
-rw-r--r--src/com/android/launcher3/folder/FolderPagedView.java183
6 files changed, 210 insertions, 88 deletions
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 919c60a34..b1db7e8db 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -3792,7 +3792,7 @@ public class Workspace extends PagedView
ItemInfo info = (ItemInfo) item.getTag();
if (recurse && info instanceof FolderInfo && item instanceof FolderIcon) {
FolderIcon folder = (FolderIcon) item;
- ArrayList<View> folderChildren = folder.getFolder().getItemsInReadingOrder();
+ ArrayList<View> folderChildren = folder.getFolder().getItemsInRankOrder();
// map over all the children in the folder
final int childCount = folderChildren.size();
for (int childIdx = 0; childIdx < childCount; childIdx++) {
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 34335330b..6cd086b38 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -369,7 +369,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
Folder folder = Folder.getOpen(mLauncher);
if (folder != null) {
- if (!folder.getItemsInReadingOrder().contains(item)) {
+ if (!folder.getItemsInRankOrder().contains(item)) {
folder.close(true);
folder = null;
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 3c7c69810..f68b394c1 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -133,7 +133,7 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC
private final Alarm mOnScrollHintAlarm = new Alarm();
@Thunk final Alarm mScrollPauseAlarm = new Alarm();
- @Thunk final ArrayList<View> mItemsInReadingOrder = new ArrayList<View>();
+ @Thunk final ArrayList<View> mItemsInRankOrder = new ArrayList<>();
private AnimatorSet mCurrentAnimator;
@@ -284,7 +284,7 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC
if (tag instanceof ShortcutInfo) {
ShortcutInfo item = (ShortcutInfo) tag;
- mEmptyCellRank = item.rank;
+ mEmptyCellRank = mContent.getReadingOrderPosForRank(item.rank);
mCurrentDragView = v;
mDragController.addDragListener(this);
@@ -705,7 +705,7 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC
}
public void beginExternalDrag() {
- mEmptyCellRank = mContent.allocateRankForNewItem();
+ mEmptyCellRank = mContent.getReadingOrderPosForRank(mContent.allocateRankForNewItem());
mIsExternalDrag = true;
mDragInProgress = true;
@@ -997,7 +997,7 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC
ShortcutInfo info = (ShortcutInfo) d.dragInfo;
View icon = (mCurrentDragView != null && mCurrentDragView.getTag() == info)
? mCurrentDragView : mContent.createNewView(info);
- ArrayList<View> views = getItemsInReadingOrder();
+ ArrayList<View> views = getItemsInRankOrder();
views.add(info.rank, icon);
mContent.arrangeChildren(views, views.size());
mItemsInvalidated = true;
@@ -1072,7 +1072,7 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC
}
private void updateItemLocationsInDatabaseBatch() {
- ArrayList<View> list = getItemsInReadingOrder();
+ ArrayList<View> list = getItemsInRankOrder();
ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
for (int i = 0; i < list.size(); i++) {
View v = list.get(i);
@@ -1231,7 +1231,7 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC
* otherwise it is ignored.
*/
public void rearrangeChildren(int itemCount) {
- ArrayList<View> views = getItemsInReadingOrder();
+ ArrayList<View> views = getItemsInRankOrder();
mContent.arrangeChildren(views, Math.max(itemCount, views.size()));
mItemsInvalidated = true;
}
@@ -1376,24 +1376,20 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC
View currentDragView;
if (mIsExternalDrag) {
- currentDragView = mContent.createAndAddViewForRank(si, mEmptyCellRank);
-
// Actually move the item in the database if it was an external drag. Call this
// before creating the view, so that ShortcutInfo is updated appropriately.
- mLauncher.getModelWriter().addOrMoveItemInDatabase(
- si, mInfo.id, 0, si.cellX, si.cellY);
-
- // We only need to update the locations if it doesn't get handled in
- // #onDropCompleted.
- if (d.dragSource != this) {
- updateItemLocationsInDatabaseBatch();
- }
- mIsExternalDrag = false;
- } else {
- currentDragView = mCurrentDragView;
- mContent.addViewForRank(currentDragView, si, mEmptyCellRank);
+ mLauncher.getModelWriter().addOrMoveItemInDatabase(si, mInfo.id, 0, si.cellX, si.cellY);
}
+ currentDragView = mIsExternalDrag
+ ? mContent.createNewView(si)
+ : mCurrentDragView;
+ mIsExternalDrag = false;
+
+ // Note: addViewForRankDuringDragAndDrop handles rearranging the children.
+ mContent.addViewForRankDuringDragAndDrop(currentDragView, si, mEmptyCellRank);
+ mItemsInvalidated = true;
+
if (d.dragView.hasDrawn()) {
// Temporarily reset the scale such that the animation target gets calculated
// correctly.
@@ -1410,9 +1406,6 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC
currentDragView.setVisibility(VISIBLE);
}
- mItemsInvalidated = true;
- rearrangeChildren();
-
// Temporarily suppress the listener, as we did all the work already here.
try (SuppressInfoChanges s = new SuppressInfoChanges()) {
mInfo.add(si, false);
@@ -1450,7 +1443,7 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC
mLauncher.getModelWriter().addOrMoveItemInDatabase(item, mInfo.id, 0, item.cellX,
item.cellY);
- ArrayList<View> items = new ArrayList<>(getItemsInReadingOrder());
+ ArrayList<View> items = new ArrayList<>(getItemsInRankOrder());
items.add(rank, view);
mContent.arrangeChildren(items, items.size());
mItemsInvalidated = true;
@@ -1497,24 +1490,34 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC
public void onTitleChanged(CharSequence title) {
}
- public ArrayList<View> getItemsInReadingOrder() {
+ public ArrayList<View> getItemsInRankOrder() {
if (mItemsInvalidated) {
- mItemsInReadingOrder.clear();
- mContent.iterateOverItems(new ItemOperator() {
+ mItemsInRankOrder.clear();
+ mItemsInRankOrder.addAll(getItemsInReadingOrder());
+ mItemsInRankOrder.sort(VIEW_RANK_COMPARATOR);
- @Override
- public boolean evaluate(ItemInfo info, View view) {
- mItemsInReadingOrder.add(view);
- return false;
- }
- });
mItemsInvalidated = false;
}
- return mItemsInReadingOrder;
+ return mItemsInRankOrder;
+ }
+
+ /**
+ * This is an expensive call. Consider using {@link #getItemsInRankOrder()} instead.
+ */
+ public ArrayList<View> getItemsInReadingOrder() {
+ final ArrayList<View> itemsInReadingOrder = new ArrayList<>();
+ mContent.iterateOverItems(new ItemOperator() {
+ @Override
+ public boolean evaluate(ItemInfo info, View view) {
+ itemsInReadingOrder.add(view);
+ return false;
+ }
+ });
+ return itemsInReadingOrder;
}
public List<BubbleTextView> getItemsOnCurrentPage() {
- ArrayList<View> allItems = getItemsInReadingOrder();
+ ArrayList<View> allItems = getItemsInRankOrder();
int currentPage = mContent.getCurrentPage();
int lastPage = mContent.getPageCount() - 1;
int totalItemsInFolder = allItems.size();
@@ -1622,6 +1625,13 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC
}
};
+ public static final Comparator<View> VIEW_RANK_COMPARATOR = new Comparator<View>() {
+ @Override
+ public int compare(View lhs, View rhs) {
+ return ITEM_POS_COMPARATOR.compare((ItemInfo) lhs.getTag(), (ItemInfo) rhs.getTag());
+ }
+ };
+
/**
* Temporary resource held while we don't want to handle info changes
*/
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 1cc285ea5..3a0e71fa5 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -575,7 +575,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
mPreviewVerifier.setFolderInfo(mFolder.getInfo());
List<BubbleTextView> itemsToDisplay = new ArrayList<>();
- List<View> allItems = mFolder.getItemsInReadingOrder();
+ List<View> allItems = mFolder.getItemsInRankOrder();
int numItems = allItems.size();
for (int rank = 0; rank < numItems; ++rank) {
if (mPreviewVerifier.isItemInPreview(rank)) {
diff --git a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
index de962b021..d0d8e79d5 100644
--- a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
+++ b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
@@ -25,40 +25,15 @@ import com.android.launcher3.config.FeatureFlags;
*/
public class FolderIconPreviewVerifier {
- private final int mMaxGridCountX;
- private final int mMaxGridCountY;
- private final int mMaxItemsPerPage;
- private final int[] mGridSize = new int[2];
-
- private int mGridCountX;
- private boolean mDisplayingUpperLeftQuadrant = false;
-
public FolderIconPreviewVerifier(InvariantDeviceProfile profile) {
- mMaxGridCountX = profile.numFolderColumns;
- mMaxGridCountY = profile.numFolderRows;
- mMaxItemsPerPage = mMaxGridCountX * mMaxGridCountY;
+ // b/37570804
}
public void setFolderInfo(FolderInfo info) {
- int numItemsInFolder = info.contents.size();
- mDisplayingUpperLeftQuadrant = FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION
- && !FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON
- && numItemsInFolder > FolderIcon.NUM_ITEMS_IN_PREVIEW;
-
- if (mDisplayingUpperLeftQuadrant) {
- FolderPagedView.calculateGridSize(info.contents.size(), 0, 0, mMaxGridCountX,
- mMaxGridCountY, mMaxItemsPerPage, mGridSize);
- mGridCountX = mGridSize[0];
- }
+ // b/37570804
}
public boolean isItemInPreview(int rank) {
- if (mDisplayingUpperLeftQuadrant) {
- // Returns true iff the icon is in the 2x2 upper left quadrant of the Folder.
- int col = rank % mGridCountX;
- int row = rank / mGridCountX;
- return col < 2 && row < 2;
- }
return rank < FolderIcon.NUM_ITEMS_IN_PREVIEW;
}
}
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index f62568f78..21631fa24 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -201,18 +201,29 @@ public class FolderPagedView extends PagedView {
}
public void allocateSpaceForRank(int rank) {
- ArrayList<View> views = new ArrayList<>(mFolder.getItemsInReadingOrder());
+ ArrayList<View> views = new ArrayList<>(mFolder.getItemsInRankOrder());
views.add(rank, null);
arrangeChildren(views, views.size(), false);
}
+ private ArrayList<View> createListWithViewAtPos(ArrayList<View> list, View v, int position) {
+ ArrayList<View> newList = new ArrayList<>(list.size() + 1);
+ newList.addAll(list);
+ newList.add(position, v);
+ return newList;
+ }
+
/**
- * Create space for a new item at the end, and returns the rank for that item.
+ * Create space for a new item and returns the rank for that item.
* Also sets the current page to the last page.
*/
public int allocateRankForNewItem() {
- int rank = getItemCount();
- allocateSpaceForRank(rank);
+ ArrayList<View> rankOrder = mFolder.getItemsInRankOrder();
+ int rank = rankOrder.size();
+
+ ArrayList<View> views = createListWithViewAtPos(rankOrder, null, rank);
+ arrangeChildren(views, views.size(), false);
+
setCurrentPage(rank / mMaxItemsPerPage);
return rank;
}
@@ -229,20 +240,59 @@ public class FolderPagedView extends PagedView {
* related attributes. It assumes that {@param item} is already attached to the view.
*/
public void addViewForRank(View view, ShortcutInfo item, int rank) {
- int pagePos = rank % mMaxItemsPerPage;
- int pageNo = rank / mMaxItemsPerPage;
+ updateShortcutInfoWithRank(item, rank);
+
+ ArrayList<View> views = createListWithViewAtPos(mFolder.getItemsInRankOrder(), view, rank);
+ arrangeChildren(views, views.size(), false);
+ }
- item.rank = rank;
- item.cellX = pagePos % mGridCountX;
- item.cellY = pagePos / mGridCountX;
+ /**
+ * Similar to {@link #addViewForRank(View, ShortcutInfo, int)}}, but specific to real time
+ * reorder.
+ *
+ * The difference here is that during real time reorder, we are moving the Views in a contained
+ * order.
+ */
+ public void addViewForRankDuringReorder(View view, ShortcutInfo item, int rank) {
+ updateShortcutInfoWithRank(item, rank);
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
lp.cellX = item.cellX;
lp.cellY = item.cellY;
+
+ int pageNo = rank / mMaxItemsPerPage;
getPageAt(pageNo).addViewToCellLayout(
view, -1, mFolder.mLauncher.getViewIdForItem(item), lp, true);
}
+ /**
+ * Similar to {@link #addViewForRank(View, ShortcutInfo, int)}, but specific to drag and drop.
+ *
+ * The difference is that we handle the drag and drop by adjusting the reading order of the
+ * children, rather than based on their rank.
+ */
+ public void addViewForRankDuringDragAndDrop(View view, ShortcutInfo item, int readingRank) {
+ updateShortcutInfoWithRank(item, readingRank);
+
+ ArrayList<View> views = createListWithViewAtPos(mFolder.getItemsInReadingOrder(), view,
+ readingRank);
+
+ int numItems = views.size();
+ ArrayList<View> rankOrder = new ArrayList<>(numItems);
+ for (int i = 0; i < numItems; ++i) {
+ rankOrder.add(views.get(getReadingOrderPosForRank(i)));
+ }
+
+ arrangeChildren(rankOrder, numItems, false);
+ }
+
+ private void updateShortcutInfoWithRank(ShortcutInfo info, int rank) {
+ info.rank = rank;
+ getCellXYPositionForRank(rank, sTmpArray);
+ info.cellX = sTmpArray[0];
+ info.cellY = sTmpArray[1];
+ }
+
@SuppressLint("InflateParams")
public View createNewView(ShortcutInfo item) {
final BubbleTextView textView = (BubbleTextView) mInflater.inflate(
@@ -310,18 +360,19 @@ public class FolderPagedView extends PagedView {
* It essentially removes all views from all the pages and then adds them again in appropriate
* page.
*
- * @param list the ordered list of children.
+ * @param rankOrderedList the rank-ordered list of children.
* @param itemCount if greater than the total children count, empty spaces are left
* at the end, otherwise it is ignored.
*
*/
- public void arrangeChildren(ArrayList<View> list, int itemCount) {
- arrangeChildren(list, itemCount, true);
+ public void arrangeChildren(ArrayList<View> rankOrderedList, int itemCount) {
+ arrangeChildren(rankOrderedList, itemCount, true);
}
@SuppressLint("RtlHardcoded")
- private void arrangeChildren(ArrayList<View> list, int itemCount, boolean saveChanges) {
- ArrayList<CellLayout> pages = new ArrayList<>();
+ private void arrangeChildren(ArrayList<View> rankOrderedList, int itemCount,
+ boolean saveChanges) {
+ ArrayList<CellLayout> pages = new ArrayList<CellLayout>();
for (int i = 0; i < getChildCount(); i++) {
CellLayout page = (CellLayout) getChildAt(i);
page.removeAllViews();
@@ -339,7 +390,7 @@ public class FolderPagedView extends PagedView {
Launcher.getLauncher(getContext()).getDeviceProfile().inv);
rank = 0;
for (int i = 0; i < itemCount; i++) {
- View v = list.size() > i ? list.get(i) : null;
+ View v = rankOrderedList.size() > i ? rankOrderedList.get(i) : null;
if (currentPage == null || position >= mMaxItemsPerPage) {
// Next page
if (pageItr.hasNext()) {
@@ -352,8 +403,10 @@ public class FolderPagedView extends PagedView {
if (v != null) {
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams();
- newX = position % mGridCountX;
- newY = position / mGridCountX;
+ getCellXYPositionForRank(rank, sTmpArray);
+ newX = sTmpArray[0];
+ newY = sTmpArray[1];
+
ItemInfo info = (ItemInfo) v.getTag();
if (info.cellX != newX || info.cellY != newY || info.rank != rank) {
info.cellX = newX;
@@ -651,7 +704,7 @@ public class FolderPagedView extends PagedView {
if (v != null) {
if (pageToAnimate != p) {
page.removeView(v);
- addViewForRank(v, (ShortcutInfo) v.getTag(), moveStart);
+ addViewForRankDuringReorder(v, (ShortcutInfo) v.getTag(), moveStart);
} else {
// Do a fake animation before removing it.
final int newRank = moveStart;
@@ -664,14 +717,14 @@ public class FolderPagedView extends PagedView {
mPendingAnimations.remove(v);
v.setTranslationX(oldTranslateX);
((CellLayout) v.getParent().getParent()).removeView(v);
- addViewForRank(v, (ShortcutInfo) v.getTag(), newRank);
+ addViewForRankDuringReorder(v, (ShortcutInfo) v.getTag(), newRank);
}
};
v.animate()
- .translationXBy((direction > 0 ^ mIsRtl) ? -v.getWidth() : v.getWidth())
- .setDuration(REORDER_ANIMATION_DURATION)
- .setStartDelay(0)
- .withEndAction(endAction);
+ .translationXBy((direction > 0 ^ mIsRtl) ? -v.getWidth() : v.getWidth())
+ .setDuration(REORDER_ANIMATION_DURATION)
+ .setStartDelay(0)
+ .withEndAction(endAction);
mPendingAnimations.put(v, endAction);
}
}
@@ -701,4 +754,88 @@ public class FolderPagedView extends PagedView {
public int itemsPerPage() {
return mMaxItemsPerPage;
}
+
+ /**
+ * Returns the reading order position for a given rank.
+ *
+ * ie. For the permutation below, rank 0 returns 0, rank 1 returns 1, rank 4 returns 2,
+ * rank 2 returns 3, rank 3 returns 4, rank 5 returns 5.
+ *
+ * R0 R1 R4
+ * R2 R3 R5
+ */
+ public int getReadingOrderPosForRank(int rank) {
+ if (rank >= mMaxItemsPerPage) {
+ return rank;
+ }
+
+ getCellXYPositionForRank(rank, sTmpArray);
+ return sTmpArray[0] + (mGridCountX * sTmpArray[1]);
+ }
+
+ /**
+ * Returns the cell XY position for a Folder item with the given rank.
+ */
+ public void getCellXYPositionForRank(int rank, int[] outXY) {
+ boolean onFirstPage = rank < mMaxItemsPerPage;
+
+ if (onFirstPage && mGridCountX == 3) {
+ outXY[0] = FolderPermutation.THREE_COLS[rank][0];
+ outXY[1] = FolderPermutation.THREE_COLS[rank][1];
+ } else if (onFirstPage && mGridCountX == 4) {
+ outXY[0] = FolderPermutation.FOUR_COLS[rank][0];
+ outXY[1] = FolderPermutation.FOUR_COLS[rank][1];
+ } else if (onFirstPage && mGridCountX == 5) {
+ outXY[0] = FolderPermutation.FIVE_COLS[rank][0];
+ outXY[1] = FolderPermutation.FIVE_COLS[rank][1];
+ } else {
+ outXY[0] = (rank % mMaxItemsPerPage) % mGridCountX;
+ outXY[1] = (rank % mMaxItemsPerPage) / mGridCountX;
+ }
+ }
+
+ /**
+ * Provides the mapping between a folder item's rank and its cell location, based on the
+ * number of columns.
+ *
+ * We use this mapping, rather than the regular reading order, to preserve the items in the
+ * upper left quadrant of the Folder. This allows a smooth transition between the FolderIcon
+ * and the opened Folder.
+ *
+ * TODO: We will replace these hard coded tables with an algorithm b/62986680
+ */
+ private static class FolderPermutation {
+ /**
+ * R0 R1 R4
+ * R2 R3 R5
+ * R6 R7 R8
+ */
+ static final int[][] THREE_COLS = new int[][] {
+ {0, 0}, {1, 0}, {0, 1}, {1, 1}, {2, 0}, {2, 1}, {0, 2}, {1, 2}, {2, 2}
+ };
+
+ /**
+ * R0 R1 R4 R6
+ * R2 R3 R5 R7
+ * R8 R9 R10 R11
+ * R12 R13 R14 R15
+ */
+ static final int[][] FOUR_COLS = new int[][] {
+ {0, 0}, {1, 0}, {0, 1}, {1, 1}, {2, 0}, {2, 1}, {3, 0}, {3, 1}, {0, 2}, {1, 2},
+ {2, 2}, {3, 2}, {0, 3}, {1, 3}, {2, 3}, {3, 3}
+ };
+
+ /**
+ * R0 R1 R4 R6 R12
+ * R2 R3 R5 R7 R13
+ * R8 R9 R10 R11 R14
+ * R15 R16 R17 R18 R19
+ * R20 R21 R22 R23 R24
+ */
+ static final int[][] FIVE_COLS = new int[][] {
+ {0, 0}, {1, 0}, {0, 1}, {1, 1}, {2, 0}, {2, 1}, {3, 0}, {3, 1}, {0, 2}, {1, 2},
+ {2, 2}, {3, 2}, {4, 0}, {4, 1}, {4, 2}, {0, 3}, {1, 3}, {2, 3}, {3, 3}, {4, 3},
+ {0, 4}, {1, 4}, {2, 4}, {3, 4}, {4, 4}
+ };
+ }
}