Merge "Normalizing app icons based on the standard icon guidelines" into ub-launcher3-burnaby-polish
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index 44403e2..d59c644 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -45,6 +45,23 @@
}
}
+/**
+ * A keyboard listener we set on full screen pages (e.g. custom content).
+ */
+class FullscreenKeyEventListener implements View.OnKeyListener {
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
+ || keyCode == KeyEvent.KEYCODE_PAGE_DOWN || keyCode == KeyEvent.KEYCODE_PAGE_UP) {
+ // Handle the key event just like a workspace icon would in these cases. In this case,
+ // it will basically act as if there is a single icon in the top left (so you could
+ // think of the fullscreen page as a focusable fullscreen widget).
+ return FocusHelper.handleIconKeyEvent(v, keyCode, event);
+ }
+ return false;
+ }
+}
+
public class FocusHelper {
private static final String TAG = "FocusHelper";
@@ -83,8 +100,6 @@
// Initialize variables.
final ShortcutAndWidgetContainer itemContainer = (ShortcutAndWidgetContainer) v.getParent();
final CellLayout cellLayout = (CellLayout) itemContainer.getParent();
- final int countX = cellLayout.getCountX();
- final int countY = cellLayout.getCountY();
final int iconIndex = itemContainer.indexOfChild(v);
final FolderPagedView pagedView = (FolderPagedView) cellLayout.getParent();
@@ -95,8 +110,8 @@
int[][] matrix = FocusLogic.createSparseMatrix(cellLayout);
// Process focus.
- int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX,
- countY, matrix, iconIndex, pageIndex, pageCount, isLayoutRtl);
+ int newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, iconIndex, pageIndex,
+ pageCount, isLayoutRtl);
if (newIconIndex == FocusLogic.NOOP) {
handleNoopKey(keyCode, v);
return consume;
@@ -113,7 +128,8 @@
pagedView.snapToPage(pageIndex - 1);
child = newParent.getChildAt(
((newIconIndex == FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN)
- ^ newParent.invertLayoutHorizontally()) ? 0 : countX - 1, row);
+ ^ newParent.invertLayoutHorizontally()) ? 0 : matrix.length - 1,
+ row);
}
break;
case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
@@ -127,7 +143,7 @@
newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex - 1);
if (newParent != null) {
pagedView.snapToPage(pageIndex - 1);
- child = newParent.getChildAt(countX - 1, countY - 1);
+ child = newParent.getChildAt(matrix.length - 1, matrix[0].length - 1);
}
break;
case FocusLogic.NEXT_PAGE_FIRST_ITEM:
@@ -202,8 +218,6 @@
final ItemInfo itemInfo = (ItemInfo) v.getTag();
int pageIndex = workspace.getNextPage();
int pageCount = workspace.getChildCount();
- int countX = -1;
- int countY = -1;
int iconIndex = hotseatParent.indexOfChild(v);
int iconRank = ((CellLayout.LayoutParams) hotseatLayout.getShortcutsAndWidgets()
.getChildAt(iconIndex).getLayoutParams()).cellX;
@@ -222,21 +236,15 @@
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 */);
+ matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout,
+ true /* hotseat horizontal */, profile.inv.hotseatAllAppsRank);
iconIndex += iconParent.getChildCount();
- countX = iconLayout.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 */);
+ matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout,
+ false /* hotseat horizontal */, profile.inv.hotseatAllAppsRank);
iconIndex += iconParent.getChildCount();
- countX = iconLayout.getCountX() + hotseatLayout.getCountX();
- countY = iconLayout.getCountY();
parent = iconParent;
} else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT &&
profile.isVerticalBarLayout()) {
@@ -253,14 +261,12 @@
// For other KEYCODE_DPAD_LEFT and KEYCODE_DPAD_RIGHT navigation, do not use the
// matrix extended with hotseat.
matrix = FocusLogic.createSparseMatrix(hotseatLayout);
- countX = hotseatLayout.getCountX();
- countY = hotseatLayout.getCountY();
parent = hotseatParent;
}
// Process the focus.
- int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX,
- countY, matrix, iconIndex, pageIndex, pageCount, Utilities.isRtl(v.getResources()));
+ int newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, iconIndex, pageIndex,
+ pageCount, Utilities.isRtl(v.getResources()));
View newIcon = null;
switch (newIconIndex) {
@@ -289,18 +295,32 @@
case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN:
// Go to the previous page but keep the focus on the same hotseat icon.
workspace.snapToPage(pageIndex - 1);
+ // If the page we are going to is fullscreen, have it take the focus from hotseat.
+ CellLayout prevPage = (CellLayout) workspace.getPageAt(pageIndex - 1);
+ boolean isPrevPageFullscreen = ((CellLayout.LayoutParams) prevPage
+ .getShortcutsAndWidgets().getChildAt(0).getLayoutParams()).isFullscreen;
+ if (isPrevPageFullscreen) {
+ workspace.getPageAt(pageIndex - 1).requestFocus();
+ }
break;
case FocusLogic.NEXT_PAGE_LEFT_COLUMN:
case FocusLogic.NEXT_PAGE_RIGHT_COLUMN:
// Go to the next page but keep the focus on the same hotseat icon.
workspace.snapToPage(pageIndex + 1);
+ // If the page we are going to is fullscreen, have it take the focus from hotseat.
+ CellLayout nextPage = (CellLayout) workspace.getPageAt(pageIndex + 1);
+ boolean isNextPageFullscreen = ((CellLayout.LayoutParams) nextPage
+ .getShortcutsAndWidgets().getChildAt(0).getLayoutParams()).isFullscreen;
+ if (isNextPageFullscreen) {
+ workspace.getPageAt(pageIndex + 1).requestFocus();
+ }
break;
}
if (parent == iconParent && newIconIndex >= iconParent.getChildCount()) {
newIconIndex -= iconParent.getChildCount();
}
if (parent != null) {
- if (newIcon == null && newIconIndex >=0) {
+ if (newIcon == null && newIconIndex >= 0) {
newIcon = parent.getChildAt(newIconIndex);
}
if (newIcon != null) {
@@ -340,8 +360,6 @@
final int iconIndex = parent.indexOfChild(v);
final int pageIndex = workspace.indexOfChild(iconLayout);
final int pageCount = workspace.getChildCount();
- int countX = iconLayout.getCountX();
- int countY = iconLayout.getCountY();
CellLayout hotseatLayout = (CellLayout) hotseat.getChildAt(0);
ShortcutAndWidgetContainer hotseatParent = hotseatLayout.getShortcutsAndWidgets();
@@ -351,16 +369,12 @@
// to take a user to the hotseat. For other dpad navigation, do not use the matrix extended
// 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;
+ matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout,
+ true /* horizontal */, profile.inv.hotseatAllAppsRank);
} 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;
+ matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout,
+ false /* horizontal */, profile.inv.hotseatAllAppsRank);
} else if (isUninstallKeyChord(e)) {
matrix = FocusLogic.createSparseMatrix(iconLayout);
if (UninstallDropTarget.supportsDrop(launcher, itemInfo)) {
@@ -374,9 +388,11 @@
}
// Process the focus.
- int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX,
- countY, matrix, iconIndex, pageIndex, pageCount, Utilities.isRtl(v.getResources()));
+ int newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, iconIndex, pageIndex,
+ pageCount, Utilities.isRtl(v.getResources()));
+ boolean isRtl = Utilities.isRtl(v.getResources());
View newIcon = null;
+ CellLayout workspaceLayout = (CellLayout) workspace.getChildAt(pageIndex);
switch (newIconIndex) {
case FocusLogic.NOOP:
if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
@@ -393,25 +409,35 @@
parent = getCellLayoutChildrenForIndex(workspace, newPageIndex);
if (parent != null) {
iconLayout = (CellLayout) parent.getParent();
- matrix = FocusLogic.createSparseMatrix(iconLayout,
+ matrix = FocusLogic.createSparseMatrixWithPivotColumn(iconLayout,
iconLayout.getCountX(), row);
- newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY,
- matrix, FocusLogic.PIVOT, newPageIndex, pageCount,
- Utilities.isRtl(v.getResources()));
- newIcon = parent.getChildAt(newIconIndex);
+ newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, FocusLogic.PIVOT,
+ newPageIndex, pageCount, Utilities.isRtl(v.getResources()));
+ if (newIconIndex == FocusLogic.NEXT_PAGE_FIRST_ITEM) {
+ newIcon = handleNextPageFirstItem(workspace, hotseatLayout, pageIndex,
+ isRtl);
+ } else if (newIconIndex == FocusLogic.PREVIOUS_PAGE_LAST_ITEM) {
+ newIcon = handlePreviousPageLastItem(workspace, hotseatLayout, pageIndex,
+ isRtl);
+ } else {
+ newIcon = parent.getChildAt(newIconIndex);
+ }
}
break;
case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
- parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
- newIcon = parent.getChildAt(0);
+ workspaceLayout = (CellLayout) workspace.getChildAt(pageIndex - 1);
+ newIcon = getFirstFocusableIconInReadingOrder(workspaceLayout, isRtl);
+ if (newIcon == null) {
+ // Check the hotseat if no focusable item was found on the workspace.
+ newIcon = getFirstFocusableIconInReadingOrder(hotseatLayout, isRtl);
+ workspace.snapToPage(pageIndex - 1);
+ }
break;
case FocusLogic.PREVIOUS_PAGE_LAST_ITEM:
- parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
- newIcon = parent.getChildAt(parent.getChildCount() - 1);
+ newIcon = handlePreviousPageLastItem(workspace, hotseatLayout, pageIndex, isRtl);
break;
case FocusLogic.NEXT_PAGE_FIRST_ITEM:
- parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
- newIcon = parent.getChildAt(0);
+ newIcon = handleNextPageFirstItem(workspace, hotseatLayout, pageIndex, isRtl);
break;
case FocusLogic.NEXT_PAGE_LEFT_COLUMN:
case FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN:
@@ -423,18 +449,33 @@
parent = getCellLayoutChildrenForIndex(workspace, newPageIndex);
if (parent != null) {
iconLayout = (CellLayout) parent.getParent();
- matrix = FocusLogic.createSparseMatrix(iconLayout, -1, row);
- newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY,
- matrix, FocusLogic.PIVOT, newPageIndex, pageCount,
- Utilities.isRtl(v.getResources()));
- newIcon = parent.getChildAt(newIconIndex);
+ matrix = FocusLogic.createSparseMatrixWithPivotColumn(iconLayout, -1, row);
+ newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, FocusLogic.PIVOT,
+ newPageIndex, pageCount, Utilities.isRtl(v.getResources()));
+ if (newIconIndex == FocusLogic.NEXT_PAGE_FIRST_ITEM) {
+ newIcon = handleNextPageFirstItem(workspace, hotseatLayout, pageIndex,
+ isRtl);
+ } else if (newIconIndex == FocusLogic.PREVIOUS_PAGE_LAST_ITEM) {
+ newIcon = handlePreviousPageLastItem(workspace, hotseatLayout, pageIndex,
+ isRtl);
+ } else {
+ newIcon = parent.getChildAt(newIconIndex);
+ }
}
break;
case FocusLogic.CURRENT_PAGE_FIRST_ITEM:
- newIcon = parent.getChildAt(0);
+ newIcon = getFirstFocusableIconInReadingOrder(workspaceLayout, isRtl);
+ if (newIcon == null) {
+ // Check the hotseat if no focusable item was found on the workspace.
+ newIcon = getFirstFocusableIconInReadingOrder(hotseatLayout, isRtl);
+ }
break;
case FocusLogic.CURRENT_PAGE_LAST_ITEM:
- newIcon = parent.getChildAt(parent.getChildCount() - 1);
+ newIcon = getFirstFocusableIconInReverseReadingOrder(workspaceLayout, isRtl);
+ if (newIcon == null) {
+ // Check the hotseat if no focusable item was found on the workspace.
+ newIcon = getFirstFocusableIconInReverseReadingOrder(hotseatLayout, isRtl);
+ }
break;
default:
// current page, some item.
@@ -509,4 +550,63 @@
return (keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL) &&
event.hasModifiers(KeyEvent.META_CTRL_ON);
}
+
+ private static View handlePreviousPageLastItem(Workspace workspace, CellLayout hotseatLayout,
+ int pageIndex, boolean isRtl) {
+ if (pageIndex - 1 < 0) {
+ return null;
+ }
+ CellLayout workspaceLayout = (CellLayout) workspace.getChildAt(pageIndex - 1);
+ View newIcon = getFirstFocusableIconInReverseReadingOrder(workspaceLayout, isRtl);
+ if (newIcon == null) {
+ // Check the hotseat if no focusable item was found on the workspace.
+ newIcon = getFirstFocusableIconInReverseReadingOrder(hotseatLayout,isRtl);
+ workspace.snapToPage(pageIndex - 1);
+ }
+ return newIcon;
+ }
+
+ private static View handleNextPageFirstItem(Workspace workspace, CellLayout hotseatLayout,
+ int pageIndex, boolean isRtl) {
+ if (pageIndex + 1 >= workspace.getPageCount()) {
+ return null;
+ }
+ CellLayout workspaceLayout = (CellLayout) workspace.getChildAt(pageIndex + 1);
+ View newIcon = getFirstFocusableIconInReadingOrder(workspaceLayout, isRtl);
+ if (newIcon == null) {
+ // Check the hotseat if no focusable item was found on the workspace.
+ newIcon = getFirstFocusableIconInReadingOrder(hotseatLayout, isRtl);
+ workspace.snapToPage(pageIndex + 1);
+ }
+ return newIcon;
+ }
+
+ private static View getFirstFocusableIconInReadingOrder(CellLayout cellLayout, boolean isRtl) {
+ View icon;
+ int countX = cellLayout.getCountX();
+ for (int y = 0; y < cellLayout.getCountY(); y++) {
+ int increment = isRtl ? -1 : 1;
+ for (int x = isRtl ? countX - 1 : 0; 0 <= x && x < countX; x += increment) {
+ if ((icon = cellLayout.getChildAt(x, y)) != null && icon.isFocusable()) {
+ return icon;
+ }
+ }
+ }
+ return null;
+ }
+
+ private static View getFirstFocusableIconInReverseReadingOrder(CellLayout cellLayout,
+ boolean isRtl) {
+ View icon;
+ int countX = cellLayout.getCountX();
+ for (int y = cellLayout.getCountY() - 1; y >= 0; y--) {
+ int increment = isRtl ? 1 : -1;
+ for (int x = isRtl ? 0 : countX - 1; 0 <= x && x < countX; x += increment) {
+ if ((icon = cellLayout.getChildAt(x, y)) != null && icon.isFocusable()) {
+ return icon;
+ }
+ }
+ }
+ return null;
+ }
}
diff --git a/src/com/android/launcher3/FocusIndicatorView.java b/src/com/android/launcher3/FocusIndicatorView.java
index ecf93e4..58b38eb 100644
--- a/src/com/android/launcher3/FocusIndicatorView.java
+++ b/src/com/android/launcher3/FocusIndicatorView.java
@@ -30,6 +30,7 @@
// It can be any number >0. The view is resized using scaleX and scaleY.
static final int DEFAULT_LAYOUT_SIZE = 100;
+
private static final float MIN_VISIBLE_ALPHA = 0.2f;
private static final long ANIM_DURATION = 150;
@@ -41,6 +42,7 @@
private View mLastFocusedView;
private boolean mInitiated;
+ private final OnFocusChangeListener mHideIndicatorOnFocusListener;
private Pair<View, Boolean> mPendingCall;
@@ -52,6 +54,16 @@
super(context, attrs);
setAlpha(0);
setBackgroundColor(getResources().getColor(R.color.focused_background));
+
+ mHideIndicatorOnFocusListener = new OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (hasFocus) {
+ endCurrentAnimation();
+ setAlpha(0);
+ }
+ }
+ };
}
@Override
@@ -66,6 +78,13 @@
}
}
+ /**
+ * Sets the alpha of this FocusIndicatorView to 0 when a view with this listener receives focus.
+ */
+ public View.OnFocusChangeListener getHideIndicatorOnFocusListener() {
+ return mHideIndicatorOnFocusListener;
+ }
+
@Override
public void onFocusChange(View v, boolean hasFocus) {
mPendingCall = null;
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 902b6ec..3e83876 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -70,7 +70,7 @@
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/Workspace.java b/src/com/android/launcher3/Workspace.java
index 5073902..94cbc7a 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -651,6 +651,10 @@
parent.removeView(customContent);
}
customScreen.removeAllViews();
+ customContent.setFocusable(true);
+ customContent.setOnKeyListener(new FullscreenKeyEventListener());
+ customContent.setOnFocusChangeListener(mLauncher.mFocusHandler
+ .getHideIndicatorOnFocusListener());
customScreen.addViewToCellLayout(customContent, 0, 0, lp, true);
mCustomContentDescription = description;
diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java
index 2aae3c0..a5498f7 100644
--- a/src/com/android/launcher3/util/FocusLogic.java
+++ b/src/com/android/launcher3/util/FocusLogic.java
@@ -63,6 +63,8 @@
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;
@@ -78,8 +80,11 @@
keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL);
}
- public static int handleKeyEvent(int keyCode, int cntX, int cntY,
- int [][] map, int iconIdx, int pageIndex, int pageCount, boolean isRtl) {
+ public static int handleKeyEvent(int keyCode, int [][] map, int iconIdx, int pageIndex,
+ int pageCount, boolean isRtl) {
+
+ int cntX = map == null ? -1 : map.length;
+ int cntY = map == null ? -1 : map[0].length;
if (DEBUG) {
Log.v(TAG, String.format(
@@ -90,7 +95,7 @@
int newIndex = NOOP;
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_LEFT:
- newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, -1 /*increment*/);
+ newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, -1 /*increment*/, isRtl);
if (!isRtl && newIndex == NOOP && pageIndex > 0) {
newIndex = PREVIOUS_PAGE_RIGHT_COLUMN;
} else if (isRtl && newIndex == NOOP && pageIndex < pageCount - 1) {
@@ -98,7 +103,7 @@
}
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
- newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, 1 /*increment*/);
+ newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, 1 /*increment*/, isRtl);
if (!isRtl && newIndex == NOOP && pageIndex < pageCount - 1) {
newIndex = NEXT_PAGE_LEFT_COLUMN;
} else if (isRtl && newIndex == NOOP && pageIndex > 0) {
@@ -185,23 +190,37 @@
* in portrait orientation. In landscape, [(icon + hotseat) column count x (icon row count)]
*/
// TODO: get rid of the dynamic matrix creation
- public static int[][] createSparseMatrix(CellLayout iconLayout, CellLayout hotseatLayout,
- boolean isHorizontal, int allappsiconRank, boolean includeAllappsicon) {
+ public static int[][] createSparseMatrixWithHotseat(CellLayout iconLayout,
+ CellLayout hotseatLayout, 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 +228,29 @@
}
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) {
@@ -253,7 +270,8 @@
* @param pivotY y coordinate of the focused item in the current page
*/
// TODO: get rid of the dynamic matrix creation
- public static int[][] createSparseMatrix(CellLayout iconLayout, int pivotX, int pivotY) {
+ public static int[][] createSparseMatrixWithPivotColumn(CellLayout iconLayout,
+ int pivotX, int pivotY) {
ViewGroup iconParent = iconLayout.getShortcutsAndWidgets();
@@ -300,7 +318,7 @@
*/
// TODO: add unit tests to verify all permutation.
private static int handleDpadHorizontal(int iconIdx, int cntX, int cntY,
- int[][] matrix, int increment) {
+ int[][] matrix, int increment, boolean isRtl) {
if(matrix == null) {
throw new IllegalStateException("Dpad navigation requires a matrix.");
}
@@ -323,8 +341,9 @@
}
// 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,30 +352,40 @@
// (x2-n, yPos + 2*increment), (x2-n, yPos - 2*increment)
int nextYPos1;
int nextYPos2;
- int i = -1;
+ boolean haveCrossedAllAppsColumn1 = false;
+ boolean haveCrossedAllAppsColumn2 = false;
+ 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) {
+ haveCrossedAllAppsColumn1 = true;
+ }
+ if (inspectMatrix(x, nextYPos2, cntX, cntY, matrix) == ALL_APPS_COLUMN) {
+ haveCrossedAllAppsColumn2 = true;
+ }
+ for (; 0 <= x && x < cntX; x += increment) {
+ int offset1 = haveCrossedAllAppsColumn1 && x < cntX - 1 ? increment : 0;
+ newIconIndex = inspectMatrix(x, nextYPos1 + offset1, cntX, cntY, matrix);
+ if (newIconIndex != NOOP) {
return newIconIndex;
}
- if ((newIconIndex = inspectMatrix(i, nextYPos2, cntX, cntY, matrix)) != NOOP) {
+ int offset2 = haveCrossedAllAppsColumn2 && x < cntX - 1 ? -increment : 0;
+ newIconIndex = inspectMatrix(x, nextYPos2 + offset2, cntX, cntY, matrix);
+ if (newIconIndex != NOOP) {
return newIconIndex;
}
}
}
- // 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)
+ // Rule3: 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 (int y = 0; y < cntY; y++) {
- if ((newIconIndex = inspectMatrix(x, y, cntX, cntY, matrix)) != NOOP) {
- return newIconIndex;
- }
- }
+ if (isRtl) {
+ return increment < 0 ? NEXT_PAGE_FIRST_ITEM : PREVIOUS_PAGE_LAST_ITEM;
}
+ return increment < 0 ? PREVIOUS_PAGE_LAST_ITEM : NEXT_PAGE_FIRST_ITEM;
}
return newIconIndex;
}
@@ -396,8 +425,9 @@
}
// 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 +436,28 @@
// (xPos + 2*increment, y_(2-n))), (xPos - 2*increment, y_(2-n))
int nextXPos1;
int nextXPos2;
- int j = -1;
+ boolean haveCrossedAllAppsColumn1 = false;
+ boolean haveCrossedAllAppsColumn2 = false;
+ 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) {
+ haveCrossedAllAppsColumn1 = true;
+ }
+ if (inspectMatrix(nextXPos2, y, cntX, cntY, matrix) == ALL_APPS_COLUMN) {
+ haveCrossedAllAppsColumn2 = true;
+ }
+ for (; 0 <= y && y < cntY; y = y + increment) {
+ int offset1 = haveCrossedAllAppsColumn1 && y < cntY - 1 ? increment : 0;
+ newIconIndex = inspectMatrix(nextXPos1 + offset1, y, cntX, cntY, matrix);
+ if (newIconIndex != NOOP) {
return newIconIndex;
}
- if ((newIconIndex = inspectMatrix(nextXPos2, j, cntX, cntY, matrix)) != NOOP) {
+ int offset2 = haveCrossedAllAppsColumn2 && y < cntY - 1 ? -increment : 0;
+ newIconIndex = inspectMatrix(nextXPos2 + offset2, y, cntX, cntY, matrix);
+ if (newIconIndex != NOOP) {
return newIconIndex;
}
}
@@ -481,6 +524,7 @@
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 2c2f0d3..a0d17c8 100644
--- a/tests/src/com/android/launcher3/util/FocusLogicTest.java
+++ b/tests/src/com/android/launcher3/util/FocusLogicTest.java
@@ -19,6 +19,7 @@
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.view.KeyEvent;
+import android.view.View;
import com.android.launcher3.util.FocusLogic;
@@ -66,7 +67,7 @@
{-1, -1, -1, -1, -1, -1},
{100, 1, -1, -1, -1, -1},
});
- int i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_RIGHT, 6, 5, map, 100, 1, 2, false);
+ int i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_RIGHT, map, 100, 1, 2, false);
assertEquals(1, i);
}
@@ -78,8 +79,169 @@
{-1, -1, -1, -1, -1, -1},
{100, -1, -1, -1, -1, -1},
});
- int i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_RIGHT, 6, 5, map, 100, 1, 2, false);
+ int i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_RIGHT, map, 100, 1, 2, false);
+ assertEquals(FocusLogic.NEXT_PAGE_FIRST_ITEM, 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, 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, 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, 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, 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, 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, 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, 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, map, 0, 1, 1, true);
+ assertEquals(7, i);
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, map, 1, 1, 1, true);
+ assertEquals(8, i);
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, map, 2, 1, 1, true);
+ assertEquals(9, i);
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, map, 3, 1, 1, true);
+ assertEquals(10, i);
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, map, 4, 1, 1, true);
+ assertEquals(11, i);
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, 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, 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, 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, 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, map, 0, 1, 1, true);
+ assertEquals(1, i);
+ // Test NOT crossing it, if the All Apps button is the only suitable candidate.
+ map = transpose(new int[][]{
+ {-1, 0, -1, -1, -1},
+ {-1, 1, -1, -1, -1},
+ {-11, -11, -11, -11, 4},
+ {-1, 2, -1, -1, -1},
+ {-1, 3, -1, -1, -1},
+ });
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_RIGHT, map, 1, 1, 1, true);
+ assertEquals(4, i);
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_RIGHT, map, 2, 1, 1, true);
+ assertEquals(4, i);
}
/** Transposes the matrix so that we can write it in human-readable format in the tests. */