[key event focus handling] (1)hotseat <-> icon now symmetric, (2)support DEL keycode

TL;DR; (1) Key event navigation from and to the hotseat and icons in the workspace
is now symmetric. Since there is one more icon in the hotseat, only left
N-1 icon navigation was symmetric.

(2) KeyEvent.KEYCODE_DEL and KeyEvent.KEYCODE_FORWARD_DEL can now delete icons
from the workspace. The focus move to the previous icon where the focus
traveled from.

Also contains minor styling and indexing issues.

Bug: 15408321
Bug: 19381790

Change-Id: I16cbcb2693e92eebb830997d01c0bf674073dd51
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index c02d73c..bdfd7b2 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -170,54 +170,68 @@
         // Initialize the variables.
         final ShortcutAndWidgetContainer hotseatParent = (ShortcutAndWidgetContainer) v.getParent();
         final CellLayout hotseatLayout = (CellLayout) hotseatParent.getParent();
+        Hotseat hotseat = (Hotseat) hotseatLayout.getParent();
+
         Workspace workspace = (Workspace) v.getRootView().findViewById(R.id.workspace);
         int pageIndex = workspace.getCurrentPage();
         int pageCount = workspace.getChildCount();
-        int countX, countY;
+        int countX = -1;
+        int countY = -1;
         int iconIndex = findIndexOfView(hotseatParent, v);
 
         final CellLayout iconLayout = (CellLayout) workspace.getChildAt(pageIndex);
         final ViewGroup iconParent = iconLayout.getShortcutsAndWidgets();
 
         ViewGroup parent = null;
-        int[][] matrix;
+        int[][] matrix = null;
 
         if (keyCode == KeyEvent.KEYCODE_DPAD_UP &&
                 orientation == Configuration.ORIENTATION_PORTRAIT) {
-            matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation);
-            // TODO: hotseat indexing should be symmetric.
+            matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation,
+                    hotseat.getAllAppsButtonRank(), true /* include all apps icon */);
             iconIndex += iconParent.getChildCount();
             countX = iconLayout.getCountX();
             countY = iconLayout.getCountY() + hotseatLayout.getCountY();
             parent = iconParent;
         } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT &&
                 orientation == Configuration.ORIENTATION_LANDSCAPE) {
-            matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation);
-            // TODO: hotseat indexing should be symmetric.
+            matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation,
+                    hotseat.getAllAppsButtonRank(), true /* include all apps icon */);
             iconIndex += iconParent.getChildCount();
             countX = iconLayout.getCountX() + hotseatLayout.getCountX();
             countY = iconLayout.getCountY();
             parent = iconParent;
-        } else {
+        } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT &&
+                orientation == Configuration.ORIENTATION_LANDSCAPE) {
+            keyCode = KeyEvent.KEYCODE_PAGE_DOWN;
+        }else {
             // For other KEYCODE_DPAD_LEFT and KEYCODE_DPAD_RIGHT navigation, do not use the
             // matrix extended with hotseat.
             matrix = FocusLogic.createSparseMatrix(hotseatLayout);
-            parent = hotseatParent;
             countX = hotseatLayout.getCountX();
             countY = hotseatLayout.getCountY();
-
+            parent = hotseatParent;
         }
 
         // Process the focus.
         int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix,
                 iconIndex, pageIndex, pageCount);
 
