summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTony Wickham <twickham@google.com>2015-11-09 17:51:08 -0800
committerTony Wickham <twickham@google.com>2015-11-10 11:40:58 -0800
commit6cbd22279e5e033c67863a3db851e0f43be9ddff (patch)
tree299cb5d5398a0e504072524665b89f5cbd756b45
parent3cfa97d4c14fa4af3ee3613e44ba7b7128141e31 (diff)
downloadandroid_packages_apps_Trebuchet-6cbd22279e5e033c67863a3db851e0f43be9ddff.tar.gz
android_packages_apps_Trebuchet-6cbd22279e5e033c67863a3db851e0f43be9ddff.tar.bz2
android_packages_apps_Trebuchet-6cbd22279e5e033c67863a3db851e0f43be9ddff.zip
Add special column for All Apps button in FocusLogic's sparse matrix.
The All Apps button creates a number of edge cases, mainly because it causes the hotseat to sometimes have an extra column than the workspace. Previously, we sort of swept these problems under the rug by simply ignoring the All Apps button if other icons were present in the hotseat, with the assumption that those other icons should get focus instead of the All Apps button. (If possible, we want to stay in the same column when moving from the workspace to the hotseat.) But this doesn't always work, as in the attached bug where the hotseat doesn't get focus at all when the All Apps button is an obvious candidate for it. By adding a specialized column in the focus matrix for the All Apps button, we ensure that moving down to the hotseat stays within the original column when possible, while also allowing the focus to switch to the All Apps button if appropriate. Furthermore, we take care to skip over the All Apps column when necessary in order to maintain all previous functionality. Bug: 25590522 Change-Id: I5d6a8ee69de8834314c4689246fe7d54329b2eef
-rw-r--r--src/com/android/launcher3/FocusHelper.java22
-rw-r--r--src/com/android/launcher3/Hotseat.java2
-rw-r--r--src/com/android/launcher3/util/FocusLogic.java108
-rw-r--r--tests/src/com/android/launcher3/util/FocusLogicTest.java150
4 files changed, 232 insertions, 50 deletions
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index 8516afb4e..3d12aa3e2 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -223,20 +223,18 @@ public class FocusHelper {
if (keyCode == KeyEvent.KEYCODE_DPAD_UP &&
!profile.isVerticalBarLayout()) {
matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout,
- true /* hotseat horizontal */, profile.inv.hotseatAllAppsRank,
- iconRank == profile.inv.hotseatAllAppsRank /* include all apps icon */);
+ true /* hotseat horizontal */, profile.inv.hotseatAllAppsRank);
iconIndex += iconParent.getChildCount();
- countX = iconLayout.getCountX();
+ countX = hotseatLayout.getCountX();
countY = iconLayout.getCountY() + hotseatLayout.getCountY();
parent = iconParent;
} else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT &&
profile.isVerticalBarLayout()) {
matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout,
- false /* hotseat horizontal */, profile.inv.hotseatAllAppsRank,
- iconRank == profile.inv.hotseatAllAppsRank /* include all apps icon */);
+ false /* hotseat horizontal */, profile.inv.hotseatAllAppsRank);
iconIndex += iconParent.getChildCount();
countX = iconLayout.getCountX() + hotseatLayout.getCountX();
- countY = iconLayout.getCountY();
+ countY = hotseatLayout.getCountY();
parent = iconParent;
} else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT &&
profile.isVerticalBarLayout()) {
@@ -326,15 +324,15 @@ public class FocusHelper {
// with the hotseat.
if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN && !profile.isVerticalBarLayout()) {
matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, true /* horizontal */,
- profile.inv.hotseatAllAppsRank,
- !hotseat.hasIcons() /* ignore all apps icon, unless there are no other icons */);
- countY = countY + 1;
+ profile.inv.hotseatAllAppsRank);
+ countX = hotseatLayout.getCountX();
+ countY = countY + hotseatLayout.getCountY();
} else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT &&
profile.isVerticalBarLayout()) {
matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, false /* horizontal */,
- profile.inv.hotseatAllAppsRank,
- !hotseat.hasIcons() /* ignore all apps icon, unless there are no other icons */);
- countX = countX + 1;
+ profile.inv.hotseatAllAppsRank);
+ countX = countX + hotseatLayout.getCountX();
+ countY = hotseatLayout.getCountY();
} else if (isUninstallKeyChord(e)) {
matrix = FocusLogic.createSparseMatrix(iconLayout);
if (UninstallDropTarget.supportsDrop(launcher, itemInfo)) {
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 902b6ec61..3e838760e 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -70,7 +70,7 @@ public class Hotseat extends FrameLayout
public void setOnLongClickListener(OnLongClickListener l) {
mContent.setOnLongClickListener(l);
}
-
+
/* Get the orientation invariant order of the item in the hotseat for persistence. */
int getOrderInHotseat(int x, int y) {
return mHasVerticalHotseat ? (mContent.getCountY() - y - 1) : x;
diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java
index 2aae3c089..f56d16222 100644
--- a/src/com/android/launcher3/util/FocusLogic.java
+++ b/src/com/android/launcher3/util/FocusLogic.java
@@ -63,6 +63,8 @@ public class FocusLogic {
public static final int NEXT_PAGE_LEFT_COLUMN = -9;
public static final int NEXT_PAGE_RIGHT_COLUMN = -10;
+ public static final int ALL_APPS_COLUMN = -11;
+
// Matrix related constant.
public static final int EMPTY = -1;
public static final int PIVOT = 100;
@@ -186,22 +188,36 @@ public class FocusLogic {
*/
// TODO: get rid of the dynamic matrix creation
public static int[][] createSparseMatrix(CellLayout iconLayout, CellLayout hotseatLayout,
- boolean isHorizontal, int allappsiconRank, boolean includeAllappsicon) {
+ boolean isHotseatHorizontal, int allappsiconRank) {
ViewGroup iconParent = iconLayout.getShortcutsAndWidgets();
ViewGroup hotseatParent = hotseatLayout.getShortcutsAndWidgets();
+ boolean moreIconsInHotseatThanWorkspace = isHotseatHorizontal ?
+ hotseatLayout.getCountX() > iconLayout.getCountX() :
+ hotseatLayout.getCountY() > iconLayout.getCountY();
+
int m, n;
- if (isHorizontal) {
- m = iconLayout.getCountX();
+ if (isHotseatHorizontal) {
+ m = hotseatLayout.getCountX();
n = iconLayout.getCountY() + hotseatLayout.getCountY();
} else {
m = iconLayout.getCountX() + hotseatLayout.getCountX();
- n = iconLayout.getCountY();
+ n = hotseatLayout.getCountY();
}
int[][] matrix = createFullMatrix(m, n);
-
- // Iterate thru the children of the top parent.
+ if (moreIconsInHotseatThanWorkspace) {
+ if (isHotseatHorizontal) {
+ for (int j = 0; j < n; j++) {
+ matrix[allappsiconRank][j] = ALL_APPS_COLUMN;
+ }
+ } else {
+ for (int j = 0; j < m; j++) {
+ matrix[j][allappsiconRank] = ALL_APPS_COLUMN;
+ }
+ }
+ }
+ // Iterate thru the children of the workspace.
for (int i = 0; i < iconParent.getChildCount(); i++) {
View cell = iconParent.getChildAt(i);
if (!cell.isFocusable()) {
@@ -209,31 +225,29 @@ public class FocusLogic {
}
int cx = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellX;
int cy = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellY;
+ if (moreIconsInHotseatThanWorkspace) {
+ if (isHotseatHorizontal && cx >= allappsiconRank) {
+ // Add 1 to account for the All Apps button.
+ cx++;
+ }
+ if (!isHotseatHorizontal && cy >= allappsiconRank) {
+ // Add 1 to account for the All Apps button.
+ cy++;
+ }
+ }
matrix[cx][cy] = i;
}
- // Iterate thru the children of the bottom parent
- // The hotseat view group contains one more item than iconLayout column count.
- // If {@param allappsiconRank} not negative, then the last icon in the hotseat
- // is truncated. If it is negative, then all apps icon index is not inserted.
- for(int i = hotseatParent.getChildCount() - 1; i >= (includeAllappsicon ? 0 : 1); i--) {
- int delta = 0;
- if (isHorizontal) {
+ // Iterate thru the children of the hotseat.
+ for (int i = hotseatParent.getChildCount() - 1; i >= 0; i--) {
+ if (isHotseatHorizontal) {
int cx = ((CellLayout.LayoutParams)
hotseatParent.getChildAt(i).getLayoutParams()).cellX;
- if ((includeAllappsicon && cx >= allappsiconRank) ||
- (!includeAllappsicon && cx > allappsiconRank)) {
- delta = -1;
- }
- matrix[cx + delta][iconLayout.getCountY()] = iconParent.getChildCount() + i;
+ matrix[cx][iconLayout.getCountY()] = iconParent.getChildCount() + i;
} else {
int cy = ((CellLayout.LayoutParams)
hotseatParent.getChildAt(i).getLayoutParams()).cellY;
- if ((includeAllappsicon && cy >= allappsiconRank) ||
- (!includeAllappsicon && cy > allappsiconRank)) {
- delta = -1;
- }
- matrix[iconLayout.getCountX()][cy + delta] = iconParent.getChildCount() + i;
+ matrix[iconLayout.getCountX()][cy] = iconParent.getChildCount() + i;
}
}
if (DEBUG) {
@@ -323,8 +337,9 @@ public class FocusLogic {
}
// Rule1: check first in the horizontal direction
- for (int i = xPos + increment; 0 <= i && i < cntX; i = i + increment) {
- if ((newIconIndex = inspectMatrix(i, yPos, cntX, cntY, matrix)) != NOOP) {
+ for (int x = xPos + increment; 0 <= x && x < cntX; x += increment) {
+ if ((newIconIndex = inspectMatrix(x, yPos, cntX, cntY, matrix)) != NOOP
+ && newIconIndex != ALL_APPS_COLUMN) {
return newIconIndex;
}
}
@@ -333,15 +348,23 @@ public class FocusLogic {
// (x2-n, yPos + 2*increment), (x2-n, yPos - 2*increment)
int nextYPos1;
int nextYPos2;
- int i = -1;
+ int x = -1;
for (int coeff = 1; coeff < cntY; coeff++) {
nextYPos1 = yPos + coeff * increment;
nextYPos2 = yPos - coeff * increment;
- for (i = xPos + increment * coeff; 0 <= i && i < cntX; i = i + increment) {
- if ((newIconIndex = inspectMatrix(i, nextYPos1, cntX, cntY, matrix)) != NOOP) {
+ x = xPos + increment * coeff;
+ if (inspectMatrix(x, nextYPos1, cntX, cntY, matrix) == ALL_APPS_COLUMN) {
+ nextYPos1 += increment;
+
+ }
+ if (inspectMatrix(x, nextYPos2, cntX, cntY, matrix) == ALL_APPS_COLUMN) {
+ nextYPos2 -= increment;
+ }
+ for (; 0 <= x && x < cntX; x += increment) {
+ if ((newIconIndex = inspectMatrix(x, nextYPos1, cntX, cntY, matrix)) != NOOP) {
return newIconIndex;
}
- if ((newIconIndex = inspectMatrix(i, nextYPos2, cntX, cntY, matrix)) != NOOP) {
+ if ((newIconIndex = inspectMatrix(x, nextYPos2, cntX, cntY, matrix)) != NOOP) {
return newIconIndex;
}
}
@@ -350,9 +373,10 @@ public class FocusLogic {
// Rule 3: if switching between pages, do a brute-force search to find an item that was
// missed by rules 1 and 2 (such as when going from a bottom right icon to top left)
if (iconIdx == PIVOT) {
- for (int x = xPos + increment; 0 <= x && x < cntX; x = x + increment) {
+ for (x = xPos + increment; 0 <= x && x < cntX; x += increment) {
for (int y = 0; y < cntY; y++) {
- if ((newIconIndex = inspectMatrix(x, y, cntX, cntY, matrix)) != NOOP) {
+ if ((newIconIndex = inspectMatrix(x, y, cntX, cntY, matrix)) != NOOP
+ && newIconIndex != ALL_APPS_COLUMN) {
return newIconIndex;
}
}
@@ -396,8 +420,9 @@ public class FocusLogic {
}
// Rule1: check first in the dpad direction
- for (int j = yPos + increment; 0 <= j && j <cntY && 0 <= j; j = j + increment) {
- if ((newIconIndex = inspectMatrix(xPos, j, cntX, cntY, matrix)) != NOOP) {
+ for (int y = yPos + increment; 0 <= y && y <cntY && 0 <= y; y += increment) {
+ if ((newIconIndex = inspectMatrix(xPos, y, cntX, cntY, matrix)) != NOOP
+ && newIconIndex != ALL_APPS_COLUMN) {
return newIconIndex;
}
}
@@ -406,15 +431,23 @@ public class FocusLogic {
// (xPos + 2*increment, y_(2-n))), (xPos - 2*increment, y_(2-n))
int nextXPos1;
int nextXPos2;
- int j = -1;
+ int y = -1;
for (int coeff = 1; coeff < cntX; coeff++) {
nextXPos1 = xPos + coeff * increment;
nextXPos2 = xPos - coeff * increment;
- for (j = yPos + increment * coeff; 0 <= j && j < cntY; j = j + increment) {
- if ((newIconIndex = inspectMatrix(nextXPos1, j, cntX, cntY, matrix)) != NOOP) {
+ y = yPos + increment * coeff;
+ if (inspectMatrix(nextXPos1, y, cntX, cntY, matrix) == ALL_APPS_COLUMN) {
+ nextXPos1 += increment;
+
+ }
+ if (inspectMatrix(nextXPos2, y, cntX, cntY, matrix) == ALL_APPS_COLUMN) {
+ nextXPos2 -= increment;
+ }
+ for (; 0 <= y && y < cntY; y = y + increment) {
+ if ((newIconIndex = inspectMatrix(nextXPos1, y, cntX, cntY, matrix)) != NOOP) {
return newIconIndex;
}
- if ((newIconIndex = inspectMatrix(nextXPos2, j, cntX, cntY, matrix)) != NOOP) {
+ if ((newIconIndex = inspectMatrix(nextXPos2, y, cntX, cntY, matrix)) != NOOP) {
return newIconIndex;
}
}
@@ -481,6 +514,7 @@ public class FocusLogic {
case CURRENT_PAGE_LAST_ITEM: return "CURRENT_PAGE_LAST";
case NEXT_PAGE_FIRST_ITEM: return "NEXT_PAGE_FIRST";
case NEXT_PAGE_LEFT_COLUMN: return "NEXT_PAGE_LEFT_COLUMN";
+ case ALL_APPS_COLUMN: return "ALL_APPS_COLUMN";
default:
return Integer.toString(index);
}
diff --git a/tests/src/com/android/launcher3/util/FocusLogicTest.java b/tests/src/com/android/launcher3/util/FocusLogicTest.java
index 2c2f0d3df..f93e91313 100644
--- a/tests/src/com/android/launcher3/util/FocusLogicTest.java
+++ b/tests/src/com/android/launcher3/util/FocusLogicTest.java
@@ -19,6 +19,7 @@ package com.android.launcher3;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.view.KeyEvent;
+import android.view.View;
import com.android.launcher3.util.FocusLogic;
@@ -82,6 +83,155 @@ public final class FocusLogicTest extends AndroidTestCase {
assertEquals(0, i);
}
+ public void testMoveIntoHotseatWithEqualHotseatAndWorkspaceColumns() {
+ // Test going from an icon right above the All Apps button to the All Apps button.
+ int[][] map = transpose(new int[][] {
+ {-1, -1, -1, -1, -1},
+ {-1, -1, -1, -1, -1},
+ {-1, -1, -1, -1, -1},
+ {-1, -1, 0, -1, -1},
+ { 2, 3, 1, 4, 5},
+ });
+ int i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, 5, 5, map, 0, 1, 1, true);
+ assertEquals(1, i);
+ // Test going from an icon above and to the right of the All Apps
+ // button to an icon to the right of the All Apps button.
+ map = transpose(new int[][] {
+ {-1, -1, -1, -1, -1},
+ {-1, -1, -1, -1, -1},
+ {-1, -1, -1, -1, -1},
+ {-1, -1, -1, 0, -1},
+ { 2, 3, 1, 4, 5},
+ });
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, 5, 5, map, 0, 1, 1, true);
+ assertEquals(4, i);
+ }
+
+ public void testMoveIntoHotseatWithExtraColumnForAllApps() {
+ // Test going from an icon above and to the left
+ // of the All Apps button to the All Apps button.
+ int[][] map = transpose(new int[][] {
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, 0,-11, -1, -1, -1},
+ {-1, -1, -1, 1, 1, -1, -1},
+ });
+ int i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, 7, 6, map, 0, 1, 1, true);
+ assertEquals(1, i);
+ // Test going from an icon above and to the right
+ // of the All Apps button to the All Apps button.
+ map = transpose(new int[][] {
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, 0, -1, -1},
+ {-1, -1, -1, 1, -1, -1, -1},
+ });
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, 7, 6, map, 0, 1, 1, true);
+ assertEquals(1, i);
+ // Test going from the All Apps button to an icon
+ // above and to the right of the All Apps button.
+ map = transpose(new int[][] {
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, 0, -1, -1},
+ {-1, -1, -1, 1, -1, -1, -1},
+ });
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_UP, 7, 6, map, 1, 1, 1, true);
+ assertEquals(0, i);
+ // Test going from an icon above and to the left of the
+ // All Apps button in landscape to the All Apps button.
+ map = transpose(new int[][] {
+ { -1, -1, -1, -1, -1},
+ { -1, -1, -1, 0, -1},
+ {-11,-11,-11,-11, 1},
+ { -1, -1, -1, -1, -1},
+ { -1, -1, -1, -1, -1},
+ });
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_RIGHT, 5, 5, map, 0, 1, 1, true);
+ assertEquals(1, i);
+ // Test going from the All Apps button in landscape to
+ // an icon above and to the left of the All Apps button.
+ map = transpose(new int[][] {
+ { -1, -1, -1, -1, -1},
+ { -1, -1, -1, 0, -1},
+ {-11,-11,-11,-11, 1},
+ { -1, -1, -1, -1, -1},
+ { -1, -1, -1, -1, -1},
+ });
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_LEFT, 5, 5, map, 1, 1, 1, true);
+ assertEquals(0, i);
+ // Test that going to the hotseat always goes to the same row as the original icon.
+ map = transpose(new int[][]{
+ { 0, 1, 2,-11, 3, 4, 5},
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, -1, -1, -1},
+ { 7, 8, 9, 6, 10, 11, 12},
+ });
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, 7, 6, map, 0, 1, 1, true);
+ assertEquals(7, i);
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, 7, 6, map, 1, 1, 1, true);
+ assertEquals(8, i);
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, 7, 6, map, 2, 1, 1, true);
+ assertEquals(9, i);
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, 7, 6, map, 3, 1, 1, true);
+ assertEquals(10, i);
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, 7, 6, map, 4, 1, 1, true);
+ assertEquals(11, i);
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, 7, 6, map, 5, 1, 1, true);
+ assertEquals(12, i);
+ }
+
+ public void testCrossingAllAppsColumn() {
+ // Test crossing from left to right in portrait.
+ int[][] map = transpose(new int[][] {
+ {-1, -1,-11, -1, -1},
+ {-1, 0,-11, -1, -1},
+ {-1, -1,-11, 1, -1},
+ {-1, -1,-11, -1, -1},
+ {-1, -1, 2, -1, -1},
+ });
+ int i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, 5, 5, map, 0, 1, 1, true);
+ assertEquals(1, i);
+ // Test crossing from right to left in portrait.
+ map = transpose(new int[][] {
+ {-1, -1,-11, -1, -1},
+ {-1, -1,-11, 0, -1},
+ {-1, 1,-11, -1, -1},
+ {-1, -1,-11, -1, -1},
+ {-1, -1, 2, -1, -1},
+ });
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, 5, 5, map, 0, 1, 1, true);
+ assertEquals(1, i);
+ // Test crossing from left to right in landscape.
+ map = transpose(new int[][] {
+ { -1, -1, -1, -1, -1},
+ { -1, -1, -1, 0, -1},
+ {-11,-11,-11,-11, 2},
+ { -1, 1, -1, -1, -1},
+ { -1, -1, -1, -1, -1},
+ });
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_LEFT, 5, 5, map, 0, 1, 1, true);
+ assertEquals(1, i);
+ // Test crossing from right to left in landscape.
+ map = transpose(new int[][] {
+ { -1, -1, -1, -1, -1},
+ { -1, 0, -1, -1, -1},
+ {-11,-11,-11,-11, 2},
+ { -1, -1, 1, -1, -1},
+ { -1, -1, -1, -1, -1},
+ });
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_RIGHT, 5, 5, map, 0, 1, 1, true);
+ assertEquals(1, i);
+ }
+
/** Transposes the matrix so that we can write it in human-readable format in the tests. */
private int[][] transpose(int[][] m) {
int[][] t = new int[m[0].length][m.length];