Merge "Fixing drag-n-drop for folders in hotseat." into ub-launcher3-burnaby
diff --git a/WallpaperPicker/src/com/android/launcher3/CropView.java b/WallpaperPicker/src/com/android/launcher3/CropView.java
index 578b8ea..50f779a 100644
--- a/WallpaperPicker/src/com/android/launcher3/CropView.java
+++ b/WallpaperPicker/src/com/android/launcher3/CropView.java
@@ -21,7 +21,6 @@
 import android.graphics.Point;
 import android.graphics.RectF;
 import android.util.AttributeSet;
-import android.util.FloatMath;
 import android.view.MotionEvent;
 import android.view.ScaleGestureDetector;
 import android.view.ScaleGestureDetector.OnScaleGestureListener;
@@ -300,12 +299,12 @@
                     adjustment[0] = (edges.right - getWidth()) / scale;
                 }
                 if (edges.top > 0) {
-                    adjustment[1] = FloatMath.ceil(edges.top / scale);
+                    adjustment[1] = (float) Math.ceil(edges.top / scale);
                 } else if (edges.bottom < getHeight()) {
                     adjustment[1] = (edges.bottom - getHeight()) / scale;
                 }
                 for (int dim = 0; dim <= 1; dim++) {
-                    if (coef[dim] > 0) adjustment[dim] = FloatMath.ceil(adjustment[dim]);
+                    if (coef[dim] > 0) adjustment[dim] = (float) Math.ceil(adjustment[dim]);
                 }
 
                 mInverseRotateMatrix.mapPoints(adjustment);
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
index c49286a..9332091 100644
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
+++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
@@ -137,39 +137,32 @@
 
     public static class UriWallpaperInfo extends WallpaperTileInfo {
         private Uri mUri;
-        private boolean mFirstClick = true;
-        @Thunk BitmapRegionTileSource.UriBitmapSource mBitmapSource;
         public UriWallpaperInfo(Uri uri) {
             mUri = uri;
         }
         @Override
         public void onClick(final WallpaperPickerActivity a) {
-            final Runnable onLoad;
-            if (!mFirstClick) {
-                onLoad = null;
-            } else {
-                mFirstClick = false;
-                a.mSetWallpaperButton.setEnabled(false);
-                onLoad = new Runnable() {
-                    public void run() {
-                        if (mBitmapSource != null &&
-                                mBitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
-                            a.selectTile(mView);
-                            a.mSetWallpaperButton.setEnabled(true);
-                        } else {
-                            ViewGroup parent = (ViewGroup) mView.getParent();
-                            if (parent != null) {
-                                parent.removeView(mView);
-                                Toast.makeText(a.getContext(), R.string.image_load_fail,
-                                        Toast.LENGTH_SHORT).show();
-                            }
+            a.setWallpaperButtonEnabled(false);
+            final BitmapRegionTileSource.UriBitmapSource bitmapSource =
+                    new BitmapRegionTileSource.UriBitmapSource(
+                            a.getContext(), mUri, BitmapRegionTileSource.MAX_PREVIEW_SIZE);
+            a.setCropViewTileSource(bitmapSource, true, false, null, new Runnable() {
+
+                @Override
+                public void run() {
+                    if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
+                        a.selectTile(mView);
+                        a.setWallpaperButtonEnabled(true);
+                    } else {
+                        ViewGroup parent = (ViewGroup) mView.getParent();
+                        if (parent != null) {
+                            parent.removeView(mView);
+                            Toast.makeText(a.getContext(), R.string.image_load_fail,
+                                    Toast.LENGTH_SHORT).show();
                         }
                     }
-                };
-            }
-            mBitmapSource = new BitmapRegionTileSource.UriBitmapSource(
-                    a.getContext(), mUri, BitmapRegionTileSource.MAX_PREVIEW_SIZE);
-            a.setCropViewTileSource(mBitmapSource, true, false, null, onLoad);
+                }
+            });
         }
         @Override
         public void onSave(final WallpaperPickerActivity a) {
@@ -203,11 +196,18 @@
             mThumb = thumb;
         }
         @Override
-        public void onClick(WallpaperPickerActivity a) {
+        public void onClick(final WallpaperPickerActivity a) {
+            a.setWallpaperButtonEnabled(false);
             BitmapRegionTileSource.UriBitmapSource bitmapSource =
                     new BitmapRegionTileSource.UriBitmapSource(a.getContext(),
                             Uri.fromFile(mFile), 1024);
-            a.setCropViewTileSource(bitmapSource, false, true, null, null);
+            a.setCropViewTileSource(bitmapSource, false, true, null, new Runnable() {
+
+                @Override
+                public void run() {
+                    a.setWallpaperButtonEnabled(true);
+                }
+            });
         }
         @Override
         public void onSave(WallpaperPickerActivity a) {
@@ -234,6 +234,7 @@
         }
         @Override
         public void onClick(final WallpaperPickerActivity a) {
+            a.setWallpaperButtonEnabled(false);
             BitmapRegionTileSource.ResourceBitmapSource bitmapSource =
                     new BitmapRegionTileSource.ResourceBitmapSource(
                             mResources, mResId, BitmapRegionTileSource.MAX_PREVIEW_SIZE);
@@ -248,7 +249,13 @@
                             wallpaperSize.x, wallpaperSize.y, false);
                     return wallpaperSize.x / crop.width();
                 }
-            }, null);
+            }, new Runnable() {
+
+                @Override
+                public void run() {
+                    a.setWallpaperButtonEnabled(true);
+                }
+            });
         }
         @Override
         public void onSave(WallpaperPickerActivity a) {
@@ -420,7 +427,7 @@
                     }
                     return;
                 }
