[key event focus handling] Cleanup/Refactor/Feature
1) Focus navigation handling is refactored to Focus utility class.
New 2 step dpad navigation algorithm is inside Focus class
2) Introduced a map (or matrix) that indicates where sparse icons are located inside a grid.
This enables getting rid of the icon sorting logic which was costly.
3) Unified all the dpad handling logic inside the handleXXKeyEvent methods
4) DOWN/UP key will allow navigation between workspace icons and the hotseat
5) Folder icons allow DOWN/UP to navigate to the title

b/19381790

Change-Id: Id45b3f215ef7c1ca5f99b08e3d721e219298627a
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index e607047..c02d73c 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2015 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.
@@ -17,20 +17,20 @@
 package com.android.launcher3;
 
 import android.content.res.Configuration;
+import android.util.Log;
 import android.view.KeyEvent;
 import android.view.SoundEffectConstants;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ScrollView;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
+import com.android.launcher3.util.FocusLogic;
 
 /**
  * A keyboard listener we set on all the workspace icons.
  */
 class IconKeyEventListener implements View.OnKeyListener {
+    @Override
     public boolean onKey(View v, int keyCode, KeyEvent event) {
         return FocusHelper.handleIconKeyEvent(v, keyCode, event);
     }
@@ -40,6 +40,7 @@
  * A keyboard listener we set on all the workspace icons.
  */
 class FolderKeyEventListener implements View.OnKeyListener {
+    @Override
     public boolean onKey(View v, int keyCode, KeyEvent event) {
         return FocusHelper.handleFolderKeyEvent(v, keyCode, event);
     }
@@ -49,14 +50,337 @@
  * A keyboard listener we set on all the hotseat buttons.
  */
 class HotseatIconKeyEventListener implements View.OnKeyListener {
+    @Override
     public boolean onKey(View v, int keyCode, KeyEvent event) {
-        final Configuration configuration = v.getResources().getConfiguration();
-        return FocusHelper.handleHotseatButtonKeyEvent(v, keyCode, event, configuration.orientation);
+        return FocusHelper.handleHotseatButtonKeyEvent(v, keyCode, event);
     }
 }
 
 public class FocusHelper {
 
+    private static final String TAG = "FocusHelper";
+    private static final boolean DEBUG = false;
+
+    //
+    // Key code handling methods.
+    //
+
+    /**
+     * Handles key events in the all apps screen.
+     */
+    static boolean handleAppsCustomizeKeyEvent(View v, int keyCode, KeyEvent e) {
+        boolean consume = FocusLogic.shouldConsume(keyCode);
+        if (e.getAction() == KeyEvent.ACTION_UP) {
+            return consume;
+        }
+        if (DEBUG) {
+            Log.v(TAG, String.format("Handle ALL APPS keyevent=[%s].",
+                    KeyEvent.keyCodeToString(keyCode)));
+        }
+
+        // Initialize variables.
+        ViewGroup parentLayout;
+        ViewGroup itemContainer;
+        int countX;
+        int countY;
+        if (v.getParent() instanceof ShortcutAndWidgetContainer) {
+            itemContainer = (ViewGroup) v.getParent();
+            parentLayout = (ViewGroup) itemContainer.getParent();
+            countX = ((CellLayout) parentLayout).getCountX();
+            countY = ((CellLayout) parentLayout).getCountY();
+        } else if (v.getParent() instanceof ViewGroup) {
+            itemContainer = parentLayout = (ViewGroup) v.getParent();
+            countX = ((PagedViewGridLayout) parentLayout).getCellCountX();
+            countY = ((PagedViewGridLayout) parentLayout).getCellCountY();
+        } else {
+            throw new IllegalStateException(
+                    "Parent of the focused item inside all apps screen is not a supported type.");
+        }
+        final int iconIndex = itemContainer.indexOfChild(v);
+        final PagedView container = (PagedView) parentLayout.getParent();
+        final int pageIndex = container.indexToPage(container.indexOfChild(parentLayout));
+        final int pageCount = container.getChildCount();
+        ViewGroup newParent = null;
+        View child = null;
+        int[][] matrix = FocusLogic.createFullMatrix(countX, countY, true);
+
+        // Process focus.
+        int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix,
+                iconIndex, pageIndex, pageCount);
+        if (newIconIndex == FocusLogic.NOOP) {
+            return consume;
+        }
+        switch (newIconIndex) {
+            case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
+                newParent = getAppsCustomizePage(container, pageIndex - 1);
+                if (newParent != null) {
+                    container.snapToPage(pageIndex - 1);
+                    child = newParent.getChildAt(0);
+                }
+            case FocusLogic.PREVIOUS_PAGE_LAST_ITEM:
+                newParent = getAppsCustomizePage(container, pageIndex - 1);
+                if (newParent != null) {
+                    container.snapToPage(pageIndex - 1);
+                    child = newParent.getChildAt(newParent.getChildCount() - 1);
+                }
+                break;
+            case FocusLogic.NEXT_PAGE_FIRST_ITEM:
+                newParent = getAppsCustomizePage(container, pageIndex + 1);
+                if (newParent != null) {
+                    container.snapToPage(pageIndex + 1);
+                    child = newParent.getChildAt(0);
+                }
+                break;
+            case FocusLogic.CURRENT_PAGE_FIRST_ITEM:
+                child = container.getChildAt(0);
+                break;
+            case FocusLogic.CURRENT_PAGE_LAST_ITEM:
+                child = itemContainer.getChildAt(itemContainer.getChildCount() - 1);
+                break;
+            default: // Go to some item on the current page.
+                child = itemContainer.getChildAt(newIconIndex);
+                break;
+        }
+        if (child != null) {
+            child.requestFocus();
+            playSoundEffect(keyCode, v);
+        }
+        return consume;
+    }
+
+    /**
+     * Handles key events in the workspace hot seat (bottom of the screen).
+     * <p>Currently we don't special case for the phone UI in different orientations, even though
+     * the hotseat is on the side in landscape mode. This is to ensure that accessibility
+     * consistency is maintained across rotations.
+     */
+    static boolean handleHotseatButtonKeyEvent(View v, int keyCode, KeyEvent e) {
+        boolean consume = FocusLogic.shouldConsume(keyCode);
+        if (e.getAction() == KeyEvent.ACTION_UP || !consume) {
+            return consume;
+        }
+        int orientation = v.getResources().getConfiguration().orientation;
+
+        if (DEBUG) {
+            Log.v(TAG, String.format(
+                    "Handle HOTSEAT BUTTONS keyevent=[%s] on hotseat buttons, orientation=%d",
+                    KeyEvent.keyCodeToString(keyCode), orientation));
+        }
+
+        // Initialize the variables.
+        final ShortcutAndWidgetContainer hotseatParent = (ShortcutAndWidgetContainer) v.getParent();
+        final CellLayout hotseatLayout = (CellLayout) hotseatParent.getParent();
+        Workspace workspace = (Workspace) v.getRootView().findViewById(R.id.workspace);
+        int pageIndex = workspace.getCurrentPage();
+        int pageCount = workspace.getChildCount();
+        int countX, countY;
+        int iconIndex = findIndexOfView(hotseatParent, v);
+
+        final CellLayout iconLayout = (CellLayout) workspace.getChildAt(pageIndex);
+        final ViewGroup iconParent = iconLayout.getShortcutsAndWidgets();
+
+        ViewGroup parent = null;
+        int[][] matrix;
+
+        if (keyCode == KeyEvent.KEYCODE_DPAD_UP &&
+                orientation == Configuration.ORIENTATION_PORTRAIT) {
+            matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation);
+            // TODO: hotseat indexing should be symmetric.
+            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.
+            iconIndex += iconParent.getChildCount();
+            countX = iconLayout.getCountX() + hotseatLayout.getCountX();
+            countY = iconLayout.getCountY();
+            parent = iconParent;
+        } 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();
+
+        }
+
+        // Process the focus.
+        int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix,
+                iconIndex, pageIndex, pageCount);
+
+        if (iconParent.getChildCount() <= newIconIndex &&
+                newIconIndex < iconParent.getChildCount() + hotseatParent.getChildCount()) {
+            newIconIndex -= iconParent.getChildCount();
+        }
+        if (parent != null) {
+            View newIcon = parent.getChildAt(newIconIndex);
+            if (newIcon != null) {
+                newIcon.requestFocus();
+                playSoundEffect(keyCode, v);
+            }
+        }
+        return consume;
+    }
+
+    /**
+     * Handles key events in a workspace containing icons.
+     */
+    static boolean handleIconKeyEvent(View v, int keyCode, KeyEvent e) {
+        boolean consume = FocusLogic.shouldConsume(keyCode);
+        if (e.getAction() == KeyEvent.ACTION_UP || !consume) {
+            return consume;
+        }
+        int orientation = v.getResources().getConfiguration().orientation;
+        if (DEBUG) {
+            Log.v(TAG, String.format("Handle WORKSPACE ICONS keyevent=[%s] orientation=%d",
+                    KeyEvent.keyCodeToString(keyCode), orientation));
+        }
+
+        // Initialize the variables.
+        ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent();
+        final CellLayout layout = (CellLayout) parent.getParent();
+        final Workspace workspace = (Workspace) layout.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);
+        int pageCount = workspace.getChildCount();
+        final int countX = layout.getCountX();
+        int countY = layout.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
+        // 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);
+            countY = countY + 1;
+        } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT &&
+                orientation == Configuration.ORIENTATION_LANDSCAPE) {
+            matrix = FocusLogic.createSparseMatrix(layout, hotseatLayout, orientation);
+            countY = countY + 1;
+        } else {
+            matrix = FocusLogic.createSparseMatrix(layout);
+        }
+
+        // Process the focus.
+        int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix,
+                iconIndex, pageIndex, pageCount);
+        View newIcon = null;
+        switch (newIconIndex) {
+            case FocusLogic.NOOP:
+                if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
+                    newIcon = tabs;
+                }
+                break;
+            case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
+                parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
+                newIcon = parent.getChildAt(0);
+                workspace.snapToPage(pageIndex - 1);
+            case FocusLogic.PREVIOUS_PAGE_LAST_ITEM:
+                parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
+                newIcon = parent.getChildAt(parent.getChildCount() - 1);
+                workspace.snapToPage(pageIndex - 1);
+                break;
+            case FocusLogic.NEXT_PAGE_FIRST_ITEM:
+                parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
+                newIcon = parent.getChildAt(0);
+                workspace.snapToPage(pageIndex - 1);
+                break;
+            case FocusLogic.CURRENT_PAGE_FIRST_ITEM:
+                newIcon = parent.getChildAt(0);
+                break;
+            case FocusLogic.CURRENT_PAGE_LAST_ITEM:
+                newIcon = parent.getChildAt(parent.getChildCount() - 1);
+                break;
+            default:
+                // current page, some item.
+                if (0 <= newIconIndex && newIconIndex < parent.getChildCount()) {
+                    newIcon = parent.getChildAt(newIconIndex);
+                } else if (parent.getChildCount() <= newIconIndex &&
+                        newIconIndex < parent.getChildCount() + hotseatParent.getChildCount()) {
+                    newIcon = hotseatParent.getChildAt(newIconIndex - parent.getChildCount());
+                }
+                break;
+        }
+        if (newIcon != null) {
+            newIcon.requestFocus();
+            playSoundEffect(keyCode, v);
+        }
+        return consume;
+    }
+
+    /**
+     * Handles key events for items in a Folder.
+     */
+    static boolean handleFolderKeyEvent(View v, int keyCode, KeyEvent e) {
+        boolean consume = FocusLogic.shouldConsume(keyCode);
+        if (e.getAction() == KeyEvent.ACTION_UP || !consume) {
+            return consume;
+        }
+        if (DEBUG) {
+            Log.v(TAG, String.format("Handle FOLDER keyevent=[%s].",
+                    KeyEvent.keyCodeToString(keyCode)));
+        }
+
+        // Initialize the variables.
+        ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent();
+        final CellLayout layout = (CellLayout) parent.getParent();
+        final ScrollView scrollView = (ScrollView) layout.getParent();
+        final Folder folder = (Folder) scrollView.getParent();
+        View title = folder.mFolderName;
+        Workspace workspace = (Workspace) v.getRootView().findViewById(R.id.workspace);
+        final int countX = layout.getCountX();
+        final int countY = layout.getCountY();
+        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 */
+                );
+
+        // Process the focus.
+        int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, map, iconIndex,
+                pageIndex, pageCount);
+        View newIcon = null;
+        switch (newIconIndex) {
+            case FocusLogic.NOOP:
+                if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
+                    newIcon = title;
+                }
+                break;
+            case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
+            case FocusLogic.PREVIOUS_PAGE_LAST_ITEM:
+            case FocusLogic.NEXT_PAGE_FIRST_ITEM:
+            case FocusLogic.CURRENT_PAGE_FIRST_ITEM:
+            case FocusLogic.CURRENT_PAGE_LAST_ITEM:
+                if (DEBUG) {
+                    Log.v(TAG, "Page advance handling not supported on folder icons.");
+                }
+                break;
+            default: // current page some item.
+                newIcon = parent.getChildAt(newIconIndex);
+                break;
+        }
+        if (newIcon != null) {
+            newIcon.requestFocus();
+            playSoundEffect(keyCode, v);
+        }
+        return consume;
+    }
+
+    //
+    // Helper methods.
+    //
+
     /**
      * Returns the Viewgroup containing page contents for the page at the index specified.
      */