-        if (iconParent.getChildCount() <= newIconIndex &&
-                newIconIndex < iconParent.getChildCount() + hotseatParent.getChildCount()) {
+        View newIcon = null;
+        if (newIconIndex == FocusLogic.NEXT_PAGE_FIRST_ITEM) {
+            parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
+            newIcon = parent.getChildAt(0);
+            // TODO(hyunyoungs): handle cases where the child is not an icon but
+            // a folder or a widget.
+            workspace.snapToPage(pageIndex + 1);
+        }
+        if (parent == iconParent && newIconIndex >= iconParent.getChildCount()) {
             newIconIndex -= iconParent.getChildCount();
         }
         if (parent != null) {
-            View newIcon = parent.getChildAt(newIconIndex);
+            if (newIcon == null && newIconIndex >=0) {
+                newIcon = parent.getChildAt(newIconIndex);
+            }
             if (newIcon != null) {
                 newIcon.requestFocus();
                 playSoundEffect(keyCode, v);
@@ -242,34 +256,39 @@
 
         // Initialize the variables.
         ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent();
-        final CellLayout layout = (CellLayout) parent.getParent();
-        final Workspace workspace = (Workspace) layout.getParent();
+        final CellLayout iconLayout = (CellLayout) parent.getParent();
+        final Workspace workspace = (Workspace) iconLayout.getParent();
         final ViewGroup launcher = (ViewGroup) workspace.getParent();
         final ViewGroup tabs = (ViewGroup) launcher.findViewById(R.id.search_drop_target_bar);
-        final ViewGroup hotseat = (ViewGroup) launcher.findViewById(R.id.hotseat);
-        int pageIndex = workspace.indexOfChild(layout);
+        final Hotseat hotseat = (Hotseat) launcher.findViewById(R.id.hotseat);
+        int pageIndex = workspace.indexOfChild(iconLayout);
         int pageCount = workspace.getChildCount();
-        final int countX = layout.getCountX();
-        int countY = layout.getCountY();
+        int countX = iconLayout.getCountX();
+        int countY = iconLayout.getCountY();
         final int iconIndex = findIndexOfView(parent, v);
 
         CellLayout hotseatLayout = (CellLayout) hotseat.getChildAt(0);
         ShortcutAndWidgetContainer hotseatParent = hotseatLayout.getShortcutsAndWidgets();
         int[][] matrix;
 
-        // KEYCODE_DPAD_DOWN in portrait (KEYCODE_DPAD_LEFT in landscape) is the only key allowed
+        // KEYCODE_DPAD_DOWN in portrait (KEYCODE_DPAD_RIGHT in landscape) is the only key allowed
         // 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 &&
                 orientation == Configuration.ORIENTATION_PORTRAIT) {
-            matrix = FocusLogic.createSparseMatrix(layout, hotseatLayout, orientation);
+            matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation,
+                    hotseat.getAllAppsButtonRank(), false /* all apps icon is ignored */);
             countY = countY + 1;
-        } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT &&
+        } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT &&
                 orientation == Configuration.ORIENTATION_LANDSCAPE) {
-            matrix = FocusLogic.createSparseMatrix(layout, hotseatLayout, orientation);
-            countY = countY + 1;
+            matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation,
+                    hotseat.getAllAppsButtonRank(), false /* all apps icon is ignored */);
+            countX = countX + 1;
+        } else if (keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL) {
+            workspace.removeWorkspaceItem(v);
+            return consume;
         } else {
-            matrix = FocusLogic.createSparseMatrix(layout);
+            matrix = FocusLogic.createSparseMatrix(iconLayout);
         }
 
         // Process the focus.
@@ -294,7 +313,7 @@
             case FocusLogic.NEXT_PAGE_FIRST_ITEM:
                 parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
                 newIcon = parent.getChildAt(0);
-                workspace.snapToPage(pageIndex - 1);
+                workspace.snapToPage(pageIndex + 1);
                 break;
             case FocusLogic.CURRENT_PAGE_FIRST_ITEM:
                 newIcon = parent.getChildAt(0);
@@ -344,8 +363,7 @@
         final int iconIndex = findIndexOfView(parent, v);
         int pageIndex = workspace.indexOfChild(layout);
         int pageCount = workspace.getChildCount();
-        int[][] map = FocusLogic.createFullMatrix(countX, countY, true /* incremental order index */
-                );
+        int[][] map = FocusLogic.createFullMatrix(countX, countY, true /* incremental order */);
 
         // Process the focus.
         int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, map, iconIndex,
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 289b08b..bd6c21a 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -83,13 +83,24 @@
     int getOrderInHotseat(int x, int y) {
         return hasVerticalHotseat() ? (mContent.getCountY() - y - 1) : x;
     }
+
     /* Get the orientation specific coordinates given an invariant order in the hotseat. */
     int getCellXFromOrder(int rank) {
         return hasVerticalHotseat() ? 0 : rank;
     }
+
     int getCellYFromOrder(int rank) {
         return hasVerticalHotseat() ? (mContent.getCountY() - (rank + 1)) : 0;
     }