-                mSetWallpaperButton.setEnabled(true);
+                setWallpaperButtonEnabled(true);
                 WallpaperTileInfo info = (WallpaperTileInfo) v.getTag();
                 if (info.isSelectable() && v.getVisibility() == View.VISIBLE) {
                     selectTile(v);
@@ -639,6 +646,10 @@
         };
     }
 
+    public void setWallpaperButtonEnabled(boolean enabled) {
+        mSetWallpaperButton.setEnabled(enabled);
+    }
+
     @Thunk void selectTile(View v) {
         if (mSelectedTile != null) {
             mSelectedTile.setSelected(false);
diff --git a/res/drawable-hdpi/ic_pageindicator_add.png b/res/drawable-hdpi/ic_pageindicator_add.png
index 971361d..ab0e5db 100644
--- a/res/drawable-hdpi/ic_pageindicator_add.png
+++ b/res/drawable-hdpi/ic_pageindicator_add.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_pageindicator_current.png b/res/drawable-hdpi/ic_pageindicator_current.png
index 09405d8..423ca2b 100644
--- a/res/drawable-hdpi/ic_pageindicator_current.png
+++ b/res/drawable-hdpi/ic_pageindicator_current.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_pageindicator_current_folder.png b/res/drawable-hdpi/ic_pageindicator_current_folder.png
index c3a6f69..43fbb0e 100644
--- a/res/drawable-hdpi/ic_pageindicator_current_folder.png
+++ b/res/drawable-hdpi/ic_pageindicator_current_folder.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_pageindicator_default.png b/res/drawable-hdpi/ic_pageindicator_default.png
index 251de44..83fa73f 100644
--- a/res/drawable-hdpi/ic_pageindicator_default.png
+++ b/res/drawable-hdpi/ic_pageindicator_default.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_pageindicator_default_folder.png b/res/drawable-hdpi/ic_pageindicator_default_folder.png
index 48a3218..55cab1c 100644
--- a/res/drawable-hdpi/ic_pageindicator_default_folder.png
+++ b/res/drawable-hdpi/ic_pageindicator_default_folder.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_add.png b/res/drawable-mdpi/ic_pageindicator_add.png
index 50e1ba5..11659a3 100644
--- a/res/drawable-mdpi/ic_pageindicator_add.png
+++ b/res/drawable-mdpi/ic_pageindicator_add.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_current.png b/res/drawable-mdpi/ic_pageindicator_current.png
index 19a8972..ca889c4 100644
--- a/res/drawable-mdpi/ic_pageindicator_current.png
+++ b/res/drawable-mdpi/ic_pageindicator_current.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_current_folder.png b/res/drawable-mdpi/ic_pageindicator_current_folder.png
index 8d6c0b1..5bbba91 100644
--- a/res/drawable-mdpi/ic_pageindicator_current_folder.png
+++ b/res/drawable-mdpi/ic_pageindicator_current_folder.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_default.png b/res/drawable-mdpi/ic_pageindicator_default.png
index 6b0a9c9..34493b1 100644
--- a/res/drawable-mdpi/ic_pageindicator_default.png
+++ b/res/drawable-mdpi/ic_pageindicator_default.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_default_folder.png b/res/drawable-mdpi/ic_pageindicator_default_folder.png
index c9edf8d..0a987a4 100644
--- a/res/drawable-mdpi/ic_pageindicator_default_folder.png
+++ b/res/drawable-mdpi/ic_pageindicator_default_folder.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_add.png b/res/drawable-xhdpi/ic_pageindicator_add.png
index 9fb5359..af1da2d 100644
--- a/res/drawable-xhdpi/ic_pageindicator_add.png
+++ b/res/drawable-xhdpi/ic_pageindicator_add.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_current.png b/res/drawable-xhdpi/ic_pageindicator_current.png
index c44b88f..3054f2f 100644
--- a/res/drawable-xhdpi/ic_pageindicator_current.png
+++ b/res/drawable-xhdpi/ic_pageindicator_current.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_current_folder.png b/res/drawable-xhdpi/ic_pageindicator_current_folder.png
index fa2168f..cd92e9f 100644
--- a/res/drawable-xhdpi/ic_pageindicator_current_folder.png
+++ b/res/drawable-xhdpi/ic_pageindicator_current_folder.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_default.png b/res/drawable-xhdpi/ic_pageindicator_default.png
index 6d1b5be..38538dc 100644
--- a/res/drawable-xhdpi/ic_pageindicator_default.png
+++ b/res/drawable-xhdpi/ic_pageindicator_default.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_default_folder.png b/res/drawable-xhdpi/ic_pageindicator_default_folder.png
index ff0ed39..e7c46e3 100644
--- a/res/drawable-xhdpi/ic_pageindicator_default_folder.png
+++ b/res/drawable-xhdpi/ic_pageindicator_default_folder.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_add.png b/res/drawable-xxhdpi/ic_pageindicator_add.png
index f3f6fbe..c288952 100644
--- a/res/drawable-xxhdpi/ic_pageindicator_add.png
+++ b/res/drawable-xxhdpi/ic_pageindicator_add.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_current.png b/res/drawable-xxhdpi/ic_pageindicator_current.png
index 3a3a3ae..5941c8e 100644
--- a/res/drawable-xxhdpi/ic_pageindicator_current.png
+++ b/res/drawable-xxhdpi/ic_pageindicator_current.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_current_folder.png b/res/drawable-xxhdpi/ic_pageindicator_current_folder.png
index 4ff8ec6..602b89a 100644
--- a/res/drawable-xxhdpi/ic_pageindicator_current_folder.png
+++ b/res/drawable-xxhdpi/ic_pageindicator_current_folder.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_default.png b/res/drawable-xxhdpi/ic_pageindicator_default.png
index b057c63..3fa9e5f 100644
--- a/res/drawable-xxhdpi/ic_pageindicator_default.png
+++ b/res/drawable-xxhdpi/ic_pageindicator_default.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_default_folder.png b/res/drawable-xxhdpi/ic_pageindicator_default_folder.png
index 756ad09..bbcd7f9 100644
--- a/res/drawable-xxhdpi/ic_pageindicator_default_folder.png
+++ b/res/drawable-xxhdpi/ic_pageindicator_default_folder.png
Binary files differ
diff --git a/res/layout/apps_list_view.xml b/res/layout/apps_list_view.xml
index 595c46c..dfb7b58 100644
--- a/res/layout/apps_list_view.xml
+++ b/res/layout/apps_list_view.xml
@@ -20,7 +20,8 @@
     android:layout_height="match_parent"
     android:orientation="vertical"
     android:elevation="15dp"
-    android:visibility="gone">
+    android:visibility="gone"
+    android:focusableInTouchMode="true">
     <EditText
         android:id="@+id/app_search_box"
         android:layout_width="match_parent"
diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java
index 559f6eb..52bc6b6 100644
--- a/src/com/android/launcher3/AppsContainerView.java
+++ b/src/com/android/launcher3/AppsContainerView.java
@@ -57,8 +57,11 @@
     private RecyclerView.Adapter mAdapter;
     private RecyclerView.LayoutManager mLayoutManager;
     private RecyclerView.ItemDecoration mItemDecoration;
-    @Thunk AppsContainerRecyclerView mAppsListView;
-    private EditText mSearchBar;
+
+    private LinearLayout mContentView;
+    @Thunk AppsContainerRecyclerView mAppsRecyclerView;
+    private EditText mSearchBarView;
+    
     private int mNumAppsPerRow;
     private Point mLastTouchDownPos = new Point();
     private Rect mInsets = new Rect();
@@ -140,7 +143,7 @@
      * Hides the search bar
      */
     public void hideSearchBar() {
-        mSearchBar.setVisibility(View.GONE);
+        mSearchBarView.setVisibility(View.GONE);
         updateBackgrounds();
         updatePaddings();
     }
@@ -149,14 +152,14 @@
      * Scrolls this list view to the top.
      */
     public void scrollToTop() {
-        mAppsListView.scrollToPosition(0);
+        mAppsRecyclerView.scrollToPosition(0);
     }
 
     /**
      * Returns the content view used for the launcher transitions.
      */
     public View getContentView() {
-        return findViewById(R.id.apps_list);
+        return mContentView;
     }
 
     /**
@@ -173,19 +176,31 @@
         if (USE_LAYOUT == GRID_LAYOUT) {
             ((AppsGridAdapter) mAdapter).setRtl(isRtl);
         }
-        mSearchBar = (EditText) findViewById(R.id.app_search_box);
-        if (mSearchBar != null) {
-            mSearchBar.addTextChangedListener(this);
-            mSearchBar.setOnEditorActionListener(this);
+
+        // Work around the search box getting first focus and showing the cursor by
+        // proxying the focus from the content view to the recycler view directly
+        mContentView = (LinearLayout) findViewById(R.id.apps_list);
+        mContentView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+            @Override
+            public void onFocusChange(View v, boolean hasFocus) {
+                if (v == mContentView && hasFocus) {
+                    mAppsRecyclerView.requestFocus();
+                }
+            }
+        });
+        mSearchBarView = (EditText) findViewById(R.id.app_search_box);
+        if (mSearchBarView != null) {
+            mSearchBarView.addTextChangedListener(this);
+            mSearchBarView.setOnEditorActionListener(this);
         }
-        mAppsListView = (AppsContainerRecyclerView) findViewById(R.id.apps_list_view);
-        mAppsListView.setApps(mApps);
-        mAppsListView.setNumAppsPerRow(mNumAppsPerRow);
-        mAppsListView.setLayoutManager(mLayoutManager);
-        mAppsListView.setAdapter(mAdapter);
-        mAppsListView.setHasFixedSize(true);
+        mAppsRecyclerView = (AppsContainerRecyclerView) findViewById(R.id.apps_list_view);
+        mAppsRecyclerView.setApps(mApps);
+        mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow);
+        mAppsRecyclerView.setLayoutManager(mLayoutManager);
+        mAppsRecyclerView.setAdapter(mAdapter);
+        mAppsRecyclerView.setHasFixedSize(true);
         if (mItemDecoration != null) {
-            mAppsListView.addItemDecoration(mItemDecoration);
+            mAppsRecyclerView.addItemDecoration(mItemDecoration);
         }
         updateBackgrounds();
         updatePaddings();
@@ -207,7 +222,7 @@
             DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
             if (grid.updateAppsViewNumCols(context.getResources(), fixedBounds.width())) {
                 mNumAppsPerRow = grid.appsViewNumCols;
-                mAppsListView.setNumAppsPerRow(mNumAppsPerRow);
+                mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow);
                 if (USE_LAYOUT == GRID_LAYOUT) {
                     ((AppsGridAdapter) mAdapter).setNumAppsPerRow(mNumAppsPerRow);
                 }
@@ -372,7 +387,7 @@
             for (int i = 0; i < items.size(); i++) {
                 AlphabeticalAppsList.AdapterItem item = items.get(i);
                 if (!item.isSectionHeader) {
-                    mAppsListView.getChildAt(i).performClick();
+                    mAppsRecyclerView.getChildAt(i).performClick();
                     InputMethodManager imm = (InputMethodManager)
                             getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
                     imm.hideSoftInputFromWindow(getWindowToken(), 0);
@@ -390,12 +405,7 @@
 
     @Override
     public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) {
-        if (!toWorkspace) {
-            // Disable the focus so that the search bar doesn't get focus
-            if (mSearchBar != null) {
-                mSearchBar.setFocusableInTouchMode(false);
-            }
-        }
+        // Do nothing
     }
 
     @Override
@@ -410,12 +420,10 @@
 
     @Override
     public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
-        if (mSearchBar != null) {
+        if (mSearchBarView != null) {
             if (toWorkspace) {
                 // Clear the search bar
-                mSearchBar.setText("");
-            } else {
-                mSearchBar.setFocusableInTouchMode(true);
+                mSearchBarView.setText("");
             }
         }
     }
@@ -430,7 +438,8 @@
     private void updatePaddings() {
         boolean isRtl = (getResources().getConfiguration().getLayoutDirection() ==
                 LAYOUT_DIRECTION_RTL);
-        boolean hasSearchBar = (mSearchBar != null) && (mSearchBar.getVisibility() == View.VISIBLE);
+        boolean hasSearchBar = (mSearchBarView != null) &&
+                (mSearchBarView.getVisibility() == View.VISIBLE);
 
         if (mFixedBounds.isEmpty()) {
             // If there are no fixed bounds, then use the default padding and insets
@@ -446,14 +455,15 @@
         // Update the apps recycler view
         int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset;
         if (isRtl) {
-            mAppsListView.setPadding(inset, inset, inset + mContentMarginStart, inset);
+            mAppsRecyclerView.setPadding(inset, inset, inset + mContentMarginStart, inset);
         } else {
-            mAppsListView.setPadding(inset + mContentMarginStart, inset, inset, inset);
+            mAppsRecyclerView.setPadding(inset + mContentMarginStart, inset, inset, inset);
         }
 
         // Update the search bar
         if (hasSearchBar) {
-            LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mSearchBar.getLayoutParams();
+            LinearLayout.LayoutParams lp =
+                    (LinearLayout.LayoutParams) mSearchBarView.getLayoutParams();
             lp.leftMargin = lp.rightMargin = inset;
         }
     }
@@ -463,11 +473,12 @@
      */
     private void updateBackgrounds() {
         int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset;
-        boolean hasSearchBar = (mSearchBar != null) && (mSearchBar.getVisibility() == View.VISIBLE);
+        boolean hasSearchBar = (mSearchBarView != null) &&
+                (mSearchBarView.getVisibility() == View.VISIBLE);
 
         // Update the background of the reveal view and list to be inset with the fixed bound
         // insets instead of the default insets
-        mAppsListView.setBackground(new InsetDrawable(
+        mAppsRecyclerView.setBackground(new InsetDrawable(
                 getContext().getResources().getDrawable(
                         hasSearchBar ? R.drawable.apps_list_search_bg : R.drawable.apps_list_bg),
                 inset, 0, inset, 0));
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index 8791c89..c77d416 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -16,14 +16,12 @@
 
 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 com.android.launcher3.FocusHelper.PagedViewKeyListener;
 import com.android.launcher3.util.FocusLogic;
 import com.android.launcher3.util.Thunk;
 
@@ -52,14 +50,10 @@
     private static final String TAG = "FocusHelper";
     private static final boolean DEBUG = false;
 
-    //
-    // Key code handling methods.
-    //
-
     /**
-     * A keyboard listener for scrollable folders
+     * Handles key events in paged folder.
      */
-    public static class PagedFolderKeyEventListener extends PagedViewKeyListener {
+    public static class PagedFolderKeyEventListener implements View.OnKeyListener {
 
         private final Folder mFolder;
 
@@ -68,41 +62,18 @@
         }
 
         @Override
-        public void handleNoopKey(int keyCode, View v) {
-            if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
-                mFolder.mFolderName.requestFocus();
-                playSoundEffect(keyCode, v);
-            }
-        }
-    }
-
-    /**
-     * Handles key events in the all apps screen.
-     */
-    public static class PagedViewKeyListener implements View.OnKeyListener {
-
-        @Override
         public boolean onKey(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 and Folders keyevent=[%s].",
+                Log.v(TAG, String.format("Handle ALL Folders 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 ShortcutAndWidgetContainer)) {
                 if (LauncherAppState.isDogfoodBuild()) {
                     throw new IllegalStateException("Parent of the focused item is not supported.");
                 } else {
@@ -110,15 +81,19 @@
                 }
             }
 
-            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;
-            // TODO(hyunyoungs): this matrix is not applicable on the last page.
-            int[][] matrix = FocusLogic.createFullMatrix(countX, countY, true);
+            // 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();
+
+            final int pageIndex = pagedView.indexOfChild(cellLayout);
+            final int pageCount = pagedView.getPageCount();
+
+            int[][] matrix = FocusLogic.createSparseMatrix(cellLayout);
             // Process focus.
             int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix,
                     iconIndex, pageIndex, pageCount);
@@ -126,60 +101,55 @@
                 handleNoopKey(keyCode, v);
                 return consume;
             }
+            ShortcutAndWidgetContainer newParent = null;
+            View child = null;
+
             switch (newIconIndex) {
                 case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN:
-                case FocusLogic.NEXT_PAGE_RIGHT_COLUMN:
-                    int newPageIndex = pageIndex - 1;
-                    if (newIconIndex == FocusLogic.NEXT_PAGE_RIGHT_COLUMN) {
-                        newPageIndex = pageIndex + 1;
-                    }
-                    newParent = getAppsCustomizePage(container, newPageIndex);
+                case FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN:
+                    newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex - 1);
                     if (newParent != null) {
-                        int row = FocusLogic.findRow(matrix, iconIndex);
-                        container.snapToPage(newPageIndex);
-                        // no need to create a new matrix.
-                        child = newParent.getChildAt(matrix[countX-1][row]);
+                        int row = ((CellLayout.LayoutParams) v.getLayoutParams()).cellY;
+                        pagedView.snapToPage(pageIndex - 1);
+                        child = newParent.getChildAt(
+                                ((newIconIndex == FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN)
+                                    ^ newParent.invertLayoutHorizontally()) ? 0 : countX - 1, row);
                     }
                     break;
                 case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
-                    newParent = getAppsCustomizePage(container, pageIndex - 1);
+                    newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex - 1);
                     if (newParent != null) {
-                        container.snapToPage(pageIndex - 1);
-                        child = newParent.getChildAt(0);
+                        pagedView.snapToPage(pageIndex - 1);
+                        child = newParent.getChildAt(0, 0);
                     }
                     break;
                 case FocusLogic.PREVIOUS_PAGE_LAST_ITEM:
-                    newParent = getAppsCustomizePage(container, pageIndex - 1);
+                    newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex - 1);
                     if (newParent != null) {
-                        container.snapToPage(pageIndex - 1);
-                        child = newParent.getChildAt(newParent.getChildCount() - 1);
+                        pagedView.snapToPage(pageIndex - 1);
+                        child = newParent.getChildAt(countX - 1, countY - 1);
                     }
                     break;
                 case FocusLogic.NEXT_PAGE_FIRST_ITEM:
-                    newParent = getAppsCustomizePage(container, pageIndex + 1);
+                    newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex + 1);
                     if (newParent != null) {
-                        container.snapToPage(pageIndex + 1);
-                        child = newParent.getChildAt(0);
+                        pagedView.snapToPage(pageIndex + 1);
+                        child = newParent.getChildAt(0, 0);
                     }
                     break;
                 case FocusLogic.NEXT_PAGE_LEFT_COLUMN:
-                case FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN:
-                    newPageIndex = pageIndex + 1;
-                    if (newIconIndex == FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN) {
-                        newPageIndex = pageIndex -1;
-                    }
-                    newParent = getAppsCustomizePage(container, newPageIndex);
+                case FocusLogic.NEXT_PAGE_RIGHT_COLUMN:
+                    newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex + 1);
                     if (newParent != null) {
-                        container.snapToPage(newPageIndex);
-                        int row = FocusLogic.findRow(matrix, iconIndex);
-                        child = newParent.getChildAt(matrix[0][row]);
+                        pagedView.snapToPage(pageIndex + 1);
+                        child = FocusLogic.getAdjacentChildInNextPage(newParent, v, newIconIndex);
                     }
                     break;
                 case FocusLogic.CURRENT_PAGE_FIRST_ITEM:
-                    child = container.getChildAt(0);
+                    child = cellLayout.getChildAt(0, 0);
                     break;
                 case FocusLogic.CURRENT_PAGE_LAST_ITEM:
-                    child = itemContainer.getChildAt(itemContainer.getChildCount() - 1);
+                    child = pagedView.getLastItem();
                     break;
                 default: // Go to some item on the current page.
                     child = itemContainer.getChildAt(newIconIndex);
@@ -194,7 +164,12 @@
             return consume;
         }
 