@@ -70,243 +394,6 @@
     }
 
     /**
-     * Handles key events in a PageViewCellLayout containing PagedViewIcons.
-     */
-    static boolean handleAppsCustomizeKeyEvent(View v, int keyCode, KeyEvent e) {
-        ViewGroup parentLayout;
-        ViewGroup itemContainer;
-        int countX;
-        int countY;
-        if (v.getParent() instanceof ShortcutAndWidgetContainer) {
-            itemContainer = (ViewGroup) v.getParent();
-            parentLayout = (ViewGroup) itemContainer.getParent();
-            countX = ((CellLayout) parentLayout).getCountX();
-            countY = ((CellLayout) parentLayout).getCountY();
-        } else {
-            itemContainer = parentLayout = (ViewGroup) v.getParent();
-            countX = ((PagedViewGridLayout) parentLayout).getCellCountX();
-            countY = ((PagedViewGridLayout) parentLayout).getCellCountY();
-        }
-
-        // Note we have an extra parent because of the
-        // PagedViewCellLayout/PagedViewCellLayoutChildren relationship
-        final PagedView container = (PagedView) parentLayout.getParent();
-        final int iconIndex = itemContainer.indexOfChild(v);
-        final int itemCount = itemContainer.getChildCount();
-        final int pageIndex = ((PagedView) container).indexToPage(container.indexOfChild(parentLayout));
-        final int pageCount = container.getChildCount();
-
-        final int x = iconIndex % countX;
-        final int y = iconIndex / countX;
-
-        final int action = e.getAction();
-        final boolean handleKeyEvent = (action != KeyEvent.ACTION_UP);
-        ViewGroup newParent = null;
-        // Side pages do not always load synchronously, so check before focusing child siblings
-        // willy-nilly
-        View child = null;
-        boolean wasHandled = false;
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_DPAD_LEFT:
-                if (handleKeyEvent) {
-                    // Select the previous icon or the last icon on the previous page
-                    if (iconIndex > 0) {
-                        itemContainer.getChildAt(iconIndex - 1).requestFocus();
-                        v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT);
-                    } else {
-                        if (pageIndex > 0) {
-                            newParent = getAppsCustomizePage(container, pageIndex - 1);
-                            if (newParent != null) {
-                                container.snapToPage(pageIndex - 1);
-                                child = newParent.getChildAt(newParent.getChildCount() - 1);
-                                if (child != null) {
-                                    child.requestFocus();
-                                    v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT);
-                                }
-                            }
-                        }
-                    }
-                }
-                wasHandled = true;
-                break;
-            case KeyEvent.KEYCODE_DPAD_RIGHT:
-                if (handleKeyEvent) {
-                    // Select the next icon or the first icon on the next page
-                    if (iconIndex < (itemCount - 1)) {
-                        itemContainer.getChildAt(iconIndex + 1).requestFocus();
-                        v.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT);
-                    } else {
-                        if (pageIndex < (pageCount - 1)) {
-                            newParent = getAppsCustomizePage(container, pageIndex + 1);
-                            if (newParent != null) {
-                                container.snapToPage(pageIndex + 1);
-                                child = newParent.getChildAt(0);
-                                if (child != null) {
-                                    child.requestFocus();
-                                    v.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT);
-                                }
-                            }
-                        }
-                    }
-                }
-                wasHandled = true;
-                break;
-            case KeyEvent.KEYCODE_DPAD_UP:
-                if (handleKeyEvent) {
-                    // Select the closest icon in the previous row, otherwise select the tab bar
-                    if (y > 0) {
-                        int newiconIndex = ((y - 1) * countX) + x;
-                        itemContainer.getChildAt(newiconIndex).requestFocus();
-                        v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
-                    }
-                }
-                wasHandled = true;
-                break;
-            case KeyEvent.KEYCODE_DPAD_DOWN:
-                if (handleKeyEvent) {
-                    // Select the closest icon in the next row, otherwise do nothing
-                    if (y < (countY - 1)) {
-                        int newiconIndex = Math.min(itemCount - 1, ((y + 1) * countX) + x);
-                        int newIconY = newiconIndex / countX;
-                        if (newIconY != y) {
-                            itemContainer.getChildAt(newiconIndex).requestFocus();
-                            v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
-                        }
-                    }
-                }
-                wasHandled = true;
-                break;
-            case KeyEvent.KEYCODE_PAGE_UP:
-                if (handleKeyEvent) {
-                    // Select the first icon on the previous page, or the first icon on this page
-                    // if there is no previous page
-                    if (pageIndex > 0) {
-                        newParent = getAppsCustomizePage(container, pageIndex - 1);
-                        if (newParent != null) {
-                            container.snapToPage(pageIndex - 1);
-                            child = newParent.getChildAt(0);
-                            if (child != null) {
-                                child.requestFocus();
-                                v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
-                            }
-                        }
-                    } else {
-                        itemContainer.getChildAt(0).requestFocus();
-                        v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
-                    }
-                }
-                wasHandled = true;
-                break;
-            case KeyEvent.KEYCODE_PAGE_DOWN:
-                if (handleKeyEvent) {
-                    // Select the first icon on the next page, or the last icon on this page
-                    // if there is no next page
-                    if (pageIndex < (pageCount - 1)) {
-                        newParent = getAppsCustomizePage(container, pageIndex + 1);
-                        if (newParent != null) {
-                            container.snapToPage(pageIndex + 1);
-                            child = newParent.getChildAt(0);
-                            if (child != null) {
-                                child.requestFocus();
-                                v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
-                            }
-                        }
-                    } else {
-                        itemContainer.getChildAt(itemCount - 1).requestFocus();
-                        v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
-                    }
-                }
-                wasHandled = true;
-                break;
-            case KeyEvent.KEYCODE_MOVE_HOME:
-                if (handleKeyEvent) {
-                    // Select the first icon on this page
-                    itemContainer.getChildAt(0).requestFocus();
-                    v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
-                }
-                wasHandled = true;
-                break;
-            case KeyEvent.KEYCODE_MOVE_END:
-                if (handleKeyEvent) {
-                    // Select the last icon on this page
-                    itemContainer.getChildAt(itemCount - 1).requestFocus();
-                    v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
-                }
-                wasHandled = true;
-                break;
-            default: break;
-        }
-        return wasHandled;
-    }
-
-    /**
-     * Handles key events in the workspace hotseat (bottom of the screen).
-     */
-    static boolean handleHotseatButtonKeyEvent(View v, int keyCode, KeyEvent e, int orientation) {
-        ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent();
-        final CellLayout layout = (CellLayout) parent.getParent();
-
-        // NOTE: currently we don't special case for the phone UI in different
-        // orientations, even though the hotseat is on the side in landscape mode. This
-        // is to ensure that accessibility consistency is maintained across rotations.
-        final int action = e.getAction();
-        final boolean handleKeyEvent = (action != KeyEvent.ACTION_UP);
-        boolean wasHandled = false;
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_DPAD_LEFT:
-                if (handleKeyEvent) {
-                    ArrayList<View> views = getCellLayoutChildrenSortedSpatially(layout, parent);
-                    int myIndex = views.indexOf(v);
-                    // Select the previous button, otherwise do nothing
-                    if (myIndex > 0) {
-                        views.get(myIndex - 1).requestFocus();
-                        v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT);
-                    }
-                }
-                wasHandled = true;
-                break;
-            case KeyEvent.KEYCODE_DPAD_RIGHT:
-                if (handleKeyEvent) {
-                    ArrayList<View> views = getCellLayoutChildrenSortedSpatially(layout, parent);
-                    int myIndex = views.indexOf(v);
-                    // Select the next button, otherwise do nothing
-                    if (myIndex < views.size() - 1) {
-                        views.get(myIndex + 1).requestFocus();
-                        v.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT);
-                    }
-                }
-                wasHandled = true;
-                break;
-            case KeyEvent.KEYCODE_DPAD_UP:
-                if (handleKeyEvent) {
-                    final Workspace workspace = (Workspace)
-                            v.getRootView().findViewById(R.id.workspace);
-                    if (workspace != null) {
-                        int pageIndex = workspace.getCurrentPage();
-                        CellLayout topLayout = (CellLayout) workspace.getChildAt(pageIndex);
-                        ShortcutAndWidgetContainer children = topLayout.getShortcutsAndWidgets();
-                        final View newIcon = getIconInDirection(layout, children, -1, 1);
-                        // Select the first bubble text view in the current page of the workspace
-                        if (newIcon != null) {
-                            newIcon.requestFocus();
-                            v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
-                        } else {
-                            workspace.requestFocus();
-                        }
-                    }
-                }
-                wasHandled = true;
-                break;
-            case KeyEvent.KEYCODE_DPAD_DOWN:
-                // Do nothing
-                wasHandled = true;
-                break;
-            default: break;
-        }
-        return wasHandled;
-    }
-
-    /**
      * Private helper method to get the CellLayoutChildren given a CellLayout index.
      */
     private static ShortcutAndWidgetContainer getCellLayoutChildrenForIndex(
@@ -315,359 +402,38 @@
         return parent.getShortcutsAndWidgets();
     }
 