+
+    public int getAllAppsButtonRank() {
+        if (LauncherAppState.isDisableAllApps()) {
+            return -1;
+        } else {
+            return mAllAppsButtonRank;
+        }
+    }
+
     public boolean isAllAppsButtonRank(int rank) {
         if (LauncherAppState.isDisableAllApps()) {
             return false;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 1a4afe8..4e16a45 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -4231,6 +4231,9 @@
         mDragInfo = null;
     }
 
+    /**
+     * For opposite operation. See {@link #addInScreen}.
+     */
     public void removeWorkspaceItem(View v) {
         CellLayout parentCell = getParentCellLayoutForView(v);
         if (parentCell != null) {
diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java
index 23375dc..c0730d9 100644
--- a/src/com/android/launcher3/util/FocusLogic.java
+++ b/src/com/android/launcher3/util/FocusLogic.java
@@ -47,10 +47,13 @@
 
     // Item and page index related constant used by {@link #handleKeyEvent}.
     public static final int NOOP = -1;
+
     public static final int PREVIOUS_PAGE_FIRST_ITEM    = -2;
     public static final int PREVIOUS_PAGE_LAST_ITEM     = -3;
+
     public static final int CURRENT_PAGE_FIRST_ITEM     = -4;
     public static final int CURRENT_PAGE_LAST_ITEM      = -5;
+
     public static final int NEXT_PAGE_FIRST_ITEM        = -6;
 
     // Matrix related constant.
@@ -63,7 +66,8 @@
         if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT ||
                 keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN ||
                 keyCode == KeyEvent.KEYCODE_MOVE_HOME || keyCode == KeyEvent.KEYCODE_MOVE_END ||
-                keyCode == KeyEvent.KEYCODE_PAGE_UP || keyCode == KeyEvent.KEYCODE_PAGE_DOWN) {
+                keyCode == KeyEvent.KEYCODE_PAGE_UP || keyCode == KeyEvent.KEYCODE_PAGE_DOWN ||
+                keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL) {
             return true;
         }
         return false;
@@ -175,9 +179,9 @@
      * The size of the returning matrix is [icon column count x (icon + hotseat row count)]
      * in portrait orientation. In landscape, [(icon + hotseat) column count x (icon row count)]
      */
- // TODO: get rid of the dynamic matrix creation
+    // TODO: get rid of the dynamic matrix creation
     public static int[][] createSparseMatrix(CellLayout iconLayout, CellLayout hotseatLayout,
-            int orientation) {
+            int orientation, int allappsiconRank, boolean includeAllappsicon) {
 
         ViewGroup iconParent = iconLayout.getShortcutsAndWidgets();
         ViewGroup hotseatParent = hotseatLayout.getShortcutsAndWidgets();
@@ -203,22 +207,27 @@
         }
 
         // Iterate thru the children of the bottom parent
-        for(int i = 0; i < hotseatParent.getChildCount(); i++) {
-            // If the hotseat view group contains more items than topColumnCnt, then just
-            // discard them.
-            // TODO: make this more elegant. (look at DynamicGrid)
+        // 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 (orientation == Configuration.ORIENTATION_PORTRAIT) {
                 int cx = ((CellLayout.LayoutParams)
                         hotseatParent.getChildAt(i).getLayoutParams()).cellX;
-                if (cx < iconLayout.getCountX()) {
-                    matrix[cx][iconLayout.getCountY()] = iconParent.getChildCount() + i;
+                if ((includeAllappsicon && cx >= allappsiconRank) ||
+                        (!includeAllappsicon && cx > allappsiconRank)) {
+                        delta = -1;
                 }
+                matrix[cx + delta][iconLayout.getCountY()] = iconParent.getChildCount() + i;
             } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
                 int cy = ((CellLayout.LayoutParams)
                         hotseatParent.getChildAt(i).getLayoutParams()).cellY;
-                if (cy < iconLayout.getCountY()) {
-                    matrix[iconLayout.getCountX()][cy] = iconParent.getChildCount() + i;
+                if ((includeAllappsicon && cy >= allappsiconRank) ||
+                        (!includeAllappsicon && cy > allappsiconRank)) {
+                        delta = -1;
                 }
+                matrix[iconLayout.getCountX()][cy + delta] = iconParent.getChildCount() + i;
             }
         }
         if (DEBUG) {
@@ -266,12 +275,7 @@
 
         // Rule1: check first in the horizontal direction
         for (int i = xPos + increment; 0 <= i && i < cntX; i = i + increment) {
-            if (DEBUG) {
-                Log.v(TAG, String.format("\t\tsearch: \t[x, y]=[%d, %d] iconIndex=%d",
-                        i, yPos, matrix[i][yPos]));
-            }
-            if (matrix[i][yPos] != -1) {
-                newIconIndex = matrix[i][yPos];
+            if ((newIconIndex = inspectMatrix(i, yPos, cntX, cntY, matrix)) != NOOP) {
                 return newIconIndex;
             }
         }
@@ -332,12 +336,7 @@
 
         // Rule1: check first in the dpad direction
         for (int j = yPos + increment; 0 <= j && j <cntY && 0 <= j; j = j + increment) {
-            if (DEBUG) {
-                Log.v(TAG, String.format("\t\tsearch: \t[x, y]=[%d, %d] iconIndex=%d",
-                        xPos, j, matrix[xPos][j]));
-            }
-            if (matrix[xPos][j] != -1) {
-                newIconIndex = matrix[xPos][j];
+            if ((newIconIndex = inspectMatrix(xPos, j, cntX, cntY, matrix)) != NOOP) {
                 return newIconIndex;
             }
         }