-        public void handleNoopKey(int keyCode, View v) { }
+        public void handleNoopKey(int keyCode, View v) {
+            if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
+                mFolder.mFolderName.requestFocus();
+                playSoundEffect(keyCode, v);
+            }
+        }
     }
 
     /**
@@ -222,15 +197,21 @@
         Hotseat hotseat = (Hotseat) hotseatLayout.getParent();
 
         Workspace workspace = (Workspace) v.getRootView().findViewById(R.id.workspace);
-        int pageIndex = workspace.getCurrentPage();
+        int pageIndex = workspace.getNextPage();
         int pageCount = workspace.getChildCount();
         int countX = -1;
         int countY = -1;
-        int iconIndex = findIndexOfView(hotseatParent, v);
+        int iconIndex = hotseatParent.indexOfChild(v);
         int iconRank = ((CellLayout.LayoutParams) hotseatLayout.getShortcutsAndWidgets()
                 .getChildAt(iconIndex).getLayoutParams()).cellX;
 
         final CellLayout iconLayout = (CellLayout) workspace.getChildAt(pageIndex);
+        if (iconLayout == null) {
+            // This check is to guard against cases where key strokes rushes in when workspace
+            // child creation/deletion is still in flux. (e.g., during drop or fling
+            // animation.)
+            return consume;
+        }
         final ViewGroup iconParent = iconLayout.getShortcutsAndWidgets();
 
         ViewGroup parent = null;
@@ -317,11 +298,12 @@
         final ViewGroup launcher = (ViewGroup) workspace.getParent();
         final ViewGroup tabs = (ViewGroup) launcher.findViewById(R.id.search_drop_target_bar);
         final Hotseat hotseat = (Hotseat) launcher.findViewById(R.id.hotseat);
-        int pageIndex = workspace.indexOfChild(iconLayout);
-        int pageCount = workspace.getChildCount();
+
+        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();
-        final int iconIndex = findIndexOfView(parent, v);
 
         CellLayout hotseatLayout = (CellLayout) hotseat.getChildAt(0);
         ShortcutAndWidgetContainer hotseatParent = hotseatLayout.getShortcutsAndWidgets();
@@ -362,9 +344,11 @@
                 if (newIconIndex == FocusLogic.NEXT_PAGE_RIGHT_COLUMN) {
                     newPageIndex = pageIndex + 1;
                 }
-                int row = FocusLogic.findRow(matrix, iconIndex);
+                int row = ((CellLayout.LayoutParams) v.getLayoutParams()).cellY;
                 parent = getCellLayoutChildrenForIndex(workspace, newPageIndex);
+                workspace.snapToPage(newPageIndex);
                 if (parent != null) {
+                    workspace.snapToPage(newPageIndex);
                     iconLayout = (CellLayout) parent.getParent();
                     matrix = FocusLogic.createSparseMatrix(iconLayout,
                         iconLayout.getCountX(), row);
@@ -394,9 +378,11 @@
                 if (newIconIndex == FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN) {
                     newPageIndex = pageIndex - 1;
                 }
-                row = FocusLogic.findRow(matrix, iconIndex);
+                workspace.snapToPage(newPageIndex);
+                row = ((CellLayout.LayoutParams) v.getLayoutParams()).cellY;
                 parent = getCellLayoutChildrenForIndex(workspace, newPageIndex);
                 if (parent != null) {
+                    workspace.snapToPage(newPageIndex);
                     iconLayout = (CellLayout) parent.getParent();
                     matrix = FocusLogic.createSparseMatrix(iconLayout, -1, row);
                     newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY, matrix,
@@ -432,18 +418,6 @@
     //
 
     /**
-     * Returns the Viewgroup containing page contents for the page at the index specified.
-     */
-    @Thunk static ViewGroup getAppsCustomizePage(ViewGroup container, int index) {
-        ViewGroup page = (ViewGroup) ((PagedView) container).getPageAt(index);
-        if (page instanceof CellLayout) {
-            // There are two layers, a PagedViewCellLayout and PagedViewCellLayoutChildren
-            page = ((CellLayout) page).getShortcutsAndWidgets();
-        }
-        return page;
-    }
-
-    /**
      * Private helper method to get the CellLayoutChildren given a CellLayout index.
      */
     private static ShortcutAndWidgetContainer getCellLayoutChildrenForIndex(
@@ -452,15 +426,6 @@
         return parent.getShortcutsAndWidgets();
     }
 