-    /**
-     * Private helper method to sort all the CellLayout children in order of their (x,y) spatially
-     * from top left to bottom right.
-     */
-    private static ArrayList<View> getCellLayoutChildrenSortedSpatially(CellLayout layout,
-            ViewGroup parent) {
-        // First we order each the CellLayout children by their x,y coordinates
-        final int cellCountX = layout.getCountX();
-        final int count = parent.getChildCount();
-        ArrayList<View> views = new ArrayList<View>();
-        for (int j = 0; j < count; ++j) {
-            views.add(parent.getChildAt(j));
-        }
-        Collections.sort(views, new Comparator<View>() {
-            @Override
-            public int compare(View lhs, View rhs) {
-                CellLayout.LayoutParams llp = (CellLayout.LayoutParams) lhs.getLayoutParams();
-                CellLayout.LayoutParams rlp = (CellLayout.LayoutParams) rhs.getLayoutParams();
-                int lvIndex = (llp.cellY * cellCountX) + llp.cellX;
-                int rvIndex = (rlp.cellY * cellCountX) + rlp.cellX;
-                return lvIndex - rvIndex;
-            }
-        });
-        return views;
-    }
-    /**
-     * Private helper method to find the index of the next BubbleTextView or FolderIcon in the 
-     * direction delta.
-     * 
-     * @param delta either -1 or 1 depending on the direction we want to search
-     */
-    private static View findIndexOfIcon(ArrayList<View> views, int i, int delta) {
-        // Then we find the next BubbleTextView offset by delta from i
-        final int count = views.size();
-        int newI = i + delta;
-        while (0 <= newI && newI < count) {
-            View newV = views.get(newI);
-            if (newV instanceof BubbleTextView || newV instanceof FolderIcon) {
-                return newV;
-            }
-            newI += delta;
-        }
-        return null;
-    }
-    private static View getIconInDirection(CellLayout layout, ViewGroup parent, int i,
-            int delta) {
-        final ArrayList<View> views = getCellLayoutChildrenSortedSpatially(layout, parent);
-        return findIndexOfIcon(views, i, delta);
-    }
-    private static View getIconInDirection(CellLayout layout, ViewGroup parent, View v,
-            int delta) {
-        final ArrayList<View> views = getCellLayoutChildrenSortedSpatially(layout, parent);
-        return findIndexOfIcon(views, views.indexOf(v), delta);
-    }
-    /**
-     * Private helper method to find the next closest BubbleTextView or FolderIcon in the direction 
-     * delta on the next line.
-     * 
-     * @param delta either -1 or 1 depending on the line and direction we want to search
-     */
-    private static View getClosestIconOnLine(CellLayout layout, ViewGroup parent, View v,
-            int lineDelta) {
-        final ArrayList<View> views = getCellLayoutChildrenSortedSpatially(layout, parent);
-        final CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams();
-        final int cellCountY = layout.getCountY();
-        final int row = lp.cellY;
-        final int newRow = row + lineDelta;
-        if (0 <= newRow && newRow < cellCountY) {
-            float closestDistance = Float.MAX_VALUE;
-            int closestIndex = -1;
-            int index = views.indexOf(v);
-            int endIndex = (lineDelta < 0) ? -1 : views.size();
-            while (index != endIndex) {
-                View newV = views.get(index);
-                CellLayout.LayoutParams tmpLp = (CellLayout.LayoutParams) newV.getLayoutParams();
-                boolean satisfiesRow = (lineDelta < 0) ? (tmpLp.cellY < row) : (tmpLp.cellY > row);
-                if (satisfiesRow &&
-                        (newV instanceof BubbleTextView || newV instanceof FolderIcon)) {
-                    float tmpDistance = (float) Math.sqrt(Math.pow(tmpLp.cellX - lp.cellX, 2) +
-                            Math.pow(tmpLp.cellY - lp.cellY, 2));
-                    if (tmpDistance < closestDistance) {
-                        closestIndex = index;
-                        closestDistance = tmpDistance;
-                    }
-                }
-                if (index <= endIndex) {
-                    ++index;
-                } else {
-                    --index;
-                }
-            }
-            if (closestIndex > -1) {
-                return views.get(closestIndex);
+    private static int findIndexOfView(ViewGroup parent, View v) {
+        for (int i = 0; i < parent.getChildCount(); i++) {
+            if (v != null && v.equals(parent.getChildAt(i))) {
+                return i;
             }
         }
-        return null;
+        return -1;
     }
 
     /**
-     * Handles key events in a Workspace containing.
+     * Helper method to be used for playing sound effects.
      */
-    static boolean handleIconKeyEvent(View v, int keyCode, KeyEvent e) {
-        ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent();
-        final CellLayout layout = (CellLayout) parent.getParent();
-        final Workspace workspace = (Workspace) layout.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);
-        int pageCount = workspace.getChildCount();
-
-        final int action = e.getAction();
-        final boolean handleKeyEvent = (action != KeyEvent.ACTION_UP);
-        boolean wasHandled = false;
+    private static void playSoundEffect(int keyCode, View v) {
         switch (keyCode) {
             case KeyEvent.KEYCODE_DPAD_LEFT:
-                if (handleKeyEvent) {
-                    // Select the previous icon or the last icon on the previous page if possible
-                    View newIcon = getIconInDirection(layout, parent, v, -1);
-                    if (newIcon != null) {
-                        newIcon.requestFocus();
-                        v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT);
-                    } else {
-                        if (pageIndex > 0) {
-                            parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
-                            newIcon = getIconInDirection(layout, parent,
-                                    parent.getChildCount(), -1);
-                            if (newIcon != null) {
-                                newIcon.requestFocus();
-                            } else {
-                                // Snap to the previous page
-                                workspace.snapToPage(pageIndex - 1);
-                            }
-                            v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT);
-                        }
-                    }
-                }
-                wasHandled = true;
+                v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT);
                 break;
             case KeyEvent.KEYCODE_DPAD_RIGHT:
-                if (handleKeyEvent) {
-                    // Select the next icon or the first icon on the next page if possible
-                    View newIcon = getIconInDirection(layout, parent, v, 1);
-                    if (newIcon != null) {
-                        newIcon.requestFocus();
-                        v.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT);
-                    } else {
-                        if (pageIndex < (pageCount - 1)) {
-                            parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
-                            newIcon = getIconInDirection(layout, parent, -1, 1);
-                            if (newIcon != null) {
-                                newIcon.requestFocus();
-                            } else {
-                                // Snap to the next page
-                                workspace.snapToPage(pageIndex + 1);
-                            }
-                            v.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT);
-                        }
-                    }
-                }
-                wasHandled = true;
-                break;
-            case KeyEvent.KEYCODE_DPAD_UP:
-                if (handleKeyEvent) {
-                    // Select the closest icon in the previous line, otherwise select the tab bar
-                    View newIcon = getClosestIconOnLine(layout, parent, v, -1);
-                    if (newIcon != null) {
-                        newIcon.requestFocus();
-                        wasHandled = true;
-                    } else {
-                        tabs.requestFocus();
-                    }
-                    v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
-                }
+                v.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT);
                 break;
             case KeyEvent.KEYCODE_DPAD_DOWN:
-                if (handleKeyEvent) {
-                    // Select the closest icon in the next line, otherwise select the button bar
-                    View newIcon = getClosestIconOnLine(layout, parent, v, 1);
-                    if (newIcon != null) {
-                        newIcon.requestFocus();
-                        v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
-                        wasHandled = true;
-                    } else if (hotseat != null) {
-                        hotseat.requestFocus();
-                        v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
-                    }
-                }
-                break;
-            case KeyEvent.KEYCODE_PAGE_UP:
-                if (handleKeyEvent) {
-                    // Select the first icon on the previous page or the first icon on this page
-                    // if there is no previous page
-                    if (pageIndex > 0) {
-                        parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
-                        View newIcon = getIconInDirection(layout, parent, -1, 1);
-                        if (newIcon != null) {
-                            newIcon.requestFocus();
-                        } else {
-                            // Snap to the previous page
-                            workspace.snapToPage(pageIndex - 1);
-                        }
-                        v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
-                    } else {
-                        View newIcon = getIconInDirection(layout, parent, -1, 1);
-                        if (newIcon != null) {
-                            newIcon.requestFocus();
-                            v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
-                        }
-                    }
-                }
-                wasHandled = true;
-                break;
             case KeyEvent.KEYCODE_PAGE_DOWN:
-                if (handleKeyEvent) {
-                    // Select the first icon on the next page or the last icon on this page
-                    // if there is no previous page
-                    if (pageIndex < (pageCount - 1)) {
-                        parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
-                        View newIcon = getIconInDirection(layout, parent, -1, 1);
-                        if (newIcon != null) {
-                            newIcon.requestFocus();
-                        } else {
-                            // Snap to the next page
-                            workspace.snapToPage(pageIndex + 1);
-                        }
-                        v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
-                    } else {
-                        View newIcon = getIconInDirection(layout, parent,
-                                parent.getChildCount(), -1);
-                        if (newIcon != null) {
-                            newIcon.requestFocus();
-                            v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
-                        }
-                    }
-                }
-                wasHandled = true;
-                break;
-            case KeyEvent.KEYCODE_MOVE_HOME:
-                if (handleKeyEvent) {
-                    // Select the first icon on this page
-                    View newIcon = getIconInDirection(layout, parent, -1, 1);
-                    if (newIcon != null) {
-                        newIcon.requestFocus();
-                        v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
-                    }
-                }
-                wasHandled = true;
-                break;
             case KeyEvent.KEYCODE_MOVE_END:
-                if (handleKeyEvent) {
-                    // Select the last icon on this page
-                    View newIcon = getIconInDirection(layout, parent,
-                            parent.getChildCount(), -1);
-                    if (newIcon != null) {
-                        newIcon.requestFocus();
-                        v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
-                    }
-                }
-                wasHandled = true;
-                break;
-            default: break;
-        }
-        return wasHandled;
-    }
-
-    /**
-     * Handles key events for items in a Folder.
-     */
-    static boolean handleFolderKeyEvent(View v, int keyCode, KeyEvent e) {
-        ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent();
-        final CellLayout layout = (CellLayout) parent.getParent();
-        final ScrollView scrollView = (ScrollView) layout.getParent();
-        final Folder folder = (Folder) scrollView.getParent();
-        View title = folder.mFolderName;
-
-        final int action = e.getAction();
-        final boolean handleKeyEvent = (action != KeyEvent.ACTION_UP);
-        boolean wasHandled = false;
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_DPAD_LEFT:
-                if (handleKeyEvent) {
-                    // Select the previous icon
-                    View newIcon = getIconInDirection(layout, parent, v, -1);
-                    if (newIcon != null) {
-                        newIcon.requestFocus();
-                        v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT);
-                    }
-                }
-                wasHandled = true;
-                break;
-            case KeyEvent.KEYCODE_DPAD_RIGHT:
-                if (handleKeyEvent) {
-                    // Select the next icon
-                    View newIcon = getIconInDirection(layout, parent, v, 1);
-                    if (newIcon != null) {
-                        newIcon.requestFocus();
-                    } else {
-                        title.requestFocus();
-                    }
-                    v.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT);
-                }
-                wasHandled = true;
+                v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
                 break;
             case KeyEvent.KEYCODE_DPAD_UP:
-                if (handleKeyEvent) {
-                    // Select the closest icon in the previous line
-                    View newIcon = getClosestIconOnLine(layout, parent, v, -1);
-                    if (newIcon != null) {
-                        newIcon.requestFocus();
-                        v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
-                    }
-                }
-                wasHandled = true;
-                break;
-            case KeyEvent.KEYCODE_DPAD_DOWN:
-                if (handleKeyEvent) {
-                    // Select the closest icon in the next line
-                    View newIcon = getClosestIconOnLine(layout, parent, v, 1);
-                    if (newIcon != null) {
-                        newIcon.requestFocus();
-                    } else {
-                        title.requestFocus();
-                    }
-                    v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
-                }
-                wasHandled = true;
-                break;
+            case KeyEvent.KEYCODE_PAGE_UP:
             case KeyEvent.KEYCODE_MOVE_HOME:
-                if (handleKeyEvent) {
-                    // Select the first icon on this page
-                    View newIcon = getIconInDirection(layout, parent, -1, 1);
-                    if (newIcon != null) {
-                        newIcon.requestFocus();
-                        v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
-                    }
-                }
-                wasHandled = true;
+                v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
                 break;
-            case KeyEvent.KEYCODE_MOVE_END:
-                if (handleKeyEvent) {
-                    // Select the last icon on this page
-                    View newIcon = getIconInDirection(layout, parent,
-                            parent.getChildCount(), -1);
-                    if (newIcon != null) {
-                        newIcon.requestFocus();
-                        v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
-                    }
-                }
-                wasHandled = true;
+            default:
                 break;
-            default: break;
         }
-        return wasHandled;
     }
 }