-    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 -1;
-    }
-
     /**
      * Helper method to be used for playing sound effects.
      */
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 9ad87c3..3d8cf31 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1003,7 +1003,7 @@
         super.onResume();
 
         // Restore the previous launcher state
-        if (mOnResumeState == State.WORKSPACE || mOnResumeState == State.NONE) {
+        if (mOnResumeState == State.WORKSPACE) {
             showWorkspace(false);
         } else if (mOnResumeState == State.APPS) {
             showAppsView(false /* animated */, false /* resetListToTop */);
diff --git a/src/com/android/launcher3/LauncherScroller.java b/src/com/android/launcher3/LauncherScroller.java
index 3bd0a78..a9b4955 100644
--- a/src/com/android/launcher3/LauncherScroller.java
+++ b/src/com/android/launcher3/LauncherScroller.java
@@ -20,7 +20,6 @@
 import android.content.Context;
 import android.hardware.SensorManager;
 import android.os.Build;
-import android.util.FloatMath;
 import android.view.ViewConfiguration;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
@@ -409,7 +408,7 @@
 
             float dx = (float) (mFinalX - mStartX);
             float dy = (float) (mFinalY - mStartY);
-            float hyp = FloatMath.sqrt(dx * dx + dy * dy);
+            float hyp = (float) Math.hypot(dx, dy);
 
             float ndx = dx / hyp;
             float ndy = dy / hyp;
@@ -426,7 +425,7 @@
         mMode = FLING_MODE;
         mFinished = false;
 
-        float velocity = FloatMath.sqrt(velocityX * velocityX + velocityY * velocityY);
+        float velocity = (float) Math.hypot(velocityX, velocityY);
 
         mVelocity = velocity;
         mDuration = getSplineFlingDuration(velocity);
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index 57bd5b2..78272a8 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -184,11 +184,6 @@
         final WidgetsContainerView toView = mLauncher.getWidgetsView();
         PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
             @Override
-            public void onRevealViewVisible(View revealView, View contentView,
-                    View allAppsButtonView) {
-                revealView.setBackground(mLauncher.getDrawable(R.drawable.quantum_panel_dark));
-            }
-            @Override
             public float getMaterialRevealViewFinalAlpha(View revealView) {
                 return 0.3f;
             }
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 158a30c..88295c0 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -19,6 +19,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
+import android.animation.LayoutTransition;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
@@ -475,13 +476,14 @@
 
     /**
      * Returns the index of the currently displayed page.
-     *
-     * @return The index of the currently displayed page.
      */
     int getCurrentPage() {
         return mCurrentPage;
     }
 
+    /**
+     * Returns the index of page to be shown immediately afterwards.
+     */
     int getNextPage() {
         return (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
     }
@@ -961,8 +963,8 @@
         LayoutParams nextLp;
 
         int childLeft = offsetX + (lp.isFullScreenPage ? 0 : getPaddingLeft());
-        if (mPageScrolls == null || getChildCount() != mChildCountOnLastLayout) {
-            mPageScrolls = new int[getChildCount()];
+        if (mPageScrolls == null || childCount != mChildCountOnLastLayout) {
+            mPageScrolls = new int[childCount];
         }
 
         for (int i = startIndex; i != endIndex; i += delta) {
@@ -1009,19 +1011,36 @@
             }
         }
 
-        if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
+        if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < childCount) {
             updateCurrentPageScroll();
             mFirstLayout = false;
         }
 
-        if (childCount > 0) {
-            final int index = isLayoutRtl() ? 0 : childCount - 1;
-            mMaxScrollX = getScrollForPage(index);
+        final LayoutTransition transition = getLayoutTransition();
+        // If the transition is running defer updating max scroll, as some empty pages could
+        // still be present, and a max scroll change could cause sudden jumps in scroll.
+        if (transition != null && transition.isRunning()) {
+            transition.addTransitionListener(new LayoutTransition.TransitionListener() {
+
+                @Override
+                public void startTransition(LayoutTransition transition, ViewGroup container,
+                        View view, int transitionType) { }
+
+                @Override
+                public void endTransition(LayoutTransition transition, ViewGroup container,
+                        View view, int transitionType) {
+                    // Wait until all transitions are complete.
+                    if (!transition.isRunning()) {
+                        transition.removeTransitionListener(this);
+                        updateMaxScrollX();
+                    }
+                }
+            });
         } else {
-            mMaxScrollX = 0;
+            updateMaxScrollX();
         }
 
-        if (mScroller.isFinished() && mChildCountOnLastLayout != getChildCount() &&
+        if (mScroller.isFinished() && mChildCountOnLastLayout != childCount &&
                 !mDeferringForDelete) {
             if (mRestorePage != INVALID_RESTORE_PAGE) {
                 setCurrentPage(mRestorePage);
@@ -1030,13 +1049,23 @@
                 setCurrentPage(getNextPage());
             }
         }
-        mChildCountOnLastLayout = getChildCount();
+        mChildCountOnLastLayout = childCount;
 
         if (isReordering(true)) {
             updateDragViewTranslationDuringDrag();
         }
     }
 
+    private void updateMaxScrollX() {
+        int childCount = getChildCount();
+        if (childCount > 0) {
+            final int index = isLayoutRtl() ? 0 : childCount - 1;
+            mMaxScrollX = getScrollForPage(index);
+        } else {
+            mMaxScrollX = 0;
+        }
+    }
+
     public void setPageSpacing(int pageSpacing) {
         mPageSpacing = pageSpacing;
         requestLayout();
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index ff06045..15b6176 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -177,7 +177,7 @@
         child.measure(childWidthMeasureSpec, childheightMeasureSpec);
     }
 
-    private boolean invertLayoutHorizontally() {
+    public boolean invertLayoutHorizontally() {
         return mInvertIfRtl && isLayoutRtl();
     }
 
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 9173971..043ecb0 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1410,7 +1410,22 @@
         }
 
         private float wallpaperOffsetForCurrentScroll() {
+            // TODO: do different behavior if it's  a live wallpaper?
+            // Don't use up all the wallpaper parallax until you have at least
+            // MIN_PARALLAX_PAGE_SPAN pages
+            int numScrollingPages = getNumScreensExcludingEmptyAndCustom();
+            int parallaxPageSpan;
+            if (mWallpaperIsLiveWallpaper) {
+                parallaxPageSpan = numScrollingPages - 1;
+            } else {
+                parallaxPageSpan = Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollingPages - 1);
+            }
+            mNumPagesForWallpaperParallax = parallaxPageSpan;
+
             if (getChildCount() <= 1) {
+                if (isLayoutRtl()) {
+                    return 1 - 1.0f/mNumPagesForWallpaperParallax;
+                }
                 return 0;
             }
 
@@ -1430,28 +1445,20 @@
             if (scrollRange == 0) {
                 return 0;
             } else {
-                // TODO: do different behavior if it's  a live wallpaper?
                 // Sometimes the left parameter of the pages is animated during a layout transition;
                 // this parameter offsets it to keep the wallpaper from animating as well
                 int adjustedScroll =
                         getScrollX() - firstPageScrollX - getLayoutTransitionOffsetForPage(0);
                 float offset = Math.min(1, adjustedScroll / (float) scrollRange);
                 offset = Math.max(0, offset);
-                // Don't use up all the wallpaper parallax until you have at least
-                // MIN_PARALLAX_PAGE_SPAN pages
-                int numScrollingPages = getNumScreensExcludingEmptyAndCustom();
-                int parallaxPageSpan;
-                if (mWallpaperIsLiveWallpaper) {
-                    parallaxPageSpan = numScrollingPages - 1;
-                } else {
-                    parallaxPageSpan = Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollingPages - 1);
-                }
-                mNumPagesForWallpaperParallax = parallaxPageSpan;
 
                 // On RTL devices, push the wallpaper offset to the right if we don't have enough
                 // pages (ie if numScrollingPages < MIN_PARALLAX_PAGE_SPAN)
-                int padding = isLayoutRtl() ? parallaxPageSpan - numScrollingPages + 1 : 0;
-                return offset * (padding + numScrollingPages - 1) / parallaxPageSpan;
+                if (!mWallpaperIsLiveWallpaper && numScrollingPages < MIN_PARALLAX_PAGE_SPAN
+                        && isLayoutRtl()) {
+                    return offset * (parallaxPageSpan - numScrollingPages + 1) / parallaxPageSpan;
+                }
+                return offset * (numScrollingPages - 1) / parallaxPageSpan;
             }
         }
 
diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java
index 8a08a4e..a84e7df 100644
--- a/src/com/android/launcher3/util/FocusLogic.java
+++ b/src/com/android/launcher3/util/FocusLogic.java
@@ -18,11 +18,15 @@
 
 import android.util.Log;
 import android.view.KeyEvent;
+import android.view.View;
 import android.view.ViewGroup;
 
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.ShortcutAndWidgetContainer;
+
+import java.util.Arrays;
 
 /**
  * Calculates the next item that a {@link KeyEvent} should change the focus to.
@@ -69,14 +73,11 @@
      * Returns true only if this utility class handles the key code.
      */
     public static boolean shouldConsume(int keyCode) {
-        if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT ||
+        return (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_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL) {
-            return true;
-        }
-        return false;
+                keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL);
     }
 
     public static int handleKeyEvent(int keyCode, int cntX, int cntY, int [][] map,
@@ -138,33 +139,17 @@
     }
 
     /**
-     * Returns a matrix of size (m x n) that has been initialized with incremental index starting
-     * with 0 or a matrix where all the values are initialized to {@link #EMPTY}.
+     * Returns a matrix of size (m x n) that has been initialized with {@link #EMPTY}.
      *
      * @param m                 number of columns in the matrix
      * @param n                 number of rows in the matrix
-     * @param incrementOrder    {@code true} if the matrix contents should increment in reading
-     *                          order with 0 indexing. {@code false} if each cell should be
-     *                          initialized to {@link #EMPTY};
      */
     // TODO: get rid of dynamic matrix creation.
-    public static int[][] createFullMatrix(int m, int n, boolean incrementOrder) {
-        DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid()
-                .getDeviceProfile();
+    private static int[][] createFullMatrix(int m, int n) {
         int[][] matrix = new int [m][n];
 
         for (int i=0; i < m;i++) {
-            for (int j=0; j < n; j++) {
-                if (incrementOrder) {
-                    if (!profile.isLayoutRtl) {
-                        matrix[i][j] = j * m + i;
-                    } else {
-                        matrix[i][j] = j * m + m - i -1;
-                    }
-                } else {
-                    matrix[i][j] = EMPTY;
-                }
-            }
+            Arrays.fill(matrix[i], EMPTY);
         }
         return matrix;
     }
@@ -175,17 +160,18 @@
      */
     // TODO: get rid of the dynamic matrix creation
     public static int[][] createSparseMatrix(CellLayout layout) {
-        ViewGroup parent = layout.getShortcutsAndWidgets();
+        ShortcutAndWidgetContainer parent = layout.getShortcutsAndWidgets();
         final int m = layout.getCountX();
         final int n = layout.getCountY();
+        final boolean invert = parent.invertLayoutHorizontally();
 
-        int[][] matrix = createFullMatrix(m, n, false /* initialize to #EMPTY */);
+        int[][] matrix = createFullMatrix(m, n);
 
         // Iterate thru the children.
         for (int i = 0; i < parent.getChildCount(); i++ ) {
             int cx = ((CellLayout.LayoutParams) parent.getChildAt(i).getLayoutParams()).cellX;
             int cy = ((CellLayout.LayoutParams) parent.getChildAt(i).getLayoutParams()).cellY;
-            matrix[cx][cy] = i;
+            matrix[invert ? (m - cx - 1) : cx][cy] = i;
         }
         if (DEBUG) {
             printMatrix(matrix);
@@ -213,7 +199,7 @@
             m = iconLayout.getCountX() + hotseatLayout.getCountX();
             n = iconLayout.getCountY();
         }
-        int[][] matrix = createFullMatrix(m, n, false /* set all cell to empty */);
+        int[][] matrix = createFullMatrix(m, n);
 
         // Iterate thru the children of the top parent.
         for (int i = 0; i < iconParent.getChildCount(); i++) {
@@ -267,8 +253,7 @@
 
         ViewGroup iconParent = iconLayout.getShortcutsAndWidgets();
 
-        int[][] matrix = createFullMatrix(iconLayout.getCountX() + 1, iconLayout.getCountY(),
-                false /* set all cell to empty */);
+        int[][] matrix = createFullMatrix(iconLayout.getCountX() + 1, iconLayout.getCountY());
 
         // Iterate thru the children of the top parent.
         for (int i = 0; i < iconParent.getChildCount(); i++) {
@@ -499,22 +484,25 @@
     }
 
     /**
-     * Figure out the location of the icon.
-     *
+     * @param edgeColumn the column of the new icon. either {@link #NEXT_PAGE_LEFT_COLUMN} or
+     * {@link #NEXT_PAGE_RIGHT_COLUMN}
+     * @return the view adjacent to {@param oldView} in the {@param nextPage}.
      */
-    //TODO(hyunyoungs): this helper method should move to CellLayout class while removing the
-    // dynamic matrix creation all together.
-    public static int findRow(int[][] matrix, int iconIndex) {
-        int cntX = matrix.length;
-        int cntY = matrix[0].length;
+    public static View getAdjacentChildInNextPage(
+            ShortcutAndWidgetContainer nextPage, View oldView, int edgeColumn) {
+        final int newRow = ((CellLayout.LayoutParams) oldView.getLayoutParams()).cellY;
 
-        for (int i = 0; i < cntX; i++) {
-            for (int j = 0; j < cntY; j++) {
-                if (matrix[i][j] == iconIndex) {
-                    return j;
+        int column = (edgeColumn == NEXT_PAGE_LEFT_COLUMN) ^ nextPage.invertLayoutHorizontally()
+                ? 0 : (((CellLayout) nextPage.getParent()).getCountX() - 1);
+
+        for (; column >= 0; column--) {
+            for (int row = newRow; row >= 0; row--) {
+                View newView = nextPage.getChildAt(column, row);
+                if (newView != null) {
+                    return newView;
                 }
             }
         }
-        return -1;
+        return null;
     }
 }