Merge "Fix widget tray crash on screen rotation introduced by ag/694693" into ub-launcher3-burnaby
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
index ee0d8b0..f2bb509 100644
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
+++ b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
@@ -263,6 +263,7 @@
         if (req.postExecute != null) {
             req.postExecute.run();
         }
+        mProgressView.setVisibility(View.GONE);
     }
 
     public final void setCropViewTileSource(BitmapSource bitmapSource, boolean touchEnabled,
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
index 72cb194..3f90203 100644
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
+++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
@@ -196,13 +196,15 @@
         @Override
         public void onClick(final WallpaperPickerActivity a) {
             a.setWallpaperButtonEnabled(false);
-            BitmapRegionTileSource.UriBitmapSource bitmapSource =
+            final BitmapRegionTileSource.UriBitmapSource bitmapSource =
                     new BitmapRegionTileSource.UriBitmapSource(a.getContext(), Uri.fromFile(mFile));
             a.setCropViewTileSource(bitmapSource, false, true, null, new Runnable() {
 
                 @Override
                 public void run() {
-                    a.setWallpaperButtonEnabled(true);
+                    if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) { 
+                        a.setWallpaperButtonEnabled(true);
+                    }
                 }
             });
         }
@@ -232,7 +234,7 @@
         @Override
         public void onClick(final WallpaperPickerActivity a) {
             a.setWallpaperButtonEnabled(false);
-            BitmapRegionTileSource.ResourceBitmapSource bitmapSource =
+            final BitmapRegionTileSource.ResourceBitmapSource bitmapSource =
                     new BitmapRegionTileSource.ResourceBitmapSource(mResources, mResId);
             a.setCropViewTileSource(bitmapSource, false, false, new CropViewScaleProvider() {
 
@@ -249,7 +251,9 @@
 
                 @Override
                 public void run() {
-                    a.setWallpaperButtonEnabled(true);
+                    if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
+                        a.setWallpaperButtonEnabled(true);
+                    }
                 }
             });
         }
diff --git a/res/drawable-hdpi/ic_launcher_info_active.png b/res/drawable-hdpi/ic_launcher_info_active.png
deleted file mode 100644
index f7a3b68..0000000
--- a/res/drawable-hdpi/ic_launcher_info_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_launcher_remove_active.png b/res/drawable-hdpi/ic_launcher_remove_active.png
deleted file mode 100644
index e53de0d..0000000
--- a/res/drawable-hdpi/ic_launcher_remove_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_launcher_uninstall_active.png b/res/drawable-hdpi/ic_launcher_uninstall_active.png
deleted file mode 100644
index 22b97ee..0000000
--- a/res/drawable-hdpi/ic_launcher_uninstall_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl/apps_list_fastscroll_bg.xml b/res/drawable-ldrtl/all_apps_fastscroll_bg.xml
similarity index 93%
rename from res/drawable-ldrtl/apps_list_fastscroll_bg.xml
rename to res/drawable-ldrtl/all_apps_fastscroll_bg.xml
index 772975a..4777f70 100644
--- a/res/drawable-ldrtl/apps_list_fastscroll_bg.xml
+++ b/res/drawable-ldrtl/all_apps_fastscroll_bg.xml
@@ -16,7 +16,7 @@
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
-    <solid android:color="@color/apps_view_scrollbar_thumb_color" />
+    <solid android:color="@color/all_apps_scrollbar_thumb_color" />
     <size
         android:width="64dp"
         android:height="64dp" />
diff --git a/res/drawable-mdpi/ic_launcher_info_active.png b/res/drawable-mdpi/ic_launcher_info_active.png
deleted file mode 100644
index ea71272..0000000
--- a/res/drawable-mdpi/ic_launcher_info_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher_remove_active.png b/res/drawable-mdpi/ic_launcher_remove_active.png
deleted file mode 100644
index f36cfdd..0000000
--- a/res/drawable-mdpi/ic_launcher_remove_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher_uninstall_active.png b/res/drawable-mdpi/ic_launcher_uninstall_active.png
deleted file mode 100644
index e4ee911..0000000
--- a/res/drawable-mdpi/ic_launcher_uninstall_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_launcher_info_active.png b/res/drawable-xhdpi/ic_launcher_info_active.png
deleted file mode 100644
index b438f9e..0000000
--- a/res/drawable-xhdpi/ic_launcher_info_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_launcher_remove_active.png b/res/drawable-xhdpi/ic_launcher_remove_active.png
deleted file mode 100644
index 14ac79d..0000000
--- a/res/drawable-xhdpi/ic_launcher_remove_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_launcher_uninstall_active.png b/res/drawable-xhdpi/ic_launcher_uninstall_active.png
deleted file mode 100644
index 2c19b32..0000000
--- a/res/drawable-xhdpi/ic_launcher_uninstall_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_launcher_info_active.png b/res/drawable-xxhdpi/ic_launcher_info_active.png
deleted file mode 100644
index d354dd3..0000000
--- a/res/drawable-xxhdpi/ic_launcher_info_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_launcher_remove_active.png b/res/drawable-xxhdpi/ic_launcher_remove_active.png
deleted file mode 100644
index 9df4404..0000000
--- a/res/drawable-xxhdpi/ic_launcher_remove_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_launcher_uninstall_active.png b/res/drawable-xxhdpi/ic_launcher_uninstall_active.png
deleted file mode 100644
index db7d339..0000000
--- a/res/drawable-xxhdpi/ic_launcher_uninstall_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_launcher_info_active.png b/res/drawable-xxxhdpi/ic_launcher_info_active.png
deleted file mode 100644
index 162e23d..0000000
--- a/res/drawable-xxxhdpi/ic_launcher_info_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_launcher_remove_active.png b/res/drawable-xxxhdpi/ic_launcher_remove_active.png
deleted file mode 100644
index c0b8ea2..0000000
--- a/res/drawable-xxxhdpi/ic_launcher_remove_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_launcher_uninstall_active.png b/res/drawable-xxxhdpi/ic_launcher_uninstall_active.png
deleted file mode 100644
index 75896f3..0000000
--- a/res/drawable-xxxhdpi/ic_launcher_uninstall_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/apps_list_fastscroll_bg.xml b/res/drawable/all_apps_fastscroll_bg.xml
similarity index 93%
rename from res/drawable/apps_list_fastscroll_bg.xml
rename to res/drawable/all_apps_fastscroll_bg.xml
index 780d3b0..6b74484 100644
--- a/res/drawable/apps_list_fastscroll_bg.xml
+++ b/res/drawable/all_apps_fastscroll_bg.xml
@@ -16,7 +16,7 @@
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
-    <solid android:color="@color/apps_view_scrollbar_thumb_color" />
+    <solid android:color="@color/all_apps_scrollbar_thumb_color" />
     <size
         android:width="64dp"
         android:height="64dp" />
diff --git a/res/drawable/apps_list_scrollbar_thumb.xml b/res/drawable/all_apps_scrollbar_thumb.xml
similarity index 85%
rename from res/drawable/apps_list_scrollbar_thumb.xml
rename to res/drawable/all_apps_scrollbar_thumb.xml
index 318d406..649a963 100644
--- a/res/drawable/apps_list_scrollbar_thumb.xml
+++ b/res/drawable/all_apps_scrollbar_thumb.xml
@@ -16,6 +16,6 @@
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
-    <solid android:color="@color/apps_view_scrollbar_thumb_color" />
-    <size android:width="@dimen/apps_view_fast_scroll_bar_width" />
+    <solid android:color="@color/all_apps_scrollbar_thumb_color" />
+    <size android:width="@dimen/all_apps_fast_scroll_bar_width" />
 </shape>
\ No newline at end of file
diff --git a/res/drawable/apps_search_bg.xml b/res/drawable/all_apps_search_bg.xml
similarity index 100%
rename from res/drawable/apps_search_bg.xml
rename to res/drawable/all_apps_search_bg.xml
diff --git a/res/drawable/apps_list_search_bg.xml b/res/drawable/apps_list_search_bg.xml
deleted file mode 100644
index 9bb6d81..0000000
--- a/res/drawable/apps_list_search_bg.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-       android:shape="rectangle">
-    <solid android:color="@color/quantum_panel_bg_color" />
-    <corners
-        android:bottomLeftRadius="2dp"
-        android:bottomRightRadius="2dp" />
-</shape>
\ No newline at end of file
diff --git a/res/drawable/remove_target_selector.xml b/res/drawable/bg_screenpanel.xml
similarity index 77%
rename from res/drawable/remove_target_selector.xml
rename to res/drawable/bg_screenpanel.xml
index 9025e8a..cdb71df 100644
--- a/res/drawable/remove_target_selector.xml
+++ b/res/drawable/bg_screenpanel.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2007, The Android Open Source Project
+** Copyright 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.
@@ -18,7 +18,9 @@
 */
 -->
 
-<transition xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@drawable/ic_launcher_remove_normal"  />
-    <item android:drawable="@drawable/ic_launcher_remove_active"  />
-</transition>
+<transition xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <item android:drawable="@drawable/screenpanel"/>
+    <item android:drawable="@drawable/screenpanel_hover"/>
+
+</transition>
\ No newline at end of file
diff --git a/res/drawable/info_target_selector.xml b/res/drawable/info_target_selector.xml
deleted file mode 100644
index 51caece..0000000
--- a/res/drawable/info_target_selector.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2011, 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.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<transition xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@drawable/ic_launcher_info_normal"  />
-    <item android:drawable="@drawable/ic_launcher_info_active"  />
-</transition>
diff --git a/res/drawable/uninstall_target_selector.xml b/res/drawable/uninstall_target_selector.xml
deleted file mode 100644
index 175cc20..0000000
--- a/res/drawable/uninstall_target_selector.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2007, 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.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<transition xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@drawable/ic_launcher_uninstall_normal"  />
-    <item android:drawable="@drawable/ic_launcher_uninstall_active"  />
-</transition>
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index f0f7bbb..bf9296f 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -21,7 +21,6 @@
     android:id="@+id/launcher"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@drawable/workspace_bg"
     android:fitsSystemWindows="true">
 
     <com.android.launcher3.DragLayer
@@ -62,7 +61,7 @@
             android:layout_height="match_parent"
             android:visibility="invisible" />
 
-        <include layout="@layout/apps_view"
+        <include layout="@layout/all_apps"
             android:id="@+id/apps_view"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index 5a018c5..8ff7762 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -22,7 +22,6 @@
     android:id="@+id/launcher"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@drawable/workspace_bg"
     android:fitsSystemWindows="true">
 
     <com.android.launcher3.DragLayer
@@ -72,7 +71,7 @@
             android:layout_height="match_parent"
             android:visibility="invisible" />
 
-        <include layout="@layout/apps_view"
+        <include layout="@layout/all_apps"
             android:id="@+id/apps_view"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
diff --git a/res/layout-sw600dp/apps_view.xml b/res/layout-sw600dp/all_apps.xml
similarity index 82%
rename from res/layout-sw600dp/apps_view.xml
rename to res/layout-sw600dp/all_apps.xml
index e6e0ec3..368e6ab 100644
--- a/res/layout-sw600dp/apps_view.xml
+++ b/res/layout-sw600dp/all_apps.xml
@@ -13,21 +13,21 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.launcher3.AppsContainerView
+<com.android.launcher3.allapps.AllAppsContainerView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/apps_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:padding="@dimen/apps_container_inset"
+    android:padding="@dimen/all_apps_container_inset"
     android:descendantFocusability="afterDescendants">
     <include
-        layout="@layout/apps_reveal_view"
+        layout="@layout/all_apps_reveal"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_gravity="center" />
     <include
-        layout="@layout/apps_list_view"
+        layout="@layout/all_apps_container"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_gravity="center" />
-</com.android.launcher3.AppsContainerView>
\ No newline at end of file
+</com.android.launcher3.allapps.AllAppsContainerView>
\ No newline at end of file
diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml
index a9601af..b27f33b 100644
--- a/res/layout-sw720dp/launcher.xml
+++ b/res/layout-sw720dp/launcher.xml
@@ -22,7 +22,6 @@
     android:id="@+id/launcher"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@drawable/workspace_bg"
     android:fitsSystemWindows="true">
 
     <com.android.launcher3.DragLayer
@@ -72,7 +71,7 @@
             android:layout_height="match_parent"
             android:visibility="invisible" />
 
-        <include layout="@layout/apps_view"
+        <include layout="@layout/all_apps"
             android:id="@+id/apps_view"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
diff --git a/res/layout/apps_view.xml b/res/layout/all_apps.xml
similarity index 88%
rename from res/layout/apps_view.xml
rename to res/layout/all_apps.xml
index 7f09f77..b907c34 100644
--- a/res/layout/apps_view.xml
+++ b/res/layout/all_apps.xml
@@ -16,20 +16,20 @@
 <!-- The top and bottom paddings are defined in this container, but since we want
      the list view to span the full width (for touch interception purposes), we
      will bake the left/right padding into that view's background itself. -->
-<com.android.launcher3.AppsContainerView
+<com.android.launcher3.allapps.AllAppsContainerView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/apps_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:descendantFocusability="afterDescendants">
     <include
-        layout="@layout/apps_reveal_view"
+        layout="@layout/all_apps_reveal"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_gravity="center" />
     <include
-        layout="@layout/apps_list_view"
+        layout="@layout/all_apps_container"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_gravity="center" />
-</com.android.launcher3.AppsContainerView>
\ No newline at end of file
+</com.android.launcher3.allapps.AllAppsContainerView>
\ No newline at end of file
diff --git a/res/layout/apps_list_view.xml b/res/layout/all_apps_container.xml
similarity index 82%
rename from res/layout/apps_list_view.xml
rename to res/layout/all_apps_container.xml
index 6f9be05..a20ab46 100644
--- a/res/layout/apps_list_view.xml
+++ b/res/layout/all_apps_container.xml
@@ -14,7 +14,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.launcher3.AppsRecyclerViewContainer
+<com.android.launcher3.allapps.AllAppsRecyclerViewContainerView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/apps_list"
     android:layout_width="match_parent"
@@ -23,12 +23,12 @@
     android:focusableInTouchMode="true"
     android:visibility="gone" >
 
-    <com.android.launcher3.AppsContainerRecyclerView
+    <com.android.launcher3.allapps.AllAppsRecyclerView
         android:id="@+id/apps_list_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_gravity="center_horizontal|top"
-        android:layout_marginTop="@dimen/apps_search_bar_height"
+        android:layout_marginTop="@dimen/all_apps_search_bar_height"
         android:clipToPadding="false"
         android:descendantFocusability="afterDescendants"
         android:focusable="true" />
@@ -37,8 +37,10 @@
         android:id="@+id/prediction_bar"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginTop="@dimen/apps_search_bar_height"
+        android:layout_marginTop="@dimen/all_apps_search_bar_height"
         android:orientation="horizontal"
+        android:descendantFocusability="afterDescendants"
+        android:focusable="true"
         android:visibility="invisible" >
     </LinearLayout>
 
@@ -47,8 +49,8 @@
     <FrameLayout
         android:id="@+id/header"
         android:layout_width="match_parent"
-        android:layout_height="@dimen/apps_search_bar_height"
-        android:background="@drawable/apps_search_bg" >
+        android:layout_height="@dimen/all_apps_search_bar_height"
+        android:background="@drawable/all_apps_search_bg" >
 
         <LinearLayout
             android:id="@+id/app_search_container"
@@ -69,14 +71,14 @@
                 android:paddingTop="13dp"
                 android:src="@drawable/ic_arrow_back_grey" />
 
-            <com.android.launcher3.AppsContainerSearchEditTextView
-                android:id="@+id/app_search_box"
+            <com.android.launcher3.allapps.AllAppsSearchEditView
+                android:id="@+id/apps_search_box"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 android:background="@android:color/transparent"
                 android:focusableInTouchMode="true"
                 android:gravity="fill_horizontal"
-                android:hint="@string/apps_view_search_bar_hint"
+                android:hint="@string/all_apps_search_bar_hint"
                 android:imeOptions="actionDone|flagNoExtractUi"
                 android:maxLines="1"
                 android:paddingBottom="16dp"
@@ -96,10 +98,10 @@
             android:layout_gravity="end|center_vertical"
             android:layout_marginEnd="6dp"
             android:layout_marginRight="6dp"
-            android:contentDescription="@string/apps_view_search_bar_hint"
+            android:contentDescription="@string/all_apps_search_bar_hint"
             android:paddingBottom="13dp"
             android:paddingTop="13dp"
             android:src="@drawable/ic_search_grey" />
     </FrameLayout>
 
-</com.android.launcher3.AppsRecyclerViewContainer>
\ No newline at end of file
+</com.android.launcher3.allapps.AllAppsRecyclerViewContainerView>
\ No newline at end of file
diff --git a/res/layout/apps_empty_view.xml b/res/layout/all_apps_empty_search.xml
similarity index 93%
rename from res/layout/apps_empty_view.xml
rename to res/layout/all_apps_empty_search.xml
index e4c4e2e..f60c4a0 100644
--- a/res/layout/apps_empty_view.xml
+++ b/res/layout/all_apps_empty_search.xml
@@ -21,7 +21,7 @@
     android:gravity="center"
     android:paddingTop="24dp"
     android:paddingBottom="24dp"
-    android:paddingRight="@dimen/apps_grid_view_start_margin"
+    android:paddingRight="@dimen/all_apps_grid_view_start_margin"
     android:textSize="16sp"
     android:textColor="#4c4c4c"
     android:focusable="false" />
diff --git a/res/layout/apps_grid_icon_view.xml b/res/layout/all_apps_icon.xml
similarity index 89%
rename from res/layout/apps_grid_icon_view.xml
rename to res/layout/all_apps_icon.xml
index 7165f38..10ed25c 100644
--- a/res/layout/apps_grid_icon_view.xml
+++ b/res/layout/all_apps_icon.xml
@@ -21,8 +21,8 @@
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_gravity="left|center_vertical"
-    android:paddingTop="@dimen/apps_icon_top_bottom_padding"
-    android:paddingBottom="@dimen/apps_icon_top_bottom_padding"
+    android:paddingTop="@dimen/all_apps_icon_top_bottom_padding"
+    android:paddingBottom="@dimen/all_apps_icon_top_bottom_padding"
     android:focusable="true"
     android:background="@drawable/focusable_view_bg"
     launcher:iconDisplay="all_apps" />
diff --git a/res/layout/apps_prediction_bar_icon_view.xml b/res/layout/all_apps_prediction_bar_icon.xml
similarity index 100%
rename from res/layout/apps_prediction_bar_icon_view.xml
rename to res/layout/all_apps_prediction_bar_icon.xml
diff --git a/res/layout/apps_reveal_view.xml b/res/layout/all_apps_reveal.xml
similarity index 100%
rename from res/layout/apps_reveal_view.xml
rename to res/layout/all_apps_reveal.xml
diff --git a/res/layout/application.xml b/res/layout/app_icon.xml
similarity index 100%
rename from res/layout/application.xml
rename to res/layout/app_icon.xml
diff --git a/res/layout/search_drop_target_bar.xml b/res/layout/search_drop_target_bar.xml
index b0435aa..4737ee1 100644
--- a/res/layout/search_drop_target_bar.xml
+++ b/res/layout/search_drop_target_bar.xml
@@ -36,8 +36,6 @@
             <com.android.launcher3.DeleteDropTarget
                 android:id="@+id/delete_target_text"
                 style="@style/DropTargetButton"
-                android:drawableLeft="@drawable/remove_target_selector"
-                android:drawableStart="@drawable/remove_target_selector"
                 android:text="@string/delete_target_label" />
         </FrameLayout>
 
@@ -50,8 +48,6 @@
             <com.android.launcher3.InfoDropTarget
                 android:id="@+id/info_target_text"
                 style="@style/DropTargetButton"
-                android:drawableLeft="@drawable/info_target_selector"
-                android:drawableStart="@drawable/info_target_selector"
                 android:text="@string/info_target_label" />
         </FrameLayout>
 
@@ -64,8 +60,6 @@
             <com.android.launcher3.UninstallDropTarget
                 android:id="@+id/uninstall_target_text"
                 style="@style/DropTargetButton"
-                android:drawableLeft="@drawable/uninstall_target_selector"
-                android:drawableStart="@drawable/uninstall_target_selector"
                 android:text="@string/delete_target_uninstall_label" />
         </FrameLayout>
     </LinearLayout>
diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml
index 196dc45..2615ddb 100644
--- a/res/layout/widgets_view.xml
+++ b/res/layout/widgets_view.xml
@@ -34,7 +34,7 @@
         android:focusable="false"
         android:visibility="invisible" />
 
-        <com.android.launcher3.widget.WidgetsContainerRecyclerView
+        <com.android.launcher3.widget.WidgetsRecyclerView
             android:id="@+id/widgets_list_view"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
diff --git a/res/values-sw340dp-port/config.xml b/res/values-sw340dp-port/config.xml
deleted file mode 100644
index 5f71077..0000000
--- a/res/values-sw340dp-port/config.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 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.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resources>
-</resources>
diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml
index c366ef3..994c192 100644
--- a/res/values-sw600dp/dimens.xml
+++ b/res/values-sw600dp/dimens.xml
@@ -17,11 +17,11 @@
 <resources>
     <dimen name="app_icon_size">64dp</dimen>
 
-<!-- Apps view -->
-    <dimen name="apps_container_inset">18dp</dimen>
-    <dimen name="apps_grid_view_start_margin">0dp</dimen>
-    <dimen name="apps_view_section_text_size">26sp</dimen>
-    <dimen name="apps_icon_top_bottom_padding">12dp</dimen>
+<!-- All Apps -->
+    <dimen name="all_apps_container_inset">18dp</dimen>
+    <dimen name="all_apps_grid_view_start_margin">0dp</dimen>
+    <dimen name="all_apps_grid_section_text_size">26sp</dimen>
+    <dimen name="all_apps_icon_top_bottom_padding">12dp</dimen>
 
 <!-- Cling -->
     <dimen name="cling_migration_logo_height">400dp</dimen>
diff --git a/res/values-sw600dp/styles.xml b/res/values-sw600dp/styles.xml
deleted file mode 100644
index bcbbafd..0000000
--- a/res/values-sw600dp/styles.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2012 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.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
--->
-
-<resources>
-</resources>
diff --git a/res/values-sw720dp-land/dimens.xml b/res/values-sw720dp-land/dimens.xml
index ca13db0..514980f 100644
--- a/res/values-sw720dp-land/dimens.xml
+++ b/res/values-sw720dp-land/dimens.xml
@@ -15,10 +15,6 @@
 -->
 
 <resources>
-<!-- AppsCustomize -->
-    <integer name="apps_customize_widget_cell_count_x">4</integer>
-    <integer name="apps_customize_widget_cell_count_y">2</integer>
-
     <!-- the area at the edge of the screen that makes the workspace go left
          or right while you're dragging. -->
     <dimen name="scroll_zone">100dip</dimen>
diff --git a/res/values-sw720dp/config.xml b/res/values-sw720dp/config.xml
index af6751e..94cffcb 100644
--- a/res/values-sw720dp/config.xml
+++ b/res/values-sw720dp/config.xml
@@ -2,7 +2,7 @@
     <bool name="config_largeHeap">true</bool>
     <bool name="is_large_tablet">true</bool>
 
-<!-- AllApps/Customize/AppsCustomize -->
+<!-- All Apps & Widgets -->
     <!-- Out of 100, the percent to shrink the workspace during spring loaded mode. -->
     <integer name="config_workspaceSpringLoadShrinkPercentage">90</integer>
 
diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml
index 39b0c80..89942f7 100644
--- a/res/values-sw720dp/dimens.xml
+++ b/res/values-sw720dp/dimens.xml
@@ -16,8 +16,10 @@
 
 <resources>
     <dimen name="app_icon_size">72dp</dimen>
-    <dimen name="apps_search_bar_height">54dp</dimen>
-    <dimen name="apps_icon_top_bottom_padding">16dp</dimen>
+
+<!-- All Apps -->
+    <dimen name="all_apps_search_bar_height">54dp</dimen>
+    <dimen name="all_apps_icon_top_bottom_padding">16dp</dimen>
 
 <!-- QSB -->
     <dimen name="toolbar_button_vertical_padding">8dip</dimen>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 782d050..827332a 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -72,16 +72,6 @@
         <attr name="maxGap" format="dimension" />
     </declare-styleable>
 
-    <!-- StrokedTextView specific attributes. -->
-    <declare-styleable name="StrokedTextView">
-        <!-- The color of the stroke outline -->
-        <attr name="strokeColor" format="color" />
-        <!-- The color of the text -->
-        <attr name="strokeTextColor" format="color" />
-        <!-- The width of the stroke -->
-        <attr name="strokeWidth" format="float" />
-    </declare-styleable>
-
     <!-- PagedView specific attributes. These attributes are used to customize
          a PagedView view in XML files. -->
     <declare-styleable name="PagedView">
@@ -93,23 +83,6 @@
         <attr name="pageIndicator" format="reference" />
     </declare-styleable>
 
-    <!-- AppsCustomizePagedView specific attributes.  These attributes are used to
-         customize an AppsCustomizePagedView in xml files. -->
-    <declare-styleable name="AppsCustomizePagedView">
-        <!-- Max number of cells of applications horizontally -->
-        <attr name="maxAppCellCountX" format="integer" />
-        <!-- Max number of cells of applications vertically -->
-        <attr name="maxAppCellCountY" format="integer" />
-        <!-- Horizontal spacing between widgets and wallpapers -->
-        <attr name="widgetCellWidthGap" format="dimension" />
-        <!-- Vertical spacing between widgets -->
-        <attr name="widgetCellHeightGap" format="dimension" />
-        <!-- Number of widgets horizontally -->
-        <attr name="widgetCountX" format="integer" />
-        <!-- Number of widgets vertically -->
-        <attr name="widgetCountY" format="integer" />
-    </declare-styleable>
-
     <!-- XML attributes used by default_workspace.xml -->
     <declare-styleable name="Favorite">
         <attr name="className" format="string" />
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 1e89615..e2b8a2e 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -20,9 +20,9 @@
 <resources>
     <!-- The color tints to apply to the text and drag view when hovering
          over the delete target or the info target -->
-    <color name="delete_target_hover_tint">#DAC1C1C1</color>
-    <color name="uninstall_target_hover_tint">#DAF0592B</color>
-    <color name="info_target_hover_tint">#DA009688</color>
+    <color name="delete_target_hover_tint">#FFC1C1C1</color>
+    <color name="uninstall_target_hover_tint">#FFF0592B</color>
+    <color name="info_target_hover_tint">#FF009688</color>
     <color name="cling_scrim_background">#80000000</color>
 
     <color name="focused_background">#80c6c5c5</color>
@@ -36,9 +36,9 @@
     <color name="outline_color">#FFFFFFFF</color>
     <color name="widget_text_panel">#FF374248</color>
 
-    <!-- Apps view -->
-    <color name="apps_view_scrollbar_thumb_color">#009688</color>
-    <color name="apps_view_section_text_color">#009688</color>
+    <!-- All Apps -->
+    <color name="all_apps_scrollbar_thumb_color">#009688</color>
+    <color name="all_apps_grid_section_text_color">#009688</color>
 
     <!-- Widgets view -->
     <color name="widgets_view_section_text_color">#FFFFFF</color>
diff --git a/res/values/config.xml b/res/values/config.xml
index b6e633c..fbce3a4 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -1,8 +1,5 @@
 <resources>
 <!-- Dynamic Grid -->
-    <integer name="config_dynamic_grid_max_long_edge_cell_count">6</integer>
-    <integer name="config_dynamic_grid_max_short_edge_cell_count">5</integer>
-    <integer name="config_dynamic_grid_min_edge_cell_count">3</integer>
     <!-- Out of 100, the percent of space the overview bar should try and take vertically. -->
     <integer name="config_dynamic_grid_overview_icon_zone_percentage">20</integer>
     <!-- Out of 100, the percent to shrink the workspace during overview mode. -->
@@ -24,21 +21,20 @@
 <!-- DragController -->
     <integer name="config_flingToDeleteMinVelocity">-1500</integer>
 
-<!-- AllApps/Customize/AppsCustomize -->
+<!-- AllApps & Launcher transitions -->
     <!-- The alpha of the AppsCustomize bg in spring loaded mode -->
     <integer name="config_workspaceScrimAlpha">55</integer>
-    <integer name="config_workspaceUnshrinkTime">100</integer>
+    <integer name="config_allAppsTransitionTime">100</integer>
     <integer name="config_overviewTransitionTime">250</integer>
 
     <!-- Out of 100, the percent to shrink the workspace during spring loaded mode. -->
     <integer name="config_workspaceSpringLoadShrinkPercentage">80</integer>
 
-    <!-- Fade/zoom in/out duration & scale in the AllApps transition.
-         Note: This should be less than the workspaceShrinkTime as they happen together. -->
-    <integer name="config_appsCustomizeRevealTime">220</integer>
-    <integer name="config_appsCustomizeWorkspaceShrinkTime">300</integer>
-
-    <integer name="config_appsCustomizeItemsAlphaStagger">60</integer>
+    <!-- Fade/zoom in/out duration & scale in a Launcher overlay transition.
+         Note: This should be less than the config_overlayTransitionTime as they happen together. -->
+    <integer name="config_overlayRevealTime">220</integer>
+    <integer name="config_overlayTransitionTime">300</integer>
+    <integer name="config_overlayItemsAlphaStagger">60</integer>
 
     <!-- This constant stores the ratio of the all apps button drawable which
          is used for internal (baked-in) padding -->
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 60591e1..246adcd 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -15,13 +15,14 @@
 -->
 
 <resources>
+    <dimen name="app_icon_size">48dp</dimen>
+
 <!-- Dynamic Grid -->
     <dimen name="dynamic_grid_edge_margin">6dp</dimen>
     <dimen name="dynamic_grid_search_bar_max_width">500dp</dimen>
     <dimen name="dynamic_grid_search_bar_height">56dp</dimen>
     <dimen name="dynamic_grid_page_indicator_height">20dp</dimen>
     <dimen name="dynamic_grid_icon_drawable_padding">4dp</dimen>
-    <dimen name="dynamic_grid_all_apps_cell_padding">18dp</dimen>
     <dimen name="dynamic_grid_workspace_page_spacing">8dp</dimen>
     <dimen name="dynamic_grid_overview_min_icon_zone_height">80dp</dimen>
     <dimen name="dynamic_grid_overview_max_icon_zone_height">120dp</dimen>
@@ -43,54 +44,34 @@
 
 <!-- Workspace -->
     <dimen name="workspace_max_gap">16dp</dimen>
-    <dimen name="workspace_overscroll_drawable_padding">0dp</dimen>
 
 <!-- QSB -->
     <dimen name="toolbar_button_vertical_padding">4dip</dimen>
     <dimen name="toolbar_button_horizontal_padding">12dip</dimen>
 
-<!-- Apps view -->
-    <dimen name="apps_container_width">0dp</dimen>
-    <dimen name="apps_container_inset">8dp</dimen>
-    <dimen name="apps_grid_view_start_margin">56dp</dimen>
-    <dimen name="apps_grid_section_y_offset">8dp</dimen>
-    <dimen name="apps_view_section_text_size">24sp</dimen>
-    <dimen name="apps_view_fast_scroll_bar_width">4dp</dimen>
-    <dimen name="apps_view_fast_scroll_bar_min_height">64dp</dimen>
-    <dimen name="apps_view_fast_scroll_scrubber_touch_inset">-16dp</dimen>
-    <dimen name="apps_view_fast_scroll_popup_size">72dp</dimen>
-    <dimen name="apps_view_fast_scroll_text_size">48dp</dimen>
-    <dimen name="apps_search_bar_height">52dp</dimen>
-    <dimen name="apps_icon_top_bottom_padding">8dp</dimen>
-    <dimen name="apps_prediction_icon_top_bottom_padding">12dp</dimen>
-
+<!-- All Apps -->
     <!-- Note: This needs to match the fixed insets for the search box. -->
     <dimen name="container_fixed_bounds_inset">8dp</dimen>
 
-<!-- AllApps/Customize/AppsCustomize -->
-    <dimen name="app_icon_size">48dp</dimen>
+    <dimen name="all_apps_container_inset">8dp</dimen>
+    <dimen name="all_apps_grid_view_start_margin">56dp</dimen>
+    <dimen name="all_apps_grid_section_y_offset">8dp</dimen>
+    <dimen name="all_apps_grid_section_text_size">24sp</dimen>
+    <dimen name="all_apps_search_bar_height">52dp</dimen>
+    <dimen name="all_apps_icon_top_bottom_padding">8dp</dimen>
+    <dimen name="all_apps_icon_left_right_padding">18dp</dimen>
+    <dimen name="all_apps_prediction_icon_top_bottom_padding">12dp</dimen>
 
-    <!-- Drag padding to add to the bottom of drop targets -->
-    <dimen name="drop_target_drag_padding">14dp</dimen>
-    <dimen name="drop_target_text_size">14sp</dimen>
+    <dimen name="all_apps_fast_scroll_bar_width">4dp</dimen>
+    <dimen name="all_apps_fast_scroll_bar_min_height">64dp</dimen>
+    <dimen name="all_apps_fast_scroll_scrubber_touch_inset">-16dp</dimen>
+    <dimen name="all_apps_fast_scroll_popup_size">72dp</dimen>
+    <dimen name="all_apps_fast_scroll_text_size">48dp</dimen>
 
     <dimen name="all_apps_header_max_elevation">4dp</dimen>
     <dimen name="all_apps_header_scroll_to_elevation">16dp</dimen>
     <dimen name="all_apps_header_shadow_height">6dp</dimen>
 
-<!-- Dragging -->
-    <!-- the area at the edge of the screen that makes the workspace go left
-         or right while you're dragging. -->
-    <dimen name="scroll_zone">20dp</dimen>
-
-    <!-- When dragging an item, how much bigger (fixed dps) the dragged view
-         should be. If 0, it will not be scaled at all. -->
-    <dimen name="dragViewScale">12dp</dimen>
-
-    <!-- Elevation for the drag view. It should be larger than elevation of all other drag sources
-         and drop targets like all-apps and folders -->
-    <dimen name="drag_elevation">30dp</dimen>
-
 <!-- Widget tray -->
     <dimen name="widget_container_inset">8dp</dimen>
     <dimen name="widget_preview_label_vertical_padding">8dp</dimen>
@@ -110,6 +91,23 @@
     <dimen name="shortcut_preview_padding_right">0dp</dimen>
     <dimen name="shortcut_preview_padding_top">0dp</dimen>
 
+<!-- Dragging -->
+    <!-- Drag padding to add to the bottom of drop targets -->
+    <dimen name="drop_target_drag_padding">14dp</dimen>
+    <dimen name="drop_target_text_size">14sp</dimen>
+
+    <!-- the area at the edge of the screen that makes the workspace go left
+         or right while you're dragging. -->
+    <dimen name="scroll_zone">20dp</dimen>
+
+    <!-- When dragging an item, how much bigger (fixed dps) the dragged view
+         should be. If 0, it will not be scaled at all. -->
+    <dimen name="dragViewScale">12dp</dimen>
+
+    <!-- Elevation for the drag view. It should be larger than elevation of all other drag sources
+         and drop targets like all-apps and folders -->
+    <dimen name="drag_elevation">30dp</dimen>
+
 <!-- Theme -->
     <dimen name="quantum_panel_outer_padding">4dp</dimen>
 
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 18f97c8..a8c668d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -47,20 +47,20 @@
 
     <string name="toggle_weight_watcher">Show Mem</string>
 
-    <!-- AppsCustomize pane -->
+    <!-- Widgets -->
     <!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] -->
     <string name="long_press_widget_to_add">Touch &amp; hold to pick up a widget.</string>
     <!-- The format string for the dimensions of a widget in the drawer -->
     <!-- There is a special version of this format string for Farsi -->
     <string name="widget_dims_format">%1$d \u00d7 %2$d</string>
 
-    <!-- Apps view -->
+    <!-- All Apps -->
     <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
-    <string name="apps_view_search_bar_hint">Search Apps</string>
+    <string name="all_apps_search_bar_hint">Search Apps</string>
     <!-- Loading apps text. [CHAR_LIMIT=50] -->
-    <string name="loading_apps_message">Loading Apps&#8230;</string>
+    <string name="all_apps_loading_message">Loading Apps&#8230;</string>
     <!-- No-search-results text. [CHAR_LIMIT=50] -->
-    <string name="apps_view_no_search_results">No Apps found matching \"<xliff:g id="query" example="Android">%1$s</xliff:g>\"</string>
+    <string name="all_apps_no_search_results">No Apps found matching \"<xliff:g id="query" example="Android">%1$s</xliff:g>\"</string>
 
     <!-- Drag and drop -->
     <skip />
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index 58a57a1..9c87ced 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -38,7 +38,7 @@
     /**
      * The intent used to start the application.
      */
-    Intent intent;
+    public Intent intent;
 
     /**
      * A bitmap version of the application icon.
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index e6bf525..ea7c221 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -75,8 +75,8 @@
         mResizeMode = info.resizeMode;
         mDragLayer = dragLayer;
 
-        mMinHSpan = info.minSpanX;
-        mMinVSpan = info.minSpanY;
+        mMinHSpan = info.getMinSpanX(mLauncher);
+        mMinVSpan = info.getMinSpanY(mLauncher);
 
         setBackgroundResource(R.drawable.widget_resize_shadow);
         setForeground(getResources().getDrawable(R.drawable.widget_resize_frame));
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index dac79a8..20c9314 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -78,7 +78,7 @@
 
     static AutoInstallsLayout get(Context context, String pkg, Resources targetRes,
             AppWidgetHost appWidgetHost, LayoutParserCallback callback) {
-        DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
+        InvariantDeviceProfile grid = LauncherAppState.getInstance().getInvariantDeviceProfile();
 
         // Try with grid size and hotseat count
         String layoutName = String.format(Locale.ENGLISH, FORMATTED_LAYOUT_RES_WITH_HOSTEAT,
@@ -165,7 +165,7 @@
             LayoutParserCallback callback, Resources res,
             int layoutId, String rootTag) {
         this(context, appWidgetHost, callback, res, layoutId, rootTag,
-                LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile().hotseatAllAppsRank);
+                LauncherAppState.getInstance().getInvariantDeviceProfile().hotseatAllAppsRank);
     }
 
     public AutoInstallsLayout(Context context, AppWidgetHost appWidgetHost,
diff --git a/src/com/android/launcher3/BaseContainerRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
similarity index 91%
rename from src/com/android/launcher3/BaseContainerRecyclerView.java
rename to src/com/android/launcher3/BaseRecyclerView.java
index e52d887..b63ef78 100644
--- a/src/com/android/launcher3/BaseContainerRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -26,7 +26,7 @@
  * A base {@link RecyclerView}, which will NOT intercept a touch sequence unless the scrolling
  * velocity is below a predefined threshold.
  */
-public class BaseContainerRecyclerView extends RecyclerView
+public class BaseRecyclerView extends RecyclerView
         implements RecyclerView.OnItemTouchListener {
 
     private static final int SCROLL_DELTA_THRESHOLD_DP = 4;
@@ -35,15 +35,15 @@
     @Thunk int mDy = 0;
     private float mDeltaThreshold;
 
-    public BaseContainerRecyclerView(Context context) {
+    public BaseRecyclerView(Context context) {
         this(context, null);
     }
 
-    public BaseContainerRecyclerView(Context context, AttributeSet attrs) {
+    public BaseRecyclerView(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public BaseContainerRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public BaseRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD_DP;
 
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index d530009..edf5021 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -93,7 +93,7 @@
     public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = ((Launcher) context).getDeviceProfile();
 
         TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.BubbleTextView, defStyle, 0);
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index b8214d1..4cd28c0 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -16,15 +16,20 @@
 
 package com.android.launcher3;
 
+import android.animation.AnimatorSet;
+import android.animation.FloatArrayEvaluator;
 import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.TransitionDrawable;
 import android.os.Build;
 import android.util.AttributeSet;
 import android.view.View;
@@ -56,9 +61,11 @@
     protected int mHoverColor = 0;
 
     protected ColorStateList mOriginalTextColor;
-    protected TransitionDrawable mDrawable;
+    protected Drawable mDrawable;
 
-    private ObjectAnimator mCurrentColorAnim;
+    private AnimatorSet mCurrentColorAnim;
+    private ColorMatrix mSrcFilter, mDstFilter, mCurrentFilter;
+
 
     public ButtonDropTarget(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
@@ -85,21 +92,14 @@
 
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
     protected void setDrawable(int resId) {
-        // Get the hover color
-        mDrawable = (TransitionDrawable) getCurrentDrawable();
+        // We do not set the drawable in the xml as that inflates two drawables corresponding to
+        // drawableLeft and drawableStart.
+        mDrawable = getResources().getDrawable(resId);
 
-        if (mDrawable == null) {
-            // TODO: investigate why this is ever happening. Presently only on one known device.
-            mDrawable = (TransitionDrawable) getResources().getDrawable(resId);
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
-                setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null);
-            } else {
-                setCompoundDrawablesWithIntrinsicBounds(mDrawable, null, null, null);
-            }
-        }
-
-        if (null != mDrawable) {
-            mDrawable.setCrossFadeEnabled(true);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null);
+        } else {
+            setCompoundDrawablesWithIntrinsicBounds(mDrawable, null, null, null);
         }
     }
 
@@ -111,16 +111,6 @@
         mSearchDropTargetBar = searchDropTargetBar;
     }
 
-    protected Drawable getCurrentDrawable() {
-        Drawable[] drawables = getCompoundDrawables();
-        for (int i = 0; i < drawables.length; ++i) {
-            if (drawables[i] != null) {
-                return drawables[i];
-            }
-        }
-        return null;
-    }
-
     @Override
     public void onFlingToDelete(DragObject d, PointF vec) { }
 
@@ -128,10 +118,13 @@
     public final void onDragEnter(DragObject d) {
         d.dragView.setColor(mHoverColor);
         if (Utilities.isLmpOrAbove()) {
-            mDrawable.startTransition(DragView.COLOR_CHANGE_DURATION);
             animateTextColor(mHoverColor);
         } else {
-            mDrawable.startTransition(0);
+            if (mCurrentFilter == null) {
+                mCurrentFilter = new ColorMatrix();
+            }
+            DragView.setColorScale(mHoverColor, mCurrentFilter);
+            mDrawable.setColorFilter(new ColorMatrixColorFilter(mCurrentFilter));
             setTextColor(mHoverColor);
         }
     }
@@ -141,12 +134,11 @@
         // Do nothing
     }
 
-    protected void resetHoverColor() {
+	protected void resetHoverColor() {
         if (Utilities.isLmpOrAbove()) {
-            mDrawable.reverseTransition(DragView.COLOR_CHANGE_DURATION);
             animateTextColor(mOriginalTextColor.getDefaultColor());
         } else {
-            mDrawable.resetTransition();
+            mDrawable.setColorFilter(null);
             setTextColor(mOriginalTextColor);
         }
     }
@@ -156,8 +148,32 @@
         if (mCurrentColorAnim != null) {
             mCurrentColorAnim.cancel();
         }
-        mCurrentColorAnim = ObjectAnimator.ofArgb(this, "textColor", targetColor);
+
+        mCurrentColorAnim = new AnimatorSet();
         mCurrentColorAnim.setDuration(DragView.COLOR_CHANGE_DURATION);
+
+        if (mSrcFilter == null) {
+            mSrcFilter = new ColorMatrix();
+            mDstFilter = new ColorMatrix();
+            mCurrentFilter = new ColorMatrix();
+        }
+
+        DragView.setColorScale(getTextColor(), mSrcFilter);
+        DragView.setColorScale(targetColor, mDstFilter);
+        ValueAnimator anim1 = ValueAnimator.ofObject(
+                new FloatArrayEvaluator(mCurrentFilter.getArray()),
+                mSrcFilter.getArray(), mDstFilter.getArray());
+        anim1.addUpdateListener(new AnimatorUpdateListener() {
+
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                mDrawable.setColorFilter(new ColorMatrixColorFilter(mCurrentFilter));
+                invalidate();
+            }
+        });
+
+        mCurrentColorAnim.play(anim1);
+        mCurrentColorAnim.play(ObjectAnimator.ofArgb(this, "textColor", targetColor));
         mCurrentColorAnim.start();
     }
 
@@ -172,10 +188,10 @@
         }
     }
 
-    @Override
+	@Override
     public final void onDragStart(DragSource source, Object info, int dragAction) {
         mActive = supportsDrop(source, info);
-        mDrawable.resetTransition();
+        mDrawable.setColorFilter(null);
         if (mCurrentColorAnim != null) {
             mCurrentColorAnim.cancel();
             mCurrentColorAnim = null;
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 1d211bf..2b1cfe0 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -34,6 +34,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.TransitionDrawable;
 import android.os.Build;
 import android.os.Parcelable;
 import android.support.v4.view.ViewCompat;
@@ -87,31 +88,27 @@
 
     // These are temporary variables to prevent having to allocate a new object just to
     // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
-    private final int[] mTmpXY = new int[2];
     @Thunk final int[] mTmpPoint = new int[2];
-    int[] mTempLocation = new int[2];
+    @Thunk final int[] mTempLocation = new int[2];
 
     boolean[][] mOccupied;
     boolean[][] mTmpOccupied;
-    private boolean mLastDownOnOccupiedCell = false;
 
     private OnTouchListener mInterceptTouchListener;
 
     private ArrayList<FolderRingAnimator> mFolderOuterRings = new ArrayList<FolderRingAnimator>();
     private int[] mFolderLeaveBehindCell = {-1, -1};
 
-    private float FOREGROUND_ALPHA_DAMPER = 0.65f;
+    private static final float FOREGROUND_ALPHA_DAMPER = 0.65f;
     private int mForegroundAlpha = 0;
     private float mBackgroundAlpha;
 
-    private Drawable mNormalBackground;
-    private Drawable mActiveGlowBackground;
+    private static final int BACKGROUND_ACTIVATE_DURATION = 120;
+    private final TransitionDrawable mBackground;
+
+    private final Drawable mOverScrollLeft;
+    private final Drawable mOverScrollRight;
     private Drawable mOverScrollForegroundDrawable;
-    private Drawable mOverScrollLeft;
-    private Drawable mOverScrollRight;
-    private Rect mBackgroundRect;
-    private Rect mForegroundRect;
-    private int mForegroundPadding;
 
     // These values allow a fixed measurement to be set on the CellLayout.
     private int mFixedWidth = -1;
@@ -173,7 +170,7 @@
     private static final int INVALID_DIRECTION = -100;
     private DropTarget.DragEnforcer mDragEnforcer;
 
-    private Rect mTempRect = new Rect();
+    private final Rect mTempRect = new Rect();
 
     private final static Paint sPaint = new Paint();
 
@@ -199,8 +196,7 @@
         setClipToPadding(false);
         mLauncher = (Launcher) context;
 
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0);
 
         mCellWidth = mCellHeight = -1;
@@ -208,8 +204,8 @@
         mWidthGap = mOriginalWidthGap = 0;
         mHeightGap = mOriginalHeightGap = 0;
         mMaxGap = Integer.MAX_VALUE;
-        mCountX = (int) grid.numColumns;
-        mCountY = (int) grid.numRows;
+        mCountX = (int) grid.inv.numColumns;
+        mCountY = (int) grid.inv.numRows;
         mOccupied = new boolean[mCountX][mCountY];
         mTmpOccupied = new boolean[mCountX][mCountY];
         mPreviousReorderDirection[0] = INVALID_DIRECTION;
@@ -222,20 +218,15 @@
         final Resources res = getResources();
         mHotseatScale = (float) grid.hotseatIconSizePx / grid.iconSizePx;
 
-        mNormalBackground = res.getDrawable(R.drawable.screenpanel);
-        mActiveGlowBackground = res.getDrawable(R.drawable.screenpanel_hover);
+        mBackground = (TransitionDrawable) res.getDrawable(R.drawable.bg_screenpanel);
+        mBackground.setCallback(this);
 
         mOverScrollLeft = res.getDrawable(R.drawable.overscroll_glow_left);
         mOverScrollRight = res.getDrawable(R.drawable.overscroll_glow_right);
-        mForegroundPadding =
-                res.getDimensionPixelSize(R.dimen.workspace_overscroll_drawable_padding);
 
         mReorderPreviewAnimationMagnitude = (REORDER_PREVIEW_MAGNITUDE *
                 grid.iconSizePx);
 
-        mNormalBackground.setFilterBitmap(true);
-        mActiveGlowBackground.setFilterBitmap(true);
-
         // Initialize the data structures used for the drag visualization.
         mEaseOutInterpolator = new DecelerateInterpolator(2.5f); // Quint ease out
         mDragCell[0] = mDragCell[1] = -1;
@@ -293,9 +284,6 @@
             mDragOutlineAnims[i] = anim;
         }
 
-        mBackgroundRect = new Rect();
-        mForegroundRect = new Rect();
-
         mShortcutsAndWidgets = new ShortcutAndWidgetContainer(context);
         mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap,
                 mCountX, mCountY);
@@ -432,6 +420,11 @@
     void setIsDragOverlapping(boolean isDragOverlapping) {
         if (mIsDragOverlapping != isDragOverlapping) {
             mIsDragOverlapping = isDragOverlapping;
+            if (mIsDragOverlapping) {
+                mBackground.startTransition(BACKGROUND_ACTIVATE_DURATION);
+            } else {
+                mBackground.reverseTransition(BACKGROUND_ACTIVATE_DURATION);
+            }
             invalidate();
         }
     }
@@ -452,18 +445,7 @@
         // a drag). However, we also drag the mini hover background *over* one of those two
         // backgrounds
         if (mBackgroundAlpha > 0.0f) {
-            Drawable bg;
-
-            if (mIsDragOverlapping) {
-                // In the mini case, we draw the active_glow bg *over* the active background
-                bg = mActiveGlowBackground;
-            } else {
-                bg = mNormalBackground;
-            }
-
-            bg.setAlpha((int) (mBackgroundAlpha * 255));
-            bg.setBounds(mBackgroundRect);
-            bg.draw(canvas);
+            mBackground.draw(canvas);
         }
 
         final Paint paint = mDragOutlinePaint;
@@ -499,8 +481,7 @@
         int previewOffset = FolderRingAnimator.sPreviewSize;
 
         // The folder outer / inner ring image(s)
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         for (int i = 0; i < mFolderOuterRings.size(); i++) {
             FolderRingAnimator fra = mFolderOuterRings.get(i);
 
@@ -563,7 +544,6 @@
     protected void dispatchDraw(Canvas canvas) {
         super.dispatchDraw(canvas);
         if (mForegroundAlpha > 0) {
-            mOverScrollForegroundDrawable.setBounds(mForegroundRect);
             mOverScrollForegroundDrawable.draw(canvas);
         }
     }
@@ -841,9 +821,6 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-
         int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
         int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
         int widthSize = MeasureSpec.getSize(widthMeasureSpec);
@@ -851,8 +828,8 @@
         int childWidthSize = widthSize - (getPaddingLeft() + getPaddingRight());
         int childHeightSize = heightSize - (getPaddingTop() + getPaddingBottom());
         if (mFixedCellWidth < 0 || mFixedCellHeight < 0) {
-            int cw = grid.calculateCellWidth(childWidthSize, mCountX);
-            int ch = grid.calculateCellHeight(childHeightSize, mCountY);
+            int cw = DeviceProfile.calculateCellWidth(childWidthSize, mCountX);
+            int ch = DeviceProfile.calculateCellHeight(childHeightSize, mCountY);
             if (cw != mCellWidth || ch != mCellHeight) {
                 mCellWidth = cw;
                 mCellHeight = ch;
@@ -927,12 +904,12 @@
         super.onSizeChanged(w, h, oldw, oldh);
 
         // Expand the background drawing bounds by the padding baked into the background drawable
-        Rect padding = new Rect();
-        mNormalBackground.getPadding(padding);
-        mBackgroundRect.set(-padding.left, -padding.top, w + padding.right, h + padding.bottom);
+        mBackground.getPadding(mTempRect);
+        mBackground.setBounds(-mTempRect.left, -mTempRect.top,
+                w + mTempRect.right, h + mTempRect.bottom);
 
-        mForegroundRect.set(mForegroundPadding, mForegroundPadding,
-                w - mForegroundPadding, h - mForegroundPadding);
+        mOverScrollLeft.setBounds(0, 0, w, h);
+        mOverScrollRight.setBounds(0, 0, w, h);
     }
 
     @Override
@@ -952,10 +929,15 @@
     public void setBackgroundAlpha(float alpha) {
         if (mBackgroundAlpha != alpha) {
             mBackgroundAlpha = alpha;
-            invalidate();
+            mBackground.setAlpha((int) (mBackgroundAlpha * 255));
         }
     }
 
+    @Override
+    protected boolean verifyDrawable(Drawable who) {
+        return super.verifyDrawable(who) || (mIsDragTarget && who == mBackground);
+    }
+
     public void setShortcutAndWidgetAlpha(float alpha) {
         mShortcutsAndWidgets.setAlpha(alpha);
     }
@@ -1268,7 +1250,7 @@
                     hitMaxX = xSize >= spanX;
                     hitMaxY = ySize >= spanY;
                 }
-                final int[] cellXY = mTmpXY;
+                final int[] cellXY = mTmpPoint;
                 cellToCenterPoint(x, y, cellXY);
 
                 // We verify that the current rect is not a sub-rect of any of our previous
@@ -2729,18 +2711,16 @@
      * @param height Height in pixels
      * @param result An array of length 2 in which to store the result (may be null).
      */
-    public static int[] rectToCell(int width, int height, int[] result) {
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-        Rect padding = grid.getWorkspacePadding(grid.isLandscape ?
-                CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
+    public static int[] rectToCell(Launcher launcher, int width, int height, int[] result) {
+        DeviceProfile grid = launcher.getDeviceProfile();
+        Rect padding = grid.getWorkspacePadding(Utilities.isRtl(launcher.getResources()));
 
         // Always assume we're working with the smallest span to make sure we
         // reserve enough space in both orientations.
-        int parentWidth = grid.calculateCellWidth(grid.widthPx
-                - padding.left - padding.right, (int) grid.numColumns);
-        int parentHeight = grid.calculateCellHeight(grid.heightPx
-                - padding.top - padding.bottom, (int) grid.numRows);
+        int parentWidth = DeviceProfile.calculateCellWidth(grid.widthPx
+                - padding.left - padding.right, (int) grid.inv.numColumns);
+        int parentHeight = DeviceProfile.calculateCellHeight(grid.heightPx
+                - padding.top - padding.bottom, (int) grid.inv.numRows);
         int smallerSize = Math.min(parentWidth, parentHeight);
 
         // Always round up to next largest cell
@@ -2773,7 +2753,7 @@
             info.spanX = info.spanY = 1;
             return;
         }
-        int[] spans = rectToCell(minWidth, minHeight, null);
+        int[] spans = rectToCell(mLauncher, minWidth, minHeight, null);
         info.spanX = spans[0];
         info.spanY = spans[1];
     }
@@ -3025,10 +3005,6 @@
         }
     }
 
-    public boolean lastDownOnOccupiedCell() {
-        return mLastDownOnOccupiedCell;
-    }
-
     public boolean findVacantCell(int spanX, int spanY, int[] outXY) {
         return Utilities.findVacantCell(outXY, spanX, spanY, mCountX, mCountY, mOccupied);
     }
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 08186f5..fa6e74f 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -43,7 +43,7 @@
         // Get the hover color
         mHoverColor = getResources().getColor(R.color.delete_target_hover_tint);
 
-        setDrawable(R.drawable.remove_target_selector);
+        setDrawable(R.drawable.ic_launcher_remove_normal);
     }
 
     public static boolean supportsDrop(Object info) {
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index ad0afd9..bee6cb0 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -19,167 +19,103 @@
 import android.appwidget.AppWidgetHostView;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Paint;
 import android.graphics.Paint.FontMetrics;
 import android.graphics.Point;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.util.DisplayMetrics;
-import android.view.Display;
 import android.view.Gravity;
-import android.view.Surface;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
 import android.view.ViewGroup.MarginLayoutParams;
-import android.view.WindowManager;
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 
-import com.android.launcher3.util.Thunk;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-
-
-class DeviceProfileQuery {
-    DeviceProfile profile;
-    float widthDps;
-    float heightDps;
-    float value;
-    PointF dimens;
-
-    DeviceProfileQuery(DeviceProfile p, float v) {
-        widthDps = p.minWidthDps;
-        heightDps = p.minHeightDps;
-        value = v;
-        dimens = new PointF(widthDps, heightDps);
-        profile = p;
-    }
-}
-
 public class DeviceProfile {
-    public static interface DeviceProfileCallbacks {
-        public void onAvailableSizeChanged(DeviceProfile grid);
-    }
 
-    String name;
-    float minWidthDps;
-    float minHeightDps;
-    public int numRows;
-    public int numColumns;
-    public int numFolderRows;
-    public int numFolderColumns;
-    float numHotseatIcons;
-    float iconSize;
-    private float iconTextSize;
-    private int iconDrawablePaddingOriginalPx;
-    private float hotseatIconSize;
+    public final InvariantDeviceProfile inv;
 
-    int defaultLayoutId;
+    // Device properties
+    public final boolean isTablet;
+    public final boolean isLargeTablet;
+    public final boolean isPhone;
+    public final boolean transposeLayoutWithOrientation;
 
-    boolean isLandscape;
-    public boolean isTablet;
-    public boolean isLargeTablet;
-    public boolean isLayoutRtl;
+    // Device properties in current orientation
+    public final boolean isLandscape;
+    public final int widthPx;
+    public final int heightPx;
+    public final int availableWidthPx;
+    public final int availableHeightPx;
 
-    boolean transposeLayoutWithOrientation;
-    int desiredWorkspaceLeftRightMarginPx;
-    public int edgeMarginPx;
-    Rect defaultWidgetPadding;
+    // Overview mode
+    private final int overviewModeMinIconZoneHeightPx;
+    private final int overviewModeMaxIconZoneHeightPx;
+    private final int overviewModeBarItemWidthPx;
+    private final int overviewModeBarSpacerWidthPx;
+    private final float overviewModeIconZoneRatio;
+    private final float overviewModeScaleFactor;
 
-    int widthPx;
-    int heightPx;
-    public int availableWidthPx;
-    public int availableHeightPx;
-    int defaultPageSpacingPx;
+    // Workspace
+    private int desiredWorkspaceLeftRightMarginPx;
+    public final int edgeMarginPx;
+    public final Rect defaultWidgetPadding;
+    private final int pageIndicatorHeightPx;
+    private final int defaultPageSpacingPx;
+    private float dragViewScale;
 
-    int overviewModeMinIconZoneHeightPx;
-    int overviewModeMaxIconZoneHeightPx;
-    int overviewModeBarItemWidthPx;
-    int overviewModeBarSpacerWidthPx;
-    float overviewModeIconZoneRatio;
-    float overviewModeScaleFactor;
+    // Workspace icons
+    public int iconSizePx;
+    public int iconTextSizePx;
+    public int iconDrawablePaddingPx;
+    public int iconDrawablePaddingOriginalPx;
 
     public int cellWidthPx;
     public int cellHeightPx;
 
-    public int iconSizePx;
-    public int iconTextSizePx;
-    int iconDrawablePaddingPx;
-    int allAppsIconSizePx;
-    int allAppsIconTextSizePx;
-    int allAppsCellWidthPx;
-    int allAppsCellHeightPx;
-    int allAppsCellPaddingPx;
-    int folderBackgroundOffset;
-    int folderIconSizePx;
-    int folderCellWidthPx;
-    int folderCellHeightPx;
-    int hotseatCellWidthPx;
-    int hotseatCellHeightPx;
-    int hotseatIconSizePx;
-    int hotseatBarHeightPx;
-    int hotseatAllAppsRank;
-    int allAppsNumRows;
-    int allAppsNumCols;
-    int appsViewNumCols;
-    int appsViewNumPredictiveCols;
-    int searchBarSpaceWidthPx;
-    int searchBarSpaceHeightPx;
-    int pageIndicatorHeightPx;
-    int allAppsButtonVisualSize;
+    // Folder
+    public int folderBackgroundOffset;
+    public int folderIconSizePx;
+    public int folderCellWidthPx;
+    public int folderCellHeightPx;
 
-    float dragViewScale;
+    // Hotseat
+    public int hotseatCellWidthPx;
+    public int hotseatCellHeightPx;
+    public int hotseatIconSizePx;
+    private int hotseatBarHeightPx;
 
-    int allAppsShortEdgeCount = -1;
-    int allAppsLongEdgeCount = -1;
+    // All apps
+    public int allAppsNumCols;
+    public int allAppsNumPredictiveCols;
+    public int allAppsButtonVisualSize;
+    public final int allAppsIconSizePx;
+    public final int allAppsIconTextSizePx;
 
-    private ArrayList<DeviceProfileCallbacks> mCallbacks = new ArrayList<DeviceProfileCallbacks>();
+    // QSB
+    private int searchBarSpaceWidthPx;
+    private int searchBarSpaceHeightPx;
 
-    DeviceProfile(String n, float w, float h,
-            int r, int c, int fr, int fc,
-            float is, float its, float hs, float his, int dlId) {
-        // Ensure that we have an odd number of hotseat items (since we need to place all apps)
-        if (hs % 2 == 0) {
-            throw new RuntimeException("All Device Profiles must have an odd number of hotseat spaces");
-        }
+    public DeviceProfile(Context context, InvariantDeviceProfile inv,
+            Point minSize, Point maxSize,
+            int width, int height, boolean isLandscape) {
 
-        name = n;
-        minWidthDps = w;
-        minHeightDps = h;
+        this.inv = inv;
+        this.isLandscape = isLandscape;
 
-        numRows = r;
-        numColumns = c;
-        numFolderRows = fr;
-        numFolderColumns = fc;
-
-        iconSize = is;
-        iconTextSize = its;
-        numHotseatIcons = hs;
-        hotseatIconSize = his;
-        defaultLayoutId = dlId;
-    }
-
-    DeviceProfile() {
-    }
-
-    DeviceProfile(Context context,
-                  ArrayList<DeviceProfile> profiles,
-                  float minWidth, float minHeight,
-                  int wPx, int hPx,
-                  int awPx, int ahPx,
-                  Resources res) {
+        Resources res = context.getResources();
         DisplayMetrics dm = res.getDisplayMetrics();
-        ArrayList<DeviceProfileQuery> points =
-                new ArrayList<DeviceProfileQuery>();
+
+        // Constants from resources
+        isTablet = res.getBoolean(R.bool.is_tablet);
+        isLargeTablet = res.getBoolean(R.bool.is_large_tablet);
+        isPhone = !isTablet && !isLargeTablet;
+
+        // Some more constants
         transposeLayoutWithOrientation =
                 res.getBoolean(R.bool.hotseat_transpose_layout_with_orientation);
-        minWidthDps = minWidth;
-        minHeightDps = minHeight;
 
         ComponentName cn = new ComponentName(context.getPackageName(),
                 this.getClass().getName());
@@ -190,8 +126,6 @@
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_height);
         defaultPageSpacingPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_page_spacing);
-        allAppsCellPaddingPx =
-                res.getDimensionPixelSize(R.dimen.dynamic_grid_all_apps_cell_padding);
         overviewModeMinIconZoneHeightPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_min_icon_zone_height);
         overviewModeMaxIconZoneHeightPx =
@@ -204,185 +138,65 @@
                 res.getInteger(R.integer.config_dynamic_grid_overview_icon_zone_percentage) / 100f;
         overviewModeScaleFactor =
                 res.getInteger(R.integer.config_dynamic_grid_overview_scale_percentage) / 100f;
-
-        // Find the closes profile given the width/height
-        for (DeviceProfile p : profiles) {
-            points.add(new DeviceProfileQuery(p, 0f));
-        }
-        DeviceProfile closestProfile = findClosestDeviceProfile(minWidth, minHeight, points);
-
-        // Snap to the closest row count
-        numRows = closestProfile.numRows;
-
-        // Snap to the closest column count
-        numColumns = closestProfile.numColumns;
-
-        numFolderRows = closestProfile.numFolderRows;
-        numFolderColumns = closestProfile.numFolderColumns;
-
-        // Snap to the closest hotseat size
-        numHotseatIcons = closestProfile.numHotseatIcons;
-        hotseatAllAppsRank = (int) (numHotseatIcons / 2);
-
-        // Snap to the closest default layout id
-        defaultLayoutId = closestProfile.defaultLayoutId;
-
-        // Interpolate the icon size
-        points.clear();
-        for (DeviceProfile p : profiles) {
-            points.add(new DeviceProfileQuery(p, p.iconSize));
-        }
-        iconSize = invDistWeightedInterpolate(minWidth, minHeight, points);
-
-        // AllApps uses the original non-scaled icon size
-        allAppsIconSizePx = DynamicGrid.pxFromDp(iconSize, dm);
-
-        // Interpolate the icon text size
-        points.clear();
-        for (DeviceProfile p : profiles) {
-            points.add(new DeviceProfileQuery(p, p.iconTextSize));
-        }
-        iconTextSize = invDistWeightedInterpolate(minWidth, minHeight, points);
         iconDrawablePaddingOriginalPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding);
+
         // AllApps uses the original non-scaled icon text size
-        allAppsIconTextSizePx = DynamicGrid.pxFromDp(iconTextSize, dm);
+        allAppsIconTextSizePx = Utilities.pxFromDp(inv.iconTextSize, dm);
 
-        // Interpolate the hotseat icon size
-        points.clear();
-        for (DeviceProfile p : profiles) {
-            points.add(new DeviceProfileQuery(p, p.hotseatIconSize));
+        // AllApps uses the original non-scaled icon size
+        allAppsIconSizePx = Utilities.pxFromDp(inv.iconSize, dm);
+
+        // Determine sizes.
+        widthPx = width;
+        heightPx = height;
+        if (isLandscape) {
+            availableWidthPx = maxSize.x;
+            availableHeightPx = minSize.y;
+        } else {
+            availableWidthPx = minSize.x;
+            availableHeightPx = maxSize.y;
         }
-        // Hotseat
-        hotseatIconSize = invDistWeightedInterpolate(minWidth, minHeight, points);
-
-        // If the partner customization apk contains any grid overrides, apply them
-        applyPartnerDeviceProfileOverrides(context, dm);
 
         // Calculate the remaining vars
-        updateFromConfiguration(context, res, wPx, hPx, awPx, ahPx);
-        updateAvailableDimensions(context);
+        updateAvailableDimensions(dm, res);
         computeAllAppsButtonSize(context);
     }
 
     /**
-     * Apply any Partner customization grid overrides.
-     *
-     * Currently we support: all apps row / column count.
-     */
-    private void applyPartnerDeviceProfileOverrides(Context ctx, DisplayMetrics dm) {
-        Partner p = Partner.get(ctx.getPackageManager());
-        if (p != null) {
-            DeviceProfile partnerDp = p.getDeviceProfileOverride(dm);
-            if (partnerDp != null) {
-                if (partnerDp.numRows > 0 && partnerDp.numColumns > 0) {
-                    numRows = numFolderRows = partnerDp.numRows;
-                    numColumns = numFolderColumns = partnerDp.numColumns;
-                }
-                if (partnerDp.allAppsShortEdgeCount > 0 && partnerDp.allAppsLongEdgeCount > 0) {
-                    allAppsShortEdgeCount = partnerDp.allAppsShortEdgeCount;
-                    allAppsLongEdgeCount = partnerDp.allAppsLongEdgeCount;
-                }
-                if (partnerDp.iconSize > 0) {
-                    iconSize = partnerDp.iconSize;
-                    // AllApps uses the original non-scaled icon size
-                    allAppsIconSizePx = DynamicGrid.pxFromDp(iconSize, dm);
-                }
-            }
-        }
-    }
-
-    /**
      * Determine the exact visual footprint of the all apps button, taking into account scaling
      * and internal padding of the drawable.
      */
     private void computeAllAppsButtonSize(Context context) {
         Resources res = context.getResources();
         float padding = res.getInteger(R.integer.config_allAppsButtonPaddingPercent) / 100f;
-        LauncherAppState app = LauncherAppState.getInstance();
         allAppsButtonVisualSize = (int) (hotseatIconSizePx * (1 - padding));
     }
 
-    void addCallback(DeviceProfileCallbacks cb) {
-        mCallbacks.add(cb);
-        cb.onAvailableSizeChanged(this);
-    }
-    void removeCallback(DeviceProfileCallbacks cb) {
-        mCallbacks.remove(cb);
-    }
-
-    private int getDeviceOrientation(Context context) {
-        WindowManager windowManager =  (WindowManager)
-                context.getSystemService(Context.WINDOW_SERVICE);
-        Resources resources = context.getResources();
-        DisplayMetrics dm = resources.getDisplayMetrics();
-        Configuration config = resources.getConfiguration();
-        int rotation = windowManager.getDefaultDisplay().getRotation();
-
-        boolean isLandscape = (config.orientation == Configuration.ORIENTATION_LANDSCAPE) &&
-                (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180);
-        boolean isRotatedPortrait = (config.orientation == Configuration.ORIENTATION_PORTRAIT) &&
-                (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
-        if (isLandscape || isRotatedPortrait) {
-            return CellLayout.LANDSCAPE;
-        } else {
-            return CellLayout.PORTRAIT;
-        }
-    }
-
-    private void updateAvailableDimensions(Context context) {
-        WindowManager windowManager =  (WindowManager)
-                context.getSystemService(Context.WINDOW_SERVICE);
-        Display display = windowManager.getDefaultDisplay();
-        Resources resources = context.getResources();
-        DisplayMetrics dm = resources.getDisplayMetrics();
-        Configuration config = resources.getConfiguration();
-
-        // There are three possible configurations that the dynamic grid accounts for, portrait,
-        // landscape with the nav bar at the bottom, and landscape with the nav bar at the side.
-        // To prevent waiting for fitSystemWindows(), we make the observation that in landscape,
-        // the height is the smallest height (either with the nav bar at the bottom or to the
-        // side) and otherwise, the height is simply the largest possible height for a portrait
-        // device.
-        Point size = new Point();
-        Point smallestSize = new Point();
-        Point largestSize = new Point();
-        display.getSize(size);
-        display.getCurrentSizeRange(smallestSize, largestSize);
-        availableWidthPx = size.x;
-        if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
-            availableHeightPx = smallestSize.y;
-        } else {
-            availableHeightPx = largestSize.y;
-        }
-
+    private void updateAvailableDimensions(DisplayMetrics dm, Resources res) {
         // Check to see if the icons fit in the new available height.  If not, then we need to
         // shrink the icon size.
         float scale = 1f;
         int drawablePadding = iconDrawablePaddingOriginalPx;
-        updateIconSize(1f, drawablePadding, resources, dm);
-        float usedHeight = (cellHeightPx * numRows);
+        updateIconSize(1f, drawablePadding, res, dm);
+        float usedHeight = (cellHeightPx * inv.numRows);
 
-        Rect workspacePadding = getWorkspacePadding();
+        // We only care about the top and bottom workspace padding, which is not affected by RTL.
+        Rect workspacePadding = getWorkspacePadding(false /* isLayoutRtl */);
         int maxHeight = (availableHeightPx - workspacePadding.top - workspacePadding.bottom);
         if (usedHeight > maxHeight) {
             scale = maxHeight / usedHeight;
             drawablePadding = 0;
         }
-        updateIconSize(scale, drawablePadding, resources, dm);
-
-        // Make the callbacks
-        for (DeviceProfileCallbacks cb : mCallbacks) {
-            cb.onAvailableSizeChanged(this);
-        }
+        updateIconSize(scale, drawablePadding, res, dm);
     }
 
     private void updateIconSize(float scale, int drawablePadding, Resources res,
                                 DisplayMetrics dm) {
-        iconSizePx = (int) (DynamicGrid.pxFromDp(iconSize, dm) * scale);
-        iconTextSizePx = (int) (DynamicGrid.pxFromSp(iconTextSize, dm) * scale);
+        iconSizePx = (int) (Utilities.pxFromDp(inv.iconSize, dm) * scale);
+        iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, dm) * scale);
         iconDrawablePaddingPx = drawablePadding;
-        hotseatIconSizePx = (int) (DynamicGrid.pxFromDp(hotseatIconSize, dm) * scale);
+        hotseatIconSizePx = (int) (Utilities.pxFromDp(inv.hotseatIconSize, dm) * scale);
 
         // Search Bar
         searchBarSpaceWidthPx = Math.min(widthPx,
@@ -410,135 +224,30 @@
         folderBackgroundOffset = -edgeMarginPx;
         folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset;
 
-        // All Apps
-        allAppsCellWidthPx = allAppsIconSizePx;
-        allAppsCellHeightPx = allAppsIconSizePx + drawablePadding + allAppsIconTextSizePx;
-        int maxLongEdgeCellCount =
-                res.getInteger(R.integer.config_dynamic_grid_max_long_edge_cell_count);
-        int maxShortEdgeCellCount =
-                res.getInteger(R.integer.config_dynamic_grid_max_short_edge_cell_count);
-        int minEdgeCellCount =
-                res.getInteger(R.integer.config_dynamic_grid_min_edge_cell_count);
-        int maxRows = (isLandscape ? maxShortEdgeCellCount : maxLongEdgeCellCount);
-        int maxCols = (isLandscape ? maxLongEdgeCellCount : maxShortEdgeCellCount);
-
-        if (allAppsShortEdgeCount > 0 && allAppsLongEdgeCount > 0) {
-            allAppsNumRows = isLandscape ? allAppsShortEdgeCount : allAppsLongEdgeCount;
-            allAppsNumCols = isLandscape ? allAppsLongEdgeCount : allAppsShortEdgeCount;
-        } else {
-            allAppsNumRows = (availableHeightPx - pageIndicatorHeightPx) /
-                    (allAppsCellHeightPx + allAppsCellPaddingPx);
-            allAppsNumRows = Math.max(minEdgeCellCount, Math.min(maxRows, allAppsNumRows));
-            allAppsNumCols = (availableWidthPx) / (allAppsCellWidthPx + allAppsCellPaddingPx);
-            allAppsNumCols = Math.max(minEdgeCellCount, Math.min(maxCols, allAppsNumCols));
-        }
-
-        int appsContainerViewWidthPx = res.getDimensionPixelSize(R.dimen.apps_container_width);
-        updateAppsViewNumCols(res, appsContainerViewWidthPx);
+        updateAppsViewNumCols(res, 0);
     }
 
     public boolean updateAppsViewNumCols(Resources res, int containerWidth) {
         int appsViewLeftMarginPx =
-                res.getDimensionPixelSize(R.dimen.apps_grid_view_start_margin);
+                res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin);
+        int allAppsCellPaddingPx =
+                res.getDimensionPixelSize(R.dimen.all_apps_icon_left_right_padding);
         int availableAppsWidthPx = (containerWidth > 0) ? containerWidth : availableWidthPx;
         int numAppsCols = (availableAppsWidthPx - appsViewLeftMarginPx) /
-                (allAppsCellWidthPx + 2 * allAppsCellPaddingPx);
-        int numPredictiveAppCols = isPhone() ? 4 : numAppsCols;
-        if ((numAppsCols != appsViewNumCols) ||
-                (numPredictiveAppCols != appsViewNumPredictiveCols)) {
-            appsViewNumCols = numAppsCols;
-            appsViewNumPredictiveCols = numPredictiveAppCols;
+                (allAppsIconSizePx + 2 * allAppsCellPaddingPx);
+        int numPredictiveAppCols = isPhone ? 4 : numAppsCols;
+        if ((numAppsCols != allAppsNumCols) ||
+                (numPredictiveAppCols != allAppsNumPredictiveCols)) {
+            allAppsNumCols = numAppsCols;
+            allAppsNumPredictiveCols = numPredictiveAppCols;
             return true;
         }
         return false;
     }
 
-    void updateFromConfiguration(Context context, Resources resources, int wPx, int hPx,
-                                 int awPx, int ahPx) {
-        Configuration configuration = resources.getConfiguration();
-        isLandscape = (configuration.orientation == Configuration.ORIENTATION_LANDSCAPE);
-        isTablet = resources.getBoolean(R.bool.is_tablet);
-        isLargeTablet = resources.getBoolean(R.bool.is_large_tablet);
-        isLayoutRtl = Utilities.isRtl(resources);
-        widthPx = wPx;
-        heightPx = hPx;
-        availableWidthPx = awPx;
-        availableHeightPx = ahPx;
-
-        updateAvailableDimensions(context);
-    }
-
-    @Thunk float dist(PointF p0, PointF p1) {
-        return (float) Math.hypot(p1.x - p0.x, p1.y - p0.y);
-    }
-
-    private float weight(PointF a, PointF b,
-                        float pow) {
-        float d = dist(a, b);
-        if (d == 0f) {
-            return Float.POSITIVE_INFINITY;
-        }
-        return (float) (1f / Math.pow(d, pow));
-    }
-
-    /** Returns the closest device profile given the width and height and a list of profiles */
-    private DeviceProfile findClosestDeviceProfile(float width, float height,
-                                                   ArrayList<DeviceProfileQuery> points) {
-        return findClosestDeviceProfiles(width, height, points).get(0).profile;
-    }
-
-    /** Returns the closest device profiles ordered by closeness to the specified width and height */
-    private ArrayList<DeviceProfileQuery> findClosestDeviceProfiles(float width, float height,
-                                                   ArrayList<DeviceProfileQuery> points) {
-        final PointF xy = new PointF(width, height);
-
-        // Sort the profiles by their closeness to the dimensions
-        ArrayList<DeviceProfileQuery> pointsByNearness = points;
-        Collections.sort(pointsByNearness, new Comparator<DeviceProfileQuery>() {
-            public int compare(DeviceProfileQuery a, DeviceProfileQuery b) {
-                return (int) (dist(xy, a.dimens) - dist(xy, b.dimens));
-            }
-        });
-
-        return pointsByNearness;
-    }
-
-    private float invDistWeightedInterpolate(float width, float height,
-                ArrayList<DeviceProfileQuery> points) {
-        float sum = 0;
-        float weights = 0;
-        float pow = 5;
-        float kNearestNeighbors = 3;
-        final PointF xy = new PointF(width, height);
-
-        ArrayList<DeviceProfileQuery> pointsByNearness = findClosestDeviceProfiles(width, height,
-                points);
-
-        for (int i = 0; i < pointsByNearness.size(); ++i) {
-            DeviceProfileQuery p = pointsByNearness.get(i);
-            if (i < kNearestNeighbors) {
-                float w = weight(xy, p.dimens, pow);
-                if (w == Float.POSITIVE_INFINITY) {
-                    return p.value;
-                }
-                weights += w;
-            }
-        }
-
-        for (int i = 0; i < pointsByNearness.size(); ++i) {
-            DeviceProfileQuery p = pointsByNearness.get(i);
-            if (i < kNearestNeighbors) {
-                float w = weight(xy, p.dimens, pow);
-                sum += w * p.value / weights;
-            }
-        }
-
-        return sum;
-    }
-
     /** Returns the search bar top offset */
-    int getSearchBarTopOffset() {
-        if (isTablet() && !isVerticalBarLayout()) {
+    private int getSearchBarTopOffset() {
+        if (isTablet && !isVerticalBarLayout()) {
             return 4 * edgeMarginPx;
         } else {
             return 2 * edgeMarginPx;
@@ -546,14 +255,9 @@
     }
 
     /** Returns the search bar bounds in the current orientation */
-    Rect getSearchBarBounds() {
-        return getSearchBarBounds(isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
-    }
-    /** Returns the search bar bounds in the specified orientation */
-    Rect getSearchBarBounds(int orientation) {
+    public Rect getSearchBarBounds(boolean isLayoutRtl) {
         Rect bounds = new Rect();
-        if (orientation == CellLayout.LANDSCAPE &&
-                transposeLayoutWithOrientation) {
+        if (isLandscape && transposeLayoutWithOrientation) {
             if (isLayoutRtl) {
                 bounds.set(availableWidthPx - searchBarSpaceHeightPx, edgeMarginPx,
                         availableWidthPx, availableHeightPx - edgeMarginPx);
@@ -562,16 +266,14 @@
                         availableHeightPx - edgeMarginPx);
             }
         } else {
-            if (isTablet()) {
+            if (isTablet) {
                 // Pad the left and right of the workspace to ensure consistent spacing
                 // between all icons
-                int width = (orientation == CellLayout.LANDSCAPE)
-                        ? Math.max(widthPx, heightPx)
-                        : Math.min(widthPx, heightPx);
+                int width = isLandscape ? Math.max(widthPx, heightPx) : Math.min(widthPx, heightPx);
                 // XXX: If the icon size changes across orientations, we will have to take
                 //      that into account here too.
                 int gap = (int) ((width - 2 * edgeMarginPx -
-                        (numColumns * cellWidthPx)) / (2 * (numColumns + 1)));
+                        (inv.numColumns * cellWidthPx)) / (2 * (inv.numColumns + 1)));
                 bounds.set(edgeMarginPx + gap, getSearchBarTopOffset(),
                         availableWidthPx - (edgeMarginPx + gap),
                         searchBarSpaceHeightPx);
@@ -585,46 +287,11 @@
         return bounds;
     }
 
-    /** Returns the bounds of the workspace page indicators. */
-    Rect getWorkspacePageIndicatorBounds(Rect insets) {
-        Rect workspacePadding = getWorkspacePadding();
-        if (isLandscape && transposeLayoutWithOrientation) {
-            if (isLayoutRtl) {
-                return new Rect(workspacePadding.left, workspacePadding.top,
-                        workspacePadding.left + pageIndicatorHeightPx,
-                        heightPx - workspacePadding.bottom - insets.bottom);
-            } else {
-                int pageIndicatorLeft = widthPx - workspacePadding.right;
-                return new Rect(pageIndicatorLeft, workspacePadding.top,
-                        pageIndicatorLeft + pageIndicatorHeightPx,
-                        heightPx - workspacePadding.bottom - insets.bottom);
-            }
-        } else {
-            int pageIndicatorTop = heightPx - insets.bottom - workspacePadding.bottom;
-            return new Rect(workspacePadding.left, pageIndicatorTop,
-                    widthPx - workspacePadding.right, pageIndicatorTop + pageIndicatorHeightPx);
-        }
-    }
-
-    public int getWorkspaceGridHeight() {
-        Rect p = getWorkspacePadding();
-        return availableHeightPx - p.top - p.bottom;
-    }
-
-    public int getWorkspaceGridWidth() {
-        Rect p = getWorkspacePadding();
-        return availableWidthPx - p.left - p.right;
-    }
-
     /** Returns the workspace padding in the specified orientation */
-    Rect getWorkspacePadding() {
-        return getWorkspacePadding(isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
-    }
-    Rect getWorkspacePadding(int orientation) {
-        Rect searchBarBounds = getSearchBarBounds(orientation);
+    Rect getWorkspacePadding(boolean isLayoutRtl) {
+        Rect searchBarBounds = getSearchBarBounds(isLayoutRtl);
         Rect padding = new Rect();
-        if (orientation == CellLayout.LANDSCAPE &&
-                transposeLayoutWithOrientation) {
+        if (isLandscape && transposeLayoutWithOrientation) {
             // Pad the left and right of the workspace with search/hotseat bar sizes
             if (isLayoutRtl) {
                 padding.set(hotseatBarHeightPx, edgeMarginPx,
@@ -634,22 +301,22 @@
                         hotseatBarHeightPx, edgeMarginPx);
             }
         } else {
-            if (isTablet()) {
+            if (isTablet) {
                 // Pad the left and right of the workspace to ensure consistent spacing
                 // between all icons
                 float gapScale = 1f + (dragViewScale - 1f) / 2f;
-                int width = (orientation == CellLayout.LANDSCAPE)
+                int width = isLandscape
                         ? Math.max(widthPx, heightPx)
                         : Math.min(widthPx, heightPx);
-                int height = (orientation != CellLayout.LANDSCAPE)
+                int height = isLandscape
                         ? Math.max(widthPx, heightPx)
                         : Math.min(widthPx, heightPx);
                 int paddingTop = searchBarBounds.bottom;
                 int paddingBottom = hotseatBarHeightPx + pageIndicatorHeightPx;
-                int availableWidth = Math.max(0, width - (int) ((numColumns * cellWidthPx) +
-                        (numColumns * gapScale * cellWidthPx)));
+                int availableWidth = Math.max(0, width - (int) ((inv.numColumns * cellWidthPx) +
+                        (inv.numColumns * gapScale * cellWidthPx)));
                 int availableHeight = Math.max(0, height - paddingTop - paddingBottom
-                        - (int) (2 * numRows * cellHeightPx));
+                        - (int) (2 * inv.numRows * cellHeightPx));
                 padding.set(availableWidth / 2, paddingTop + availableHeight / 2,
                         availableWidth / 2, paddingBottom + availableHeight / 2);
             } else {
@@ -663,16 +330,15 @@
         return padding;
     }
 
-    int getWorkspacePageSpacing(int orientation) {
-        if ((orientation == CellLayout.LANDSCAPE &&
-                transposeLayoutWithOrientation) || isLargeTablet()) {
+    private int getWorkspacePageSpacing(boolean isLayoutRtl) {
+        if ((isLandscape && transposeLayoutWithOrientation) || isLargeTablet) {
             // In landscape mode the page spacing is set to the default.
             return defaultPageSpacingPx;
         } else {
             // In portrait, we want the pages spaced such that there is no
             // overhang of the previous / next page into the current page viewport.
             // We assume symmetrical padding in portrait mode.
-            return Math.max(defaultPageSpacingPx, 2 * getWorkspacePadding().left);
+            return Math.max(defaultPageSpacingPx, 2 * getWorkspacePadding(isLayoutRtl).left);
         }
     }
 
@@ -683,8 +349,8 @@
         return new Rect(0, availableHeightPx - zoneHeight, 0, availableHeightPx);
     }
 
-    float getOverviewModeScale() {
-        Rect workspacePadding = getWorkspacePadding();
+    public float getOverviewModeScale(boolean isLayoutRtl) {
+        Rect workspacePadding = getWorkspacePadding(isLayoutRtl);
         Rect overviewBar = getOverviewModeButtonBarRect();
         int pageSpace = availableHeightPx - workspacePadding.top - workspacePadding.bottom;
         return (overviewModeScaleFactor * (pageSpace - overviewBar.height())) / pageSpace;
@@ -701,23 +367,13 @@
         }
     }
 
-    int calculateCellWidth(int width, int countX) {
+    public static int calculateCellWidth(int width, int countX) {
         return width / countX;
     }
-    int calculateCellHeight(int height, int countY) {
+    public static int calculateCellHeight(int height, int countY) {
         return height / countY;
     }
 
-    boolean isPhone() {
-        return !isTablet && !isLargeTablet;
-    }
-    boolean isTablet() {
-        return isTablet;
-    }
-    boolean isLargeTablet() {
-        return isLargeTablet;
-    }
-
     /**
      * When {@code true}, hotseat is on the bottom row when in landscape mode.
      * If {@code false}, hotseat is on the right column when in landscape mode.
@@ -727,10 +383,10 @@
     }
 
     boolean shouldFadeAdjacentWorkspaceScreens() {
-        return isVerticalBarLayout() || isLargeTablet();
+        return isVerticalBarLayout() || isLargeTablet;
     }
 
-    int getVisibleChildCount(ViewGroup parent) {
+    private int getVisibleChildCount(ViewGroup parent) {
         int visibleChildren = 0;
         for (int i = 0; i < parent.getChildCount(); i++) {
             if (parent.getChildAt(i).getVisibility() != View.GONE) {
@@ -743,6 +399,7 @@
     public void layout(Launcher launcher) {
         FrameLayout.LayoutParams lp;
         boolean hasVerticalBarLayout = isVerticalBarLayout();
+        final boolean isLayoutRtl = Utilities.isRtl(launcher.getResources());
 
         // Layout the search bar space
         View searchBar = launcher.getSearchBar();
@@ -772,11 +429,10 @@
         PagedView workspace = (PagedView) launcher.findViewById(R.id.workspace);
         lp = (FrameLayout.LayoutParams) workspace.getLayoutParams();
         lp.gravity = Gravity.CENTER;
-        int orientation = isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT;
-        Rect padding = getWorkspacePadding(orientation);
+        Rect padding = getWorkspacePadding(isLayoutRtl);
         workspace.setLayoutParams(lp);
         workspace.setPadding(padding.left, padding.top, padding.right, padding.bottom);
-        workspace.setPageSpacing(getWorkspacePageSpacing(orientation));
+        workspace.setPageSpacing(getWorkspacePageSpacing(isLayoutRtl));
 
         // Layout the hotseat
         View hotseat = launcher.findViewById(R.id.hotseat);
@@ -787,7 +443,7 @@
             lp.width = hotseatBarHeightPx;
             lp.height = LayoutParams.MATCH_PARENT;
             hotseat.findViewById(R.id.layout).setPadding(0, 2 * edgeMarginPx, 0, 2 * edgeMarginPx);
-        } else if (isTablet()) {
+        } else if (isTablet) {
             // Pad the hotseat with the workspace padding calculated above
             lp.gravity = Gravity.BOTTOM;
             lp.width = LayoutParams.MATCH_PARENT;
diff --git a/src/com/android/launcher3/DragView.java b/src/com/android/launcher3/DragView.java
index 120299e..b332338 100644
--- a/src/com/android/launcher3/DragView.java
+++ b/src/com/android/launcher3/DragView.java
@@ -38,7 +38,7 @@
 import java.util.Arrays;
 
 public class DragView extends View {
-    public static int COLOR_CHANGE_DURATION = 200;
+    public static int COLOR_CHANGE_DURATION = 120;
 
     @Thunk static float sDragAlpha = 1f;
 
@@ -249,8 +249,7 @@
             m1.setSaturation(0);
 
             ColorMatrix m2 = new ColorMatrix();
-            m2.setScale(Color.red(color) / 255f, Color.green(color) / 255f,
-                    Color.blue(color) / 255f, Color.alpha(color) / 255f);
+            setColorScale(color, m2);
             m1.postConcat(m2);
 
             if (Utilities.isLmpOrAbove()) {
@@ -355,4 +354,9 @@
             mDragLayer.removeView(DragView.this);
         }
     }
+
+    public static void setColorScale(int color, ColorMatrix target) {
+        target.setScale(Color.red(color) / 255f, Color.green(color) / 255f,
+                Color.blue(color) / 255f, Color.alpha(color) / 255f);
+    }
 }
diff --git a/src/com/android/launcher3/DynamicGrid.java b/src/com/android/launcher3/DynamicGrid.java
deleted file mode 100644
index d22427f..0000000
--- a/src/com/android/launcher3/DynamicGrid.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
-
-import java.util.ArrayList;
-
-
-public class DynamicGrid {
-    @SuppressWarnings("unused")
-    private static final String TAG = "DynamicGrid";
-
-    private DeviceProfile mProfile;
-    private float mMinWidth;
-    private float mMinHeight;
-
-    // This is a static that we use for the default icon size on a 4/5-inch phone
-    static float DEFAULT_ICON_SIZE_DP = 60;
-    static float DEFAULT_ICON_SIZE_PX = 0;
-
-    public static float dpiFromPx(int size, DisplayMetrics metrics){
-        float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
-        return (size / densityRatio);
-    }
-    public static int pxFromDp(float size, DisplayMetrics metrics) {
-        return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                size, metrics));
-    }
-    public static int pxFromSp(float size, DisplayMetrics metrics) {
-        return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
-                size, metrics));
-    }
-
-    public DynamicGrid(Context context, Resources resources,
-                       int minWidthPx, int minHeightPx,
-                       int widthPx, int heightPx,
-                       int awPx, int ahPx) {
-        DisplayMetrics dm = resources.getDisplayMetrics();
-        ArrayList<DeviceProfile> deviceProfiles =
-                new ArrayList<DeviceProfile>();
-        DEFAULT_ICON_SIZE_PX = pxFromDp(DEFAULT_ICON_SIZE_DP, dm);
-        // Our phone profiles include the bar sizes in each orientation
-        deviceProfiles.add(new DeviceProfile("Super Short Stubby",
-                255, 300,  2, 3, 2, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4));
-        deviceProfiles.add(new DeviceProfile("Shorter Stubby",
-                255, 400,  3, 3, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4));
-        deviceProfiles.add(new DeviceProfile("Short Stubby",
-                275, 420,  3, 4, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
-        deviceProfiles.add(new DeviceProfile("Stubby",
-                255, 450,  3, 4, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
-        deviceProfiles.add(new DeviceProfile("Nexus S",
-                296, 491.33f,  4, 4, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
-        deviceProfiles.add(new DeviceProfile("Nexus 4",
-                335, 567,  4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
-        deviceProfiles.add(new DeviceProfile("Nexus 5",
-                359, 567,  4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
-        deviceProfiles.add(new DeviceProfile("Large Phone",
-                406, 694,  5, 5, 4, 4,  64, 14.4f,  5, 56, R.xml.default_workspace_5x5));
-        // The tablet profile is odd in that the landscape orientation
-        // also includes the nav bar on the side
-        deviceProfiles.add(new DeviceProfile("Nexus 7",
-                575, 904,  5, 6, 4, 5, 72, 14.4f,  7, 60, R.xml.default_workspace_5x6));
-        // Larger tablet profiles always have system bars on the top & bottom
-        deviceProfiles.add(new DeviceProfile("Nexus 10",
-                727, 1207,  5, 6, 4, 5, 76, 14.4f,  7, 64, R.xml.default_workspace_5x6));
-        deviceProfiles.add(new DeviceProfile("20-inch Tablet",
-                1527, 2527,  7, 7, 6, 6, 100, 20,  7, 72, R.xml.default_workspace_4x4));
-        mMinWidth = dpiFromPx(minWidthPx, dm);
-        mMinHeight = dpiFromPx(minHeightPx, dm);
-        mProfile = new DeviceProfile(context, deviceProfiles,
-                mMinWidth, mMinHeight,
-                widthPx, heightPx,
-                awPx, ahPx,
-                resources);
-    }
-
-    public DeviceProfile getDeviceProfile() {
-        return mProfile;
-    }
-
-    public String toString() {
-        return "-------- DYNAMIC GRID ------- \n" +
-                "Wd: " + mProfile.minWidthDps + ", Hd: " + mProfile.minHeightDps +
-                ", W: " + mProfile.widthPx + ", H: " + mProfile.heightPx +
-                " [r: " + mProfile.numRows + ", c: " + mProfile.numColumns +
-                ", is: " + mProfile.iconSizePx + ", its: " + mProfile.iconTextSizePx +
-                ", cw: " + mProfile.cellWidthPx + ", ch: " + mProfile.cellHeightPx +
-                ", hc: " + mProfile.numHotseatIcons + ", his: " + mProfile.hotseatIconSizePx + "]";
-    }
-}
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index c77d416..46e4902 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -92,11 +92,12 @@
 
             final int pageIndex = pagedView.indexOfChild(cellLayout);
             final int pageCount = pagedView.getPageCount();
+            final boolean isLayoutRtl = Utilities.isRtl(v.getResources());
 
             int[][] matrix = FocusLogic.createSparseMatrix(cellLayout);
             // Process focus.
-            int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix,
-                    iconIndex, pageIndex, pageCount);
+            int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX,
+                    countY, matrix, iconIndex, pageIndex, pageCount, isLayoutRtl);
             if (newIconIndex == FocusLogic.NOOP) {
                 handleNoopKey(keyCode, v);
                 return consume;
@@ -184,7 +185,8 @@
             return consume;
         }
 
-        DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
+        DeviceProfile profile = ((Launcher) v.getContext()).getDeviceProfile();
+
         if (DEBUG) {
             Log.v(TAG, String.format(
                     "Handle HOTSEAT BUTTONS keyevent=[%s] on hotseat buttons, isVertical=%s",
@@ -248,8 +250,8 @@
         }
 
         // Process the focus.
-        int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix,
-                iconIndex, pageIndex, pageCount);
+        int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX,
+                countY, matrix, iconIndex, pageIndex, pageCount, Utilities.isRtl(v.getResources()));
 
         View newIcon = null;
         if (newIconIndex == FocusLogic.NEXT_PAGE_FIRST_ITEM) {
@@ -283,8 +285,8 @@
             return consume;
         }
 
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile profile = app.getDynamicGrid().getDeviceProfile();
+        Launcher launcher = (Launcher) v.getContext();
+        DeviceProfile profile = launcher.getDeviceProfile();
 
         if (DEBUG) {
             Log.v(TAG, String.format("Handle WORKSPACE ICONS keyevent=[%s] isVerticalBar=%s",
@@ -295,9 +297,9 @@
         ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent();
         CellLayout iconLayout = (CellLayout) parent.getParent();
         final Workspace workspace = (Workspace) iconLayout.getParent();
-        final ViewGroup launcher = (ViewGroup) workspace.getParent();
-        final ViewGroup tabs = (ViewGroup) launcher.findViewById(R.id.search_drop_target_bar);
-        final Hotseat hotseat = (Hotseat) launcher.findViewById(R.id.hotseat);
+        final ViewGroup dragLayer = (ViewGroup) workspace.getParent();
+        final ViewGroup tabs = (ViewGroup) dragLayer.findViewById(R.id.search_drop_target_bar);
+        final Hotseat hotseat = (Hotseat) dragLayer.findViewById(R.id.hotseat);
 
         final int iconIndex = parent.indexOfChild(v);
         final int pageIndex = workspace.indexOfChild(iconLayout);
@@ -314,12 +316,14 @@
         // with the hotseat.
         if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN && !profile.isVerticalBarLayout()) {
             matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, true /* horizontal */,
-                    hotseat.getAllAppsButtonRank(), false /* all apps icon is ignored */);
+                    hotseat.getAllAppsButtonRank(),
+                    !hotseat.hasIcons() /* ignore all apps icon, unless there are no other icons */);
             countY = countY + 1;
         } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT &&
                 profile.isVerticalBarLayout()) {
             matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, false /* horizontal */,
-                    hotseat.getAllAppsButtonRank(), false /* all apps icon is ignored */);
+                    hotseat.getAllAppsButtonRank(),
+                    !hotseat.hasIcons() /* ignore all apps icon, unless there are no other icons */);
             countX = countX + 1;
         } else if (keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL) {
             workspace.removeWorkspaceItem(v);
@@ -329,8 +333,8 @@
         }
 
         // Process the focus.
-        int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix,
-                iconIndex, pageIndex, pageCount);
+        int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX,
+                countY, matrix, iconIndex, pageIndex, pageCount, Utilities.isRtl(v.getResources()));
         View newIcon = null;
         switch (newIconIndex) {
             case FocusLogic.NOOP:
@@ -352,8 +356,9 @@
                     iconLayout = (CellLayout) parent.getParent();
                     matrix = FocusLogic.createSparseMatrix(iconLayout,
                         iconLayout.getCountX(), row);
-                    newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY, matrix,
-                        FocusLogic.PIVOT, newPageIndex, pageCount);
+                    newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY,
+                            matrix, FocusLogic.PIVOT, newPageIndex, pageCount,
+                            Utilities.isRtl(v.getResources()));
                     newIcon = parent.getChildAt(newIconIndex);
                 }
                 break;
@@ -385,8 +390,9 @@
                     workspace.snapToPage(newPageIndex);
                     iconLayout = (CellLayout) parent.getParent();
                     matrix = FocusLogic.createSparseMatrix(iconLayout, -1, row);
-                    newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY, matrix,
-                        FocusLogic.PIVOT, newPageIndex, pageCount);
+                    newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY,
+                            matrix, FocusLogic.PIVOT, newPageIndex, pageCount,
+                            Utilities.isRtl(v.getResources()));
                     newIcon = parent.getChildAt(newIconIndex);
                 }
                 break;
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index d0a7ba3..ec4ea04 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -36,7 +36,6 @@
 import android.util.Log;
 import android.view.ActionMode;
 import android.view.KeyEvent;
-import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.MotionEvent;
@@ -945,8 +944,7 @@
 
         float scale = parent.getDescendantRectRelativeToSelf(mFolderIcon, sTempRect);
 
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
 
         int centerX = (int) (sTempRect.left + sTempRect.width() * scale / 2);
         int centerY = (int) (sTempRect.top + sTempRect.height() * scale / 2);
@@ -969,7 +967,7 @@
                 bounds.left + bounds.width() - width);
         int top = Math.min(Math.max(bounds.top, centeredTop),
                 bounds.top + bounds.height() - height);
-        if (grid.isPhone() && (grid.availableWidthPx - width) < grid.iconSizePx) {
+        if (grid.isPhone && (grid.availableWidthPx - width) < grid.iconSizePx) {
             // Center the folder if it is full (on phones only)
             left = (grid.availableWidthPx - width) / 2;
         } else if (width >= bounds.width()) {
@@ -1003,10 +1001,8 @@
     }
 
     private int getContentAreaHeight() {
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-        Rect workspacePadding = grid.getWorkspacePadding(grid.isLandscape ?
-                CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
+        DeviceProfile grid = mLauncher.getDeviceProfile();
+        Rect workspacePadding = grid.getWorkspacePadding(mContent.mIsRtl);
         int maxContentAreaHeight = grid.availableHeightPx -
                 workspacePadding.top - workspacePadding.bottom -
                 mFooterHeight;
diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java
index b161b1c..8652eef 100644
--- a/src/com/android/launcher3/FolderIcon.java
+++ b/src/com/android/launcher3/FolderIcon.java
@@ -147,8 +147,8 @@
                     "INITIAL_ITEM_ANIMATION_DURATION, as sequencing of adding first two items " +
                     "is dependent on this");
         }
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+
+        DeviceProfile grid = launcher.getDeviceProfile();
 
         FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false);
         icon.setClipToPadding(false);
@@ -217,8 +217,7 @@
                             + Thread.currentThread());
                 }
 
-                LauncherAppState app = LauncherAppState.getInstance();
-                DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+                DeviceProfile grid = launcher.getDeviceProfile();
                 sPreviewSize = grid.folderIconSizePx;
                 sPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding);
                 sSharedOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer_holo);
@@ -490,8 +489,7 @@
 
     private void computePreviewDrawingParams(int drawableSize, int totalSize) {
         if (mIntrinsicIconSize != drawableSize || mTotalWidth != totalSize) {
-            LauncherAppState app = LauncherAppState.getInstance();
-            DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+            DeviceProfile grid = mLauncher.getDeviceProfile();
 
             mIntrinsicIconSize = drawableSize;
             mTotalWidth = totalSize;
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index 930f911..aea21c9 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -92,7 +92,7 @@
     }
 
     public void setTitle(CharSequence title) {
-        this.title = Utilities.trim(title);
+        this.title = title;
         for (int i = 0; i < listeners.size(); i++) {
             listeners.get(i).onTitleChanged(title);
         }
diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java
index 06ed588..0bd6501 100644
--- a/src/com/android/launcher3/FolderPagedView.java
+++ b/src/com/android/launcher3/FolderPagedView.java
@@ -80,9 +80,9 @@
         super(context, attrs);
         LauncherAppState app = LauncherAppState.getInstance();
 
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-        mMaxCountX = (int) grid.numFolderColumns;
-        mMaxCountY = (int) grid.numFolderRows;
+        InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
+        mMaxCountX = (int) profile.numFolderColumns;
+        mMaxCountY = (int) profile.numFolderRows;
 
         mMaxItemsPerPage = mMaxCountX * mMaxCountY;
 
@@ -229,7 +229,7 @@
     }
 
     private CellLayout createAndAddNewPage() {
-        DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = ((Launcher) getContext()).getDeviceProfile();
         CellLayout page = new CellLayout(getContext());
         page.setCellDimensions(grid.folderCellWidthPx, grid.folderCellHeightPx);
         page.getShortcutsAndWidgets().setMotionEventSplittingEnabled(false);
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index b614bc6..ce33164 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -54,10 +54,7 @@
                 r.getBoolean(R.bool.hotseat_transpose_layout_with_orientation);
         mIsLandscape = context.getResources().getConfiguration().orientation ==
             Configuration.ORIENTATION_LANDSCAPE;
-    }
-
-    public void setup(Launcher launcher) {
-        mLauncher = launcher;
+        mLauncher = (Launcher) context;
     }
 
     CellLayout getLayout() {
@@ -65,6 +62,13 @@
     }
 
     /**
+     * Returns whether there are other icons than the all apps button in the hotseat.
+     */
+    public boolean hasIcons() {
+        return mContent.getShortcutsAndWidgets().getChildCount() > 1;
+    }
+
+    /**
      * Registers the specified listener on the cell layout of the hotseat.
      */
     @Override
@@ -98,37 +102,17 @@
         return rank == mAllAppsButtonRank;
     }
 
-    /** This returns the coordinates of an app in a given cell, relative to the DragLayer */
-    Rect getCellCoordinates(int cellX, int cellY) {
-        Rect coords = new Rect();
-        mContent.cellToRect(cellX, cellY, 1, 1, coords);
-        int[] hotseatInParent = new int[2];
-        Utilities.getDescendantCoordRelativeToParent(this, mLauncher.getDragLayer(),
-                hotseatInParent, false);
-        coords.offset(hotseatInParent[0], hotseatInParent[1]);
-
-        // Center the icon
-        int cWidth = mContent.getShortcutsAndWidgets().getCellContentWidth();
-        int cHeight = mContent.getShortcutsAndWidgets().getCellContentHeight();
-        int cellPaddingX = (int) Math.max(0, ((coords.width() - cWidth) / 2f));
-        int cellPaddingY = (int) Math.max(0, ((coords.height() - cHeight) / 2f));
-        coords.offset(cellPaddingX, cellPaddingY);
-
-        return coords;
-    }
-
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
 
-        mAllAppsButtonRank = grid.hotseatAllAppsRank;
+        mAllAppsButtonRank = grid.inv.hotseatAllAppsRank;
         mContent = (CellLayout) findViewById(R.id.layout);
-        if (grid.isLandscape && !grid.isLargeTablet()) {
-            mContent.setGridSize(1, (int) grid.numHotseatIcons);
+        if (grid.isLandscape && !grid.isLargeTablet) {
+            mContent.setGridSize(1, (int) grid.inv.numHotseatIcons);
         } else {
-            mContent.setGridSize((int) grid.numHotseatIcons, 1);
+            mContent.setGridSize((int) grid.inv.numHotseatIcons, 1);
         }
         mContent.setIsHotseat(true);
 
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index a387579..0c91a71 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -35,6 +35,7 @@
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
+import android.os.SystemClock;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -46,7 +47,6 @@
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.Thunk;
 
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -71,6 +71,8 @@
 
     private static final int LOW_RES_SCALE_FACTOR = 8;
 
+    private static final Object ICON_UPDATE_TOKEN = new Object();
+
     @Thunk static class CacheEntry {
         public Bitmap icon;
         public CharSequence title;
@@ -92,7 +94,7 @@
 
     private final Handler mWorkerHandler;
 
-    public IconCache(Context context) {
+    public IconCache(Context context, InvariantDeviceProfile inv) {
         ActivityManager activityManager =
                 (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
 
@@ -223,13 +225,32 @@
                 new String[] {packageName + "/%", Long.toString(userSerial)});
     }
 
+    public void updateDbIcons() {
+        // Remove all active icon update tasks.
+        mWorkerHandler.removeCallbacksAndMessages(ICON_UPDATE_TOKEN);
+
+        mIconDb.updateSystemStateString(mContext);
+        for (UserHandleCompat user : mUserManager.getUserProfiles()) {
+            // Query for the set of apps
+            final List<LauncherActivityInfoCompat> apps = mLauncherApps.getActivityList(null, user);
+            // Fail if we don't have any apps
+            // TODO: Fix this. Only fail for the current user.
+            if (apps == null || apps.isEmpty()) {
+                return;
+            }
+
+            // Update icon cache. This happens in segments and {@link #onPackageIconsUpdated}
+            // is called by the icon cache when the job is complete.
+            updateDBIcons(user, apps);
+        }
+    }
+
     /**
      * Updates the persistent DB, such that only entries corresponding to {@param apps} remain in
      * the DB and are updated.
      * @return The set of packages for which icons have updated.
      */
-    public HashSet<String> updateDBIcons(UserHandleCompat user, List<LauncherActivityInfoCompat> apps) {
-        mIconDb.updateSystemStateString(mContext);
+    private void updateDBIcons(UserHandleCompat user, List<LauncherActivityInfoCompat> apps) {
         long userSerial = mUserManager.getSerialNumberForUser(user);
         PackageManager pm = mContext.getPackageManager();
         HashMap<String, PackageInfo> pkgInfoMap = new HashMap<String, PackageInfo>();
@@ -257,7 +278,7 @@
         final int systemStateIndex = c.getColumnIndex(IconDB.COLUMN_SYSTEM_STATE);
 
         HashSet<Integer> itemsToRemove = new HashSet<Integer>();
-        HashSet<String> updatedPackages = new HashSet<String>();
+        Stack<LauncherActivityInfoCompat> appsToUpdate = new Stack<>();
 
         while (c.moveToNext()) {
             String cn = c.getString(indexComponent);
@@ -281,14 +302,9 @@
             }
             if (app == null) {
                 itemsToRemove.add(c.getInt(rowIndex));
-                continue;
+            } else {
+                appsToUpdate.add(app);
             }
-            ContentValues values = updateCacheAndGetContentValues(app, true);
-            mIconDb.getWritableDatabase().update(IconDB.TABLE_NAME, values,
-                    IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?",
-                    new String[] {cn, Long.toString(userSerial)});
-
-            updatedPackages.add(component.getPackageName());
         }
         c.close();
         if (!itemsToRemove.isEmpty()) {
@@ -298,11 +314,12 @@
         }
 
         // Insert remaining apps.
-        if (!componentMap.isEmpty()) {
-            mWorkerHandler.post(new SerializedIconAdditionTask(userSerial, pkgInfoMap,
-                    componentMap.values()));
+        if (!componentMap.isEmpty() || !appsToUpdate.isEmpty()) {
+            Stack<LauncherActivityInfoCompat> appsToAdd = new Stack<>();
+            appsToAdd.addAll(componentMap.values());
+            new SerializedIconUpdateTask(userSerial, pkgInfoMap,
+                    appsToAdd, appsToUpdate).scheduleNext();
         }
-        return updatedPackages;
     }
 
     private void addIconToDBAndMemCache(LauncherActivityInfoCompat app, PackageInfo info,
@@ -357,20 +374,6 @@
     }
 
     /**
-     * Empty out the cache that aren't of the correct grid size
-     */
-    public synchronized void flushInvalidIcons(DeviceProfile grid) {
-        Iterator<Entry<ComponentKey, CacheEntry>> it = mCache.entrySet().iterator();
-        while (it.hasNext()) {
-            final CacheEntry e = it.next().getValue();
-            if ((e.icon != null) && (e.icon.getWidth() < grid.iconSizePx
-                    || e.icon.getHeight() < grid.iconSizePx)) {
-                it.remove();
-            }
-        }
-    }
-
-    /**
      * Fetches high-res icon for the provided ItemInfo and updates the caller when done.
      * @return a request ID that can be used to cancel the request.
      */
@@ -684,25 +687,46 @@
     }
 
     /**
-     * A runnable that adds icons in the DB for the provided LauncherActivityInfoCompat list.
-     * Items are added one at a time, to that the worker thread does not get blocked.
+     * A runnable that updates invalid icons and adds missing icons in the DB for the provided
+     * LauncherActivityInfoCompat list. Items are updated/added one at a time, so that the
+     * worker thread doesn't get blocked.
      */
-    private class SerializedIconAdditionTask implements Runnable {
+    private class SerializedIconUpdateTask implements Runnable {
         private final long mUserSerial;
         private final HashMap<String, PackageInfo> mPkgInfoMap;
         private final Stack<LauncherActivityInfoCompat> mAppsToAdd;
+        private final Stack<LauncherActivityInfoCompat> mAppsToUpdate;
+        private final HashSet<String> mUpdatedPackages = new HashSet<String>();
 
-        private SerializedIconAdditionTask(long userSerial, HashMap<String, PackageInfo> pkgInfoMap,
-                Collection<LauncherActivityInfoCompat> appsToAdd) {
+        private SerializedIconUpdateTask(long userSerial, HashMap<String, PackageInfo> pkgInfoMap,
+                Stack<LauncherActivityInfoCompat> appsToAdd,
+                Stack<LauncherActivityInfoCompat> appsToUpdate) {
             mUserSerial = userSerial;
             mPkgInfoMap = pkgInfoMap;
-            mAppsToAdd = new Stack<LauncherActivityInfoCompat>();
-            mAppsToAdd.addAll(appsToAdd);
+            mAppsToAdd = appsToAdd;
+            mAppsToUpdate = appsToUpdate;
         }
 
         @Override
         public void run() {
-            if (!mAppsToAdd.isEmpty()) {
+            if (!mAppsToUpdate.isEmpty()) {
+                LauncherActivityInfoCompat app = mAppsToUpdate.pop();
+                String cn = app.getComponentName().flattenToString();
+                ContentValues values = updateCacheAndGetContentValues(app, true);
+                mIconDb.getWritableDatabase().update(IconDB.TABLE_NAME, values,
+                        IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?",
+                        new String[] {cn, Long.toString(mUserSerial)});
+                mUpdatedPackages.add(app.getComponentName().getPackageName());
+
+                if (mAppsToUpdate.isEmpty() && !mUpdatedPackages.isEmpty()) {
+                    // No more app to update. Notify model.
+                    LauncherAppState.getInstance().getModel().onPackageIconsUpdated(
+                            mUpdatedPackages, mUserManager.getUserForSerialNumber(mUserSerial));
+                }
+
+                // Let it run one more time.
+                scheduleNext();
+            } else if (!mAppsToAdd.isEmpty()) {
                 LauncherActivityInfoCompat app = mAppsToAdd.pop();
                 PackageInfo info = mPkgInfoMap.get(app.getComponentName().getPackageName());
                 if (info != null) {
@@ -710,10 +734,15 @@
                         addIconToDBAndMemCache(app, info, mUserSerial);
                     }
                 }
+
+                if (!mAppsToAdd.isEmpty()) {
+                    scheduleNext();
+                }
             }
-            if (!mAppsToAdd.isEmpty()) {
-                mWorkerHandler.post(this);
-            }
+        }
+
+        public void scheduleNext() {
+            mWorkerHandler.postAtTime(this, ICON_UPDATE_TOKEN, SystemClock.uptimeMillis() + 1);
         }
     }
 
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index f3383cc..0f139fa 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -41,7 +41,7 @@
         // Get the hover color
         mHoverColor = getResources().getColor(R.color.info_target_hover_tint);
 
-        setDrawable(R.drawable.info_target_selector);
+        setDrawable(R.drawable.ic_launcher_info_normal);
     }
 
     public static void startDetailsActivityForInfo(Object info, Launcher launcher) {
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 115598f..448cc1d 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -156,6 +156,16 @@
         queuePendingShortcutInfo(info, context);
     }
 
+    public static ShortcutInfo fromShortcutIntent(Context context, Intent data) {
+        PendingInstallShortcutInfo info = new PendingInstallShortcutInfo(data, context);
+        if (info.launchIntent == null || info.label == null) {
+            if (DBG) Log.e(TAG, "Invalid install shortcut intent");
+            return null;
+        }
+        info = convertToLauncherActivityIfPossible(info);
+        return info.getShortcutInfo();
+    }
+
     static void queueInstallShortcut(LauncherActivityInfoCompat info, Context context) {
         queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, context), context);
     }
@@ -215,30 +225,6 @@
     }
 
     /**
-     * Returns true if the intent is a valid launch intent for a shortcut.
-     * This is used to identify shortcuts which are different from the ones exposed by the
-     * applications' manifest file.
-     *
-     * When DISABLE_ALL_APPS is true, shortcuts exposed via the app's manifest should never be
-     * duplicated or removed(unless the app is un-installed).
-     *
-     * @param launchIntent The intent that will be launched when the shortcut is clicked.
-     */
-    static boolean isValidShortcutLaunchIntent(Intent launchIntent) {
-        if (launchIntent != null
-                && Intent.ACTION_MAIN.equals(launchIntent.getAction())
-                && launchIntent.getComponent() != null
-                && launchIntent.getCategories() != null
-                && launchIntent.getCategories().size() == 1
-                && launchIntent.hasCategory(Intent.CATEGORY_LAUNCHER)
-                && launchIntent.getExtras() == null
-                && TextUtils.isEmpty(launchIntent.getDataString())) {
-            return false;
-        }
-        return true;
-    }
-
-    /**
      * Ensures that we have a valid, non-null name.  If the provided name is null, we will return
      * the application name instead.
      */
@@ -428,7 +414,7 @@
             // Already an activity target
             return original;
         }
-        if (isValidShortcutLaunchIntent(original.launchIntent)
+        if (!Utilities.isLauncherAppTarget(original.launchIntent)
                 || !original.user.equals(UserHandleCompat.myUserHandle())) {
             // We can only convert shortcuts which point to a main activity in the current user.
             return original;
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
new file mode 100644
index 0000000..92fdbde
--- /dev/null
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -0,0 +1,276 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.os.Build;
+import android.util.DisplayMetrics;
+import android.view.Display;
+import android.view.WindowManager;
+import com.android.launcher3.util.Thunk;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+public class InvariantDeviceProfile {
+
+    // This is a static that we use for the default icon size on a 4/5-inch phone
+    private static float DEFAULT_ICON_SIZE_DP = 60;
+
+    private static final ArrayList<InvariantDeviceProfile> sDeviceProfiles = new ArrayList<>();
+    static {
+        sDeviceProfiles.add(new InvariantDeviceProfile("Super Short Stubby",
+                255, 300,  2, 3, 2, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4));
+        sDeviceProfiles.add(new InvariantDeviceProfile("Shorter Stubby",
+                255, 400,  3, 3, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4));
+        sDeviceProfiles.add(new InvariantDeviceProfile("Short Stubby",
+                275, 420,  3, 4, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
+        sDeviceProfiles.add(new InvariantDeviceProfile("Stubby",
+                255, 450,  3, 4, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
+        sDeviceProfiles.add(new InvariantDeviceProfile("Nexus S",
+                296, 491.33f,  4, 4, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
+        sDeviceProfiles.add(new InvariantDeviceProfile("Nexus 4",
+                335, 567,  4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
+        sDeviceProfiles.add(new InvariantDeviceProfile("Nexus 5",
+                359, 567,  4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
+        sDeviceProfiles.add(new InvariantDeviceProfile("Large Phone",
+                406, 694,  5, 5, 4, 4,  64, 14.4f,  5, 56, R.xml.default_workspace_5x5));
+        // The tablet profile is odd in that the landscape orientation
+        // also includes the nav bar on the side
+        sDeviceProfiles.add(new InvariantDeviceProfile("Nexus 7",
+                575, 904,  5, 6, 4, 5, 72, 14.4f,  7, 60, R.xml.default_workspace_5x6));
+        // Larger tablet profiles always have system bars on the top & bottom
+        sDeviceProfiles.add(new InvariantDeviceProfile("Nexus 10",
+                727, 1207,  5, 6, 4, 5, 76, 14.4f,  7, 64, R.xml.default_workspace_5x6));
+        sDeviceProfiles.add(new InvariantDeviceProfile("20-inch Tablet",
+                1527, 2527,  7, 7, 6, 6, 100, 20,  7, 72, R.xml.default_workspace_4x4));
+    }
+
+    private class DeviceProfileQuery {
+        InvariantDeviceProfile profile;
+        float widthDps;
+        float heightDps;
+        float value;
+        PointF dimens;
+
+        DeviceProfileQuery(InvariantDeviceProfile p, float v) {
+            widthDps = p.minWidthDps;
+            heightDps = p.minHeightDps;
+            value = v;
+            dimens = new PointF(widthDps, heightDps);
+            profile = p;
+        }
+    }
+
+    // Profile-defining invariant properties
+    String name;
+    float minWidthDps;
+    float minHeightDps;
+    public int numRows;
+    public int numColumns;
+    public int numFolderRows;
+    public int numFolderColumns;
+    float iconSize;
+    float iconTextSize;
+    float numHotseatIcons;
+    float hotseatIconSize;
+    int defaultLayoutId;
+
+    // Derived invariant properties
+    int hotseatAllAppsRank;
+
+    DeviceProfile landscapeProfile;
+    DeviceProfile portraitProfile;
+
+    InvariantDeviceProfile() {
+    }
+
+    InvariantDeviceProfile(String n, float w, float h, int r, int c, int fr, int fc,
+            float is, float its, float hs, float his, int dlId) {
+        // Ensure that we have an odd number of hotseat items (since we need to place all apps)
+        if (hs % 2 == 0) {
+            throw new RuntimeException("All Device Profiles must have an odd number of hotseat spaces");
+        }
+
+        name = n;
+        minWidthDps = w;
+        minHeightDps = h;
+        numRows = r;
+        numColumns = c;
+        numFolderRows = fr;
+        numFolderColumns = fc;
+        iconSize = is;
+        iconTextSize = its;
+        numHotseatIcons = hs;
+        hotseatIconSize = his;
+        defaultLayoutId = dlId;
+    }
+
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
+    InvariantDeviceProfile(Context context) {
+        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        Display display = wm.getDefaultDisplay();
+        DisplayMetrics dm = new DisplayMetrics();
+        display.getMetrics(dm);
+
+        Point smallestSize = new Point();
+        Point largestSize = new Point();
+        display.getCurrentSizeRange(smallestSize, largestSize);
+
+        minWidthDps = Utilities.dpiFromPx(Math.min(smallestSize.x, smallestSize.y), dm);
+        minHeightDps = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y), dm);
+
+        ArrayList<DeviceProfileQuery> points =
+                new ArrayList<DeviceProfileQuery>();
+
+        // Find the closes profile given the width/height
+        for (InvariantDeviceProfile p : sDeviceProfiles) {
+            points.add(new DeviceProfileQuery(p, 0f));
+        }
+
+        InvariantDeviceProfile closestProfile =
+                findClosestDeviceProfile(minWidthDps, minHeightDps, points);
+
+        // The following properties are inherited directly from the nearest archetypal profile
+        numRows = closestProfile.numRows;
+        numColumns = closestProfile.numColumns;
+        numHotseatIcons = closestProfile.numHotseatIcons;
+        hotseatAllAppsRank = (int) (numHotseatIcons / 2);
+        defaultLayoutId = closestProfile.defaultLayoutId;
+        numFolderRows = closestProfile.numFolderRows;
+        numFolderColumns = closestProfile.numFolderColumns;
+
+
+        // The following properties are interpolated based on proximity to nearby archetypal
+        // profiles
+        points.clear();
+        for (InvariantDeviceProfile p : sDeviceProfiles) {
+            points.add(new DeviceProfileQuery(p, p.iconSize));
+        }
+        iconSize = invDistWeightedInterpolate(minWidthDps, minHeightDps, points);
+        points.clear();
+        for (InvariantDeviceProfile p : sDeviceProfiles) {
+            points.add(new DeviceProfileQuery(p, p.iconTextSize));
+        }
+        iconTextSize = invDistWeightedInterpolate(minWidthDps, minHeightDps, points);
+        points.clear();
+        for (InvariantDeviceProfile p : sDeviceProfiles) {
+            points.add(new DeviceProfileQuery(p, p.hotseatIconSize));
+        }
+        hotseatIconSize = invDistWeightedInterpolate(minWidthDps, minHeightDps, points);
+
+        // If the partner customization apk contains any grid overrides, apply them
+        // Supported overrides: numRows, numColumns, iconSize
+        applyPartnerDeviceProfileOverrides(context, dm);
+
+        Point realSize = new Point();
+        display.getRealSize(realSize);
+        // The real size never changes. smallSide and largeSize will remain the
+        // same in any orientation.
+        int smallSide = Math.min(realSize.x, realSize.y);
+        int largeSide = Math.max(realSize.x, realSize.y);
+
+        landscapeProfile = new DeviceProfile(context, this, smallestSize, largestSize,
+                largeSide, smallSide, true /* isLandscape */);
+        portraitProfile = new DeviceProfile(context, this, smallestSize, largestSize,
+                smallSide, largeSide, false /* isLandscape */);
+    }
+
+    /**
+     * Apply any Partner customization grid overrides.
+     *
+     * Currently we support: all apps row / column count.
+     */
+    private void applyPartnerDeviceProfileOverrides(Context ctx, DisplayMetrics dm) {
+        Partner p = Partner.get(ctx.getPackageManager());
+        if (p != null) {
+            p.applyInvariantDeviceProfileOverrides(this, dm);
+        }
+    }
+
+    @Thunk float dist(PointF p0, PointF p1) {
+        return (float) Math.sqrt((p1.x - p0.x)*(p1.x-p0.x) +
+                (p1.y-p0.y)*(p1.y-p0.y));
+    }
+
+    private float weight(PointF a, PointF b,
+                        float pow) {
+        float d = dist(a, b);
+        if (d == 0f) {
+            return Float.POSITIVE_INFINITY;
+        }
+        return (float) (1f / Math.pow(d, pow));
+    }
+
+    /** Returns the closest device profile given the width and height and a list of profiles */
+    private InvariantDeviceProfile findClosestDeviceProfile(float width, float height,
+                                                   ArrayList<DeviceProfileQuery> points) {
+        return findClosestDeviceProfiles(width, height, points).get(0).profile;
+    }
+
+    /** Returns the closest device profiles ordered by closeness to the specified width and height */
+    private ArrayList<DeviceProfileQuery> findClosestDeviceProfiles(float width, float height,
+                                                   ArrayList<DeviceProfileQuery> points) {
+        final PointF xy = new PointF(width, height);
+
+        // Sort the profiles by their closeness to the dimensions
+        ArrayList<DeviceProfileQuery> pointsByNearness = points;
+        Collections.sort(pointsByNearness, new Comparator<DeviceProfileQuery>() {
+            public int compare(DeviceProfileQuery a, DeviceProfileQuery b) {
+                return (int) (dist(xy, a.dimens) - dist(xy, b.dimens));
+            }
+        });
+
+        return pointsByNearness;
+    }
+
+    private float invDistWeightedInterpolate(float width, float height,
+                ArrayList<DeviceProfileQuery> points) {
+        float sum = 0;
+        float weights = 0;
+        float pow = 5;
+        float kNearestNeighbors = 3;
+        final PointF xy = new PointF(width, height);
+
+        ArrayList<DeviceProfileQuery> pointsByNearness = findClosestDeviceProfiles(width, height,
+                points);
+
+        for (int i = 0; i < pointsByNearness.size(); ++i) {
+            DeviceProfileQuery p = pointsByNearness.get(i);
+            if (i < kNearestNeighbors) {
+                float w = weight(xy, p.dimens, pow);
+                if (w == Float.POSITIVE_INFINITY) {
+                    return p.value;
+                }
+                weights += w;
+            }
+        }
+
+        for (int i = 0; i < pointsByNearness.size(); ++i) {
+            DeviceProfileQuery p = pointsByNearness.get(i);
+            if (i < kNearestNeighbors) {
+                float w = weight(xy, p.dimens, pow);
+                sum += w * p.value / weights;
+            }
+        }
+
+        return sum;
+    }
+}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 59ca978..5dd64e0 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -55,6 +55,7 @@
 import android.graphics.Color;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.AsyncTask;
@@ -97,6 +98,7 @@
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.PagedView.PageSwitchListener;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
+import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.LauncherActivityInfoCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
@@ -157,6 +159,10 @@
     private static final int REQUEST_BIND_APPWIDGET = 11;
     private static final int REQUEST_RECONFIGURE_APPWIDGET = 12;
 
+    private static final int WORKSPACE_BACKGROUND_GRADIENT = 0;
+    private static final int WORKSPACE_BACKGROUND_TRANSPARENT = 1;
+    private static final int WORKSPACE_BACKGROUND_BLACK = 2;
+
     /**
      * IntentStarter uses request codes starting with this. This must be greater than all activity
      * request codes used internally.
@@ -267,7 +273,7 @@
     private SearchDropTargetBar mSearchDropTargetBar;
 
     // Main container view for the all apps screen.
-    @Thunk AppsContainerView mAppsView;
+    @Thunk AllAppsContainerView mAppsView;
 
     // Main container view and the model for the widget tray screen.
     @Thunk WidgetsContainerView mWidgetsView;
@@ -425,8 +431,14 @@
         LauncherAppState app = LauncherAppState.getInstance();
         LauncherAppState.getLauncherProvider().setLauncherProviderChangeListener(this);
 
-        // Lazy-initialize the dynamic grid
-        mDeviceProfile = app.initDynamicGrid(this);
+        // Load configuration-specific DeviceProfile
+        mDeviceProfile = getResources().getConfiguration().orientation
+                == Configuration.ORIENTATION_LANDSCAPE ?
+                        app.getInvariantDeviceProfile().landscapeProfile
+                            : app.getInvariantDeviceProfile().portraitProfile;
+
+        // TODO: Move this to icon cache.
+        Utilities.setIconSize(mDeviceProfile.iconSizePx);
 
         // the LauncherApplication should call this, but in case of Instrumentation it might not be present yet
         mSharedPrefs = getSharedPreferences(LauncherAppState.getSharedPreferencesKey(),
@@ -434,7 +446,7 @@
         mIsSafeModeEnabled = getPackageManager().isSafeMode();
         mModel = app.setLauncher(this);
         mIconCache = app.getIconCache();
-        mIconCache.flushInvalidIcons(mDeviceProfile);
+
         mDragController = new DragController(this);
         mInflater = getLayoutInflater();
         mStateTransitionAnimation = new LauncherStateTransitionAnimation(this, this);
@@ -806,7 +818,7 @@
         }
 
         boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET ||
-                requestCode == REQUEST_CREATE_APPWIDGET || requestCode == REQUEST_CREATE_SHORTCUT);
+                requestCode == REQUEST_CREATE_APPWIDGET);
 
         final boolean workspaceLocked = isWorkspaceLocked();
         // We have special handling for widgets
@@ -1042,8 +1054,9 @@
             }
         }
 
-        // Background was set to gradient in onPause(), restore to black if in all apps.
-        setWorkspaceBackground(mState == State.WORKSPACE);
+        // Background was set to gradient in onPause(), restore to transparent if in all apps.
+        setWorkspaceBackground(mState == State.WORKSPACE ? WORKSPACE_BACKGROUND_TRANSPARENT
+                : WORKSPACE_BACKGROUND_GRADIENT);
 
         mPaused = false;
         if (mRestoring || mOnResumeNeedsLoad) {
@@ -1409,7 +1422,6 @@
         // Setup the hotseat
         mHotseat = (Hotseat) findViewById(R.id.hotseat);
         if (mHotseat != null) {
-            mHotseat.setup(this);
             mHotseat.setOnLongClickListener(this);
         }
 
@@ -1464,7 +1476,7 @@
                 mDragLayer.findViewById(R.id.search_drop_target_bar);
 
         // Setup Apps
-        mAppsView = (AppsContainerView) findViewById(R.id.apps_view);
+        mAppsView = (AllAppsContainerView) findViewById(R.id.apps_view);
         if (isAllAppsSearchOverridden()) {
             mAppsView.hideHeaderBar();
         }
@@ -1513,8 +1525,6 @@
      * Creates a view representing a shortcut.
      *
      * @param info The data structure describing the shortcut.
-     *
-     * @return A View inflated from R.layout.application.
      */
     View createShortcut(ShortcutInfo info) {
         return createShortcut((ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info);
@@ -1529,7 +1539,7 @@
      * @return A View inflated from layoutResId.
      */
     public View createShortcut(ViewGroup parent, ShortcutInfo info) {
-        BubbleTextView favorite = (BubbleTextView) mInflater.inflate(R.layout.application,
+        BubbleTextView favorite = (BubbleTextView) mInflater.inflate(R.layout.app_icon,
                 parent, false);
         favorite.applyFromShortcutInfo(info, mIconCache);
         favorite.setCompoundDrawablePadding(mDeviceProfile.iconDrawablePaddingPx);
@@ -1550,14 +1560,13 @@
         int[] touchXY = mPendingAddInfo.dropPos;
         CellLayout layout = getCellLayout(container, screenId);
 
-        boolean foundCellSpan = false;
-
-        ShortcutInfo info = mModel.infoFromShortcutIntent(this, data);
+        ShortcutInfo info = InstallShortcutReceiver.fromShortcutIntent(this, data);
         if (info == null) {
             return;
         }
         final View view = createShortcut(info);
 
+        boolean foundCellSpan = false;
         // First we check if we already know the exact location where we want to add this item.
         if (cellX >= 0 && cellY >= 0) {
             cellXY[0] = cellX;
@@ -1596,31 +1605,21 @@
         }
     }
 
-    static int[] getSpanForWidget(Context context, ComponentName component, int minWidth,
-            int minHeight) {
-        Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(context, component, null);
+    private int[] getSpanForWidget(ComponentName component, int minWidth, int minHeight) {
+        Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(this, component, null);
         // We want to account for the extra amount of padding that we are adding to the widget
         // to ensure that it gets the full amount of space that it has requested
         int requiredWidth = minWidth + padding.left + padding.right;
         int requiredHeight = minHeight + padding.top + padding.bottom;
-        return CellLayout.rectToCell(requiredWidth, requiredHeight, null);
+        return CellLayout.rectToCell(this, requiredWidth, requiredHeight, null);
     }
 
-    static int[] getSpanForWidget(Context context, AppWidgetProviderInfo info) {
-        return getSpanForWidget(context, info.provider, info.minWidth, info.minHeight);
+    public int[] getSpanForWidget(AppWidgetProviderInfo info) {
+        return getSpanForWidget(info.provider, info.minWidth, info.minHeight);
     }
 
-    static int[] getMinSpanForWidget(Context context, AppWidgetProviderInfo info) {
-        return getSpanForWidget(context, info.provider, info.minResizeWidth, info.minResizeHeight);
-    }
-
-    static int[] getSpanForWidget(Context context, PendingAddWidgetInfo info) {
-        return getSpanForWidget(context, info.componentName, info.minWidth, info.minHeight);
-    }
-
-    static int[] getMinSpanForWidget(Context context, PendingAddWidgetInfo info) {
-        return getSpanForWidget(context, info.componentName, info.minResizeWidth,
-                info.minResizeHeight);
+    public int[] getMinSpanForWidget(AppWidgetProviderInfo info) {
+        return getSpanForWidget(info.provider, info.minResizeWidth, info.minResizeHeight);
     }
 
     /**
@@ -1875,7 +1874,7 @@
         return mDragLayer;
     }
 
-    public AppsContainerView getAppsView() {
+    public AllAppsContainerView getAppsView() {
         return mAppsView;
     }
 
@@ -1911,6 +1910,10 @@
         return mSharedPrefs;
     }
 
+    public DeviceProfile getDeviceProfile() {
+        return mDeviceProfile;
+    }
+
     public void closeSystemDialogs() {
         getWindow().closeAllPanels();
 
@@ -3296,9 +3299,17 @@
         return (mState == State.WIDGETS) || (mOnResumeState == State.WIDGETS);
     }
 
-    private void setWorkspaceBackground(boolean workspace) {
-        mLauncherView.setBackground(workspace ?
-                mWorkspaceBackgroundDrawable : null);
+    private void setWorkspaceBackground(int background) {
+        switch (background) {
+            case WORKSPACE_BACKGROUND_TRANSPARENT:
+                getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+                break;
+            case WORKSPACE_BACKGROUND_BLACK:
+                getWindow().setBackgroundDrawable(null);
+                break;
+            default:
+                getWindow().setBackgroundDrawable(mWorkspaceBackgroundDrawable);
+        }
     }
 
     protected void changeWallpaperVisiblity(boolean visible) {
@@ -3308,7 +3319,7 @@
         if (wpflags != curflags) {
             getWindow().setFlags(wpflags, WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER);
         }
-        setWorkspaceBackground(visible);
+        setWorkspaceBackground(visible ? WORKSPACE_BACKGROUND_GRADIENT : WORKSPACE_BACKGROUND_BLACK);
     }
 
     @Override
@@ -3335,7 +3346,7 @@
         }
     }
 
-    protected void showWorkspace(boolean animated) {
+    public void showWorkspace(boolean animated) {
         showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated, null,
                 true);
     }
@@ -4008,7 +4019,7 @@
             // Note: This assumes that the id remap broadcast is received before this step.
             // If that is not the case, the id remap will be ignored and user may see the
             // click to setup view.
-            PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(appWidgetInfo, null);
+            PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(this, appWidgetInfo, null);
             pendingInfo.spanX = item.spanX;
             pendingInfo.spanY = item.spanY;
             pendingInfo.minSpanX = item.minSpanX;
@@ -4192,7 +4203,7 @@
     }
 
     protected Rect getSearchBarBounds() {
-        return mDeviceProfile.getSearchBarBounds();
+        return mDeviceProfile.getSearchBarBounds(Utilities.isRtl(getResources()));
     }
 
     public void bindSearchablesChanged() {
@@ -4801,14 +4812,6 @@
     }
 }
 
-interface LauncherTransitionable {
-    View getContent();
-    void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace);
-    void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace);
-    void onLauncherTransitionStep(Launcher l, float t);
-    void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace);
-}
-
 interface DebugIntents {
     static final String DELETE_DATABASE = "com.android.launcher3.action.DELETE_DATABASE";
     static final String MIGRATE_DATABASE = "com.android.launcher3.action.MIGRATE_DATABASE";
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index f540eb4..d4b41e6 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3;
 
-import android.annotation.TargetApi;
 import android.app.SearchManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -24,12 +23,7 @@
 import android.content.IntentFilter;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.graphics.Point;
-import android.os.Build;
-import android.util.DisplayMetrics;
 import android.util.Log;
-import android.view.Display;
-import android.view.WindowManager;
 
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.compat.LauncherAppsCompat;
@@ -38,7 +32,7 @@
 
 import java.lang.ref.WeakReference;
 
-public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks {
+public class LauncherAppState {
 
     private final AppFilter mAppFilter;
     private final BuildInfo mBuildInfo;
@@ -57,7 +51,7 @@
 
     private static LauncherAppState INSTANCE;
 
-    private DynamicGrid mDynamicGrid;
+    private InvariantDeviceProfile mInvariantDeviceProfile;
     private LauncherAccessibilityDelegate mAccessibilityDelegate;
 
     public static LauncherAppState getInstance() {
@@ -96,8 +90,9 @@
         // set sIsScreenXLarge and mScreenDensity *before* creating icon cache
         mIsScreenLarge = isScreenLarge(sContext.getResources());
         mScreenDensity = sContext.getResources().getDisplayMetrics().density;
-        mIconCache = new IconCache(sContext);
-        mWidgetCache = new WidgetPreviewLoader(sContext, mIconCache);
+        mInvariantDeviceProfile = new InvariantDeviceProfile(sContext);
+        mIconCache = new IconCache(sContext, mInvariantDeviceProfile);
+        mWidgetCache = new WidgetPreviewLoader(sContext, mInvariantDeviceProfile, mIconCache);
 
         mAppFilter = AppFilter.loadByName(sContext.getString(R.string.app_filter_class));
         mBuildInfo = BuildInfo.loadByName(sContext.getString(R.string.build_info_class));
@@ -172,49 +167,6 @@
         return LauncherFiles.SHARED_PREFERENCES_KEY;
     }
 
-    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
-    DeviceProfile initDynamicGrid(Context context) {
-        mDynamicGrid = createDynamicGrid(context, mDynamicGrid);
-        mDynamicGrid.getDeviceProfile().addCallback(this);
-        return mDynamicGrid.getDeviceProfile();
-    }
-
-    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
-    static DynamicGrid createDynamicGrid(Context context, DynamicGrid dynamicGrid) {
-        // Determine the dynamic grid properties
-        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
-        Display display = wm.getDefaultDisplay();
-
-        Point realSize = new Point();
-        display.getRealSize(realSize);
-        DisplayMetrics dm = new DisplayMetrics();
-        display.getMetrics(dm);
-
-        if (dynamicGrid == null) {
-            Point smallestSize = new Point();
-            Point largestSize = new Point();
-            display.getCurrentSizeRange(smallestSize, largestSize);
-
-            dynamicGrid = new DynamicGrid(context,
-                    context.getResources(),
-                    Math.min(smallestSize.x, smallestSize.y),
-                    Math.min(largestSize.x, largestSize.y),
-                    realSize.x, realSize.y,
-                    dm.widthPixels, dm.heightPixels);
-        }
-
-        // Update the icon size
-        DeviceProfile grid = dynamicGrid.getDeviceProfile();
-        grid.updateFromConfiguration(context, context.getResources(),
-                realSize.x, realSize.y,
-                dm.widthPixels, dm.heightPixels);
-        return dynamicGrid;
-    }
-
-    public DynamicGrid getDynamicGrid() {
-        return mDynamicGrid;
-    }
-
     public WidgetPreviewLoader getWidgetCache() {
         return mWidgetCache;
     }
@@ -251,9 +203,8 @@
         return result;
     }
 
-    @Override
-    public void onAvailableSizeChanged(DeviceProfile grid) {
-        Utilities.setIconSize(grid.iconSizePx);
+    public InvariantDeviceProfile getInvariantDeviceProfile() {
+        return mInvariantDeviceProfile;
     }
 
     public static boolean isDogfoodBuild() {
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index 954d2d7..71fb2d2 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -152,6 +152,10 @@
         return info;
     }
 
+    public LauncherAppWidgetProviderInfo getLauncherAppWidgetProviderInfo() {
+        return (LauncherAppWidgetProviderInfo) getAppWidgetInfo();
+    }
+
     @Override
     public void onTouchComplete() {
         if (!mLongPressHelper.hasPerformedLongPress()) {
diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
index e19bc95..7ca4fe3 100644
--- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
@@ -18,10 +18,11 @@
 public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo {
 
     public boolean isCustomWidget = false;
-    public int spanX = -1;
-    public int spanY = -1;
-    public int minSpanX = -1;
-    public int minSpanY = -1;
+
+    private int mSpanX = -1;
+    private int mSpanY = -1;
+    private int mMinSpanX = -1;
+    private int mMinSpanY = -1;
 
     public static LauncherAppWidgetProviderInfo fromProviderInfo(Context context,
             AppWidgetProviderInfo info) {
@@ -35,15 +36,6 @@
         p.setDataPosition(0);
         LauncherAppWidgetProviderInfo lawpi = new LauncherAppWidgetProviderInfo(p);
         p.recycle();
-
-        int[] minResizeSpan = Launcher.getMinSpanForWidget(context, lawpi);
-        int[] span = Launcher.getSpanForWidget(context, lawpi);
-
-        lawpi.spanX = span[0];
-        lawpi.spanY = span[1];
-        lawpi.minSpanX = minResizeSpan[0];
-        lawpi.minSpanY = minResizeSpan[1];
-
         return lawpi;
     }
 
@@ -60,11 +52,6 @@
         previewImage = widget.getPreviewImage();
         initialLayout = widget.getWidgetLayout();
         resizeMode = widget.getResizeMode();
-
-        spanX = widget.getSpanX();
-        spanY = widget.getSpanY();
-        minSpanX = widget.getMinSpanX();
-        minSpanY = widget.getMinSpanY();
     }
 
     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
@@ -87,7 +74,39 @@
         if (isCustomWidget) {
             return "WidgetProviderInfo(" + provider + ")";
         }
-        return String.format("WidgetProviderInfo provider:%s package:%s short:%s label:%s span(%d, %d) minSpan(%d, %d)",
-                provider.toString(), provider.getPackageName(), provider.getShortClassName(), getLabel(pm), spanX, spanY, minSpanX, minSpanY);
+        return String.format("WidgetProviderInfo provider:%s package:%s short:%s label:%s",
+                provider.toString(), provider.getPackageName(), provider.getShortClassName(), getLabel(pm));
+    }
+
+    public int getSpanX(Launcher launcher) {
+        lazyLoadSpans(launcher);
+        return mSpanX;
+    }
+
+    public int getSpanY(Launcher launcher) {
+        lazyLoadSpans(launcher);
+        return mSpanY;
+    }
+
+    public int getMinSpanX(Launcher launcher) {
+        lazyLoadSpans(launcher);
+        return mMinSpanX;
+    }
+
+    public int getMinSpanY(Launcher launcher) {
+        lazyLoadSpans(launcher);
+        return mMinSpanY;
+    }
+
+    private void lazyLoadSpans(Launcher launcher) {
+        if (mSpanX < 0 || mSpanY < 0 || mMinSpanX < 0 || mMinSpanY < 0) {
+            int[] minResizeSpan = launcher.getMinSpanForWidget(this);
+            int[] span = launcher.getSpanForWidget(this);
+
+            mSpanX = span[0];
+            mSpanY = span[1];
+            mMinSpanX = minResizeSpan[0];
+            mMinSpanY = minResizeSpan[1];
+        }
     }
  }
diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java
index 5f7173f..e607a0f 100644
--- a/src/com/android/launcher3/LauncherBackupAgentHelper.java
+++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java
@@ -94,6 +94,7 @@
             // TODO: Update the backup set to include rank.
             if (mHelper.restoredBackupVersion <= 2) {
                 LauncherAppState.getLauncherProvider().updateFolderItemsRank();
+                LauncherAppState.getLauncherProvider().convertShortcutsToLauncherActivities();
             }
         } else {
             if (VERBOSE) Log.v(TAG, "Nothing was restored, clearing DB");
diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java
index 92bbb40..af41012 100644
--- a/src/com/android/launcher3/LauncherBackupHelper.java
+++ b/src/com/android/launcher3/LauncherBackupHelper.java
@@ -141,13 +141,15 @@
     private final ItemTypeMatcher[] mItemTypeMatchers;
     private final long mUserSerial;
 
-    private IconCache mIconCache;
     private BackupManager mBackupManager;
     private byte[] mBuffer = new byte[512];
     private long mLastBackupRestoreTime;
     private boolean mBackupDataWasUpdated;
 
-    private DeviceProfieData mCurrentProfile;
+    private LauncherAppState mLauncherAppState;
+    private IconCache mIconCache;
+    private DeviceProfieData mDeviceProfileData;
+
     boolean restoreSuccessful;
     int restoredBackupVersion = 1;
 
@@ -197,10 +199,14 @@
 
         Journal in = readJournal(oldState);
         if (!launcherIsReady()) {
+            dataChanged();
             // Perform backup later.
             writeJournal(newState, in);
             return;
         }
+
+        lazyInitAppState(true /* noCreate */);
+
         Log.v(TAG, "lastBackupTime = " + in.t);
         mKeys.clear();
         applyJournal(in);
@@ -296,12 +302,7 @@
         if (!restoreSuccessful) {
             return;
         }
-        if (!initializeIconCache()) {
-            // During restore we do not need an initialized instance of IconCache. We can create
-            // a temporary icon cache here, as the process will be rebooted after restore
-            // is complete.
-            mIconCache = new IconCache(mContext);
-        }
+        lazyInitAppState(false /* noCreate */);
 
         int dataSize = data.size();
         if (mBuffer.length < dataSize) {
@@ -395,19 +396,34 @@
      * @return the current device profile information.
      */
     private DeviceProfieData getDeviceProfieData() {
-        if (mCurrentProfile != null) {
-            return mCurrentProfile;
-        }
-        final Context applicationContext = mContext.getApplicationContext();
-        DeviceProfile profile = LauncherAppState.createDynamicGrid(applicationContext, null)
-                .getDeviceProfile();
+        return mDeviceProfileData;
+    }
 
-        mCurrentProfile = new DeviceProfieData();
-        mCurrentProfile.desktopRows = profile.numRows;
-        mCurrentProfile.desktopCols = profile.numColumns;
-        mCurrentProfile.hotseatCount = profile.numHotseatIcons;
-        mCurrentProfile.allappsRank = profile.hotseatAllAppsRank;
-        return mCurrentProfile;
+    private void lazyInitAppState(boolean noCreate) {
+        if (mLauncherAppState != null) {
+            return;
+        }
+
+        if (noCreate) {
+            mLauncherAppState = LauncherAppState.getInstanceNoCreate();
+        } else {
+            LauncherAppState.setApplicationContext(mContext);
+            mLauncherAppState = LauncherAppState.getInstance();
+        }
+
+        mIconCache = mLauncherAppState.getIconCache();
+        InvariantDeviceProfile profile = mLauncherAppState.getInvariantDeviceProfile();
+
+        mDeviceProfileData = initDeviceProfileData(profile);
+    }
+
+    private DeviceProfieData initDeviceProfileData(InvariantDeviceProfile profile) {
+        DeviceProfieData data = new DeviceProfieData();
+        data.desktopRows = profile.numRows;
+        data.desktopCols = profile.numColumns;
+        data.hotseatCount = profile.numHotseatIcons;
+        data.allappsRank = profile.hotseatAllAppsRank;
+        return data;
     }
 
     /**
@@ -518,11 +534,6 @@
      */
     private void backupIcons(BackupDataOutput data) throws IOException {
         // persist icons that haven't been persisted yet
-        if (!initializeIconCache()) {
-            dataChanged(); // try again later
-            if (DEBUG) Log.d(TAG, "Launcher is not initialized, delaying icon backup");
-            return;
-        }
         final ContentResolver cr = mContext.getContentResolver();
         final int dpi = mContext.getResources().getDisplayMetrics().densityDpi;
         final UserHandleCompat myUserHandle = UserHandleCompat.myUserHandle();
@@ -619,16 +630,9 @@
      */
     private void backupWidgets(BackupDataOutput data) throws IOException {
         // persist static widget info that hasn't been persisted yet
-        final LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
-        if (appState == null || !initializeIconCache()) {
-            Log.w(TAG, "Failed to get icon cache during restore");
-            return;
-        }
         final ContentResolver cr = mContext.getContentResolver();
-        final WidgetPreviewLoader previewLoader = appState.getWidgetCache();
+        final WidgetPreviewLoader previewLoader = mLauncherAppState.getWidgetCache();
         final int dpi = mContext.getResources().getDisplayMetrics().densityDpi;
-        final DeviceProfile profile = appState.getDynamicGrid().getDeviceProfile();
-        if (DEBUG) Log.d(TAG, "cellWidthPx: " + profile.cellWidthPx);
         int backupWidgetCount = 0;
 
         String where = Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPWIDGET + " AND "
@@ -661,8 +665,7 @@
                         if (DEBUG) Log.d(TAG, "saving widget " + backupKey);
                         UserHandleCompat user = UserHandleCompat.myUserHandle();
                         writeRowToBackup(key,
-                                packWidget(dpi, previewLoader,spanX * profile.cellWidthPx,
-                                        mIconCache, provider, user),
+                                packWidget(dpi, previewLoader, mIconCache, provider, user),
                                 data);
                         mKeys.add(key);
                         backupWidgetCount ++;
@@ -969,8 +972,7 @@
     }
 
     /** Serialize a widget for persistence, including a checksum wrapper. */
-    private Widget packWidget(int dpi, WidgetPreviewLoader previewLoader,
-            int previewWidth, IconCache iconCache,
+    private Widget packWidget(int dpi, WidgetPreviewLoader previewLoader, IconCache iconCache,
             ComponentName provider, UserHandleCompat user) {
         final LauncherAppWidgetProviderInfo info =
                 LauncherModel.getProviderInfo(mContext, provider, user);
@@ -985,12 +987,6 @@
             widget.icon.data = Utilities.flattenBitmap(icon);
             widget.icon.dpi = dpi;
         }
-        if (info.previewImage != 0) {
-            widget.preview = new Resource();
-            Bitmap preview = previewLoader.generateWidgetPreview(info, previewWidth, null);
-            widget.preview.data = Utilities.flattenBitmap(preview);
-            widget.preview.dpi = dpi;
-        }
         return widget;
     }
 
@@ -1136,25 +1132,6 @@
         return wrapper.payload;
     }
 
-    private boolean initializeIconCache() {
-        if (mIconCache != null) {
-            return true;
-        }
-
-        final LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
-        if (appState == null) {
-            if (DEBUG) {
-                Throwable stackTrace = new Throwable();
-                stackTrace.fillInStackTrace();
-                Log.w(TAG, "Failed to get app state during backup/restore", stackTrace);
-            }
-            return false;
-        }
-        mIconCache = appState.getIconCache();
-        return mIconCache != null;
-    }
-
-
     /**
      * @return true if the launcher is in a state to support backup
      */
@@ -1167,9 +1144,8 @@
         }
         cursor.close();
 
-        if (!initializeIconCache()) {
+        if (LauncherAppState.getInstanceNoCreate() == null) {
             // launcher services are unavailable, try again later
-            dataChanged();
             return false;
         }
 
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index e293c2a..224ebbf 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -424,9 +424,9 @@
     private static boolean findNextAvailableIconSpaceInScreen(ArrayList<ItemInfo> occupiedPos,
             int[] xy, int spanX, int spanY) {
         LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-        final int xCount = (int) grid.numColumns;
-        final int yCount = (int) grid.numRows;
+        InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
+        final int xCount = (int) profile.numColumns;
+        final int yCount = (int) profile.numRows;
         boolean[][] occupied = new boolean[xCount][yCount];
         if (occupiedPos != null) {
             for (ItemInfo r : occupiedPos) {
@@ -971,7 +971,8 @@
                         break;
                 }
 
-                folderInfo.title = Utilities.trim(c.getString(titleIndex));
+                // Do not trim the folder label, as is was set by the user.
+                folderInfo.title = c.getString(titleIndex);
                 folderInfo.id = id;
                 folderInfo.container = c.getInt(containerIndex);
                 folderInfo.screenId = c.getInt(screenIndex);
@@ -1664,9 +1665,9 @@
         // check & update map of what's occupied; used to discard overlapping/invalid items
         private boolean checkItemPlacement(LongArrayMap<ItemInfo[][]> occupied, ItemInfo item) {
             LauncherAppState app = LauncherAppState.getInstance();
-            DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-            final int countX = (int) grid.numColumns;
-            final int countY = (int) grid.numRows;
+            InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
+            final int countX = (int) profile.numColumns;
+            final int countY = (int) profile.numRows;
 
             long containerIndex = item.screenId;
             if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
@@ -1682,10 +1683,10 @@
                 final ItemInfo[][] hotseatItems =
                         occupied.get((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT);
 
-                if (item.screenId >= grid.numHotseatIcons) {
+                if (item.screenId >= profile.numHotseatIcons) {
                     Log.e(TAG, "Error loading shortcut " + item
                             + " into hotseat position " + item.screenId
-                            + ", position out of bounds: (0 to " + (grid.numHotseatIcons - 1)
+                            + ", position out of bounds: (0 to " + (profile.numHotseatIcons - 1)
                             + ")");
                     return false;
                 }
@@ -1703,7 +1704,7 @@
                         return true;
                     }
                 } else {
-                    final ItemInfo[][] items = new ItemInfo[(int) grid.numHotseatIcons][1];
+                    final ItemInfo[][] items = new ItemInfo[(int) profile.numHotseatIcons][1];
                     items[(int) item.screenId][0] = item;
                     occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, items);
                     return true;
@@ -1777,9 +1778,9 @@
                     new IntentFilter(StartupReceiver.SYSTEM_READY)) != null;
 
             LauncherAppState app = LauncherAppState.getInstance();
-            DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-            int countX = (int) grid.numColumns;
-            int countY = (int) grid.numRows;
+            InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
+            int countX = (int) profile.numColumns;
+            int countY = (int) profile.numRows;
 
             if ((mFlags & LOADER_FLAG_CLEAR_WORKSPACE) != 0) {
                 Launcher.addDumpLog(TAG, "loadWorkspace: resetting launcher database", true);
@@ -2028,7 +2029,7 @@
                                                 "constructing info for partially restored package",
                                                 true);
                                         info = getRestoredItemInfo(c, titleIndex, intent,
-                                                promiseType, useLowResIcon);
+                                                promiseType);
                                         intent = getRestoredItemIntent(c, context, intent);
                                     } else {
                                         // Don't restore items for other profiles.
@@ -2113,7 +2114,8 @@
                                 id = c.getLong(idIndex);
                                 FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id);
 
-                                folderInfo.title = Utilities.trim(c.getString(titleIndex));
+                                // Do not trim the folder label, as is was set by the user.
+                                folderInfo.title = c.getString(titleIndex);
                                 folderInfo.id = id;
                                 container = c.getInt(containerIndex);
                                 folderInfo.container = container;
@@ -2190,13 +2192,6 @@
                                         appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
                                                 provider.provider);
 
-                                        if (!customWidget) {
-                                            int[] minSpan =
-                                                    Launcher.getMinSpanForWidget(context, provider);
-                                            appWidgetInfo.minSpanX = minSpan[0];
-                                            appWidgetInfo.minSpanY = minSpan[1];
-                                        }
-
                                         int status = restoreStatus;
                                         if (!wasProviderReady) {
                                             // If provider was not previously ready, update the
@@ -2244,12 +2239,6 @@
                                     appWidgetInfo.spanX = c.getInt(spanXIndex);
                                     appWidgetInfo.spanY = c.getInt(spanYIndex);
 
-                                    if (!customWidget) {
-                                        int[] minSpan = Launcher.getMinSpanForWidget(context, provider);
-                                        appWidgetInfo.minSpanX = minSpan[0];
-                                        appWidgetInfo.minSpanY = minSpan[1];
-                                    }
-
                                     container = c.getInt(containerIndex);
                                     if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
                                         container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
@@ -2512,13 +2501,13 @@
          * right) */
         private void sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems) {
             final LauncherAppState app = LauncherAppState.getInstance();
-            final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+            final InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
             // XXX: review this
             Collections.sort(workspaceItems, new Comparator<ItemInfo>() {
                 @Override
                 public int compare(ItemInfo lhs, ItemInfo rhs) {
-                    int cellCountX = (int) grid.numColumns;
-                    int cellCountY = (int) grid.numRows;
+                    int cellCountX = (int) profile.numColumns;
+                    int cellCountY = (int) profile.numRows;
                     int screenOffset = cellCountX * cellCountY;
                     int containerOffset = screenOffset * (Launcher.SCREEN_COUNT + 1); // +1 hotseat
                     long lr = (lhs.container * containerOffset + lhs.screenId * screenOffset +
@@ -2757,7 +2746,7 @@
                         return;
                     }
                 }
-                updateAllAppsIconsCache();
+                mIconCache.updateDbIcons();
                 synchronized (LoaderTask.this) {
                     if (mStopped) {
                         return;
@@ -2883,68 +2872,6 @@
             }
         }
 
-        private void updateAllAppsIconsCache() {
-            final ArrayList<AppInfo> updatedApps = new ArrayList<>();
-
-            for (UserHandleCompat user : mUserManager.getUserProfiles()) {
-                // Query for the set of apps
-                final List<LauncherActivityInfoCompat> apps = mLauncherApps.getActivityList(null, user);
-                // Fail if we don't have any apps
-                // TODO: Fix this. Only fail for the current user.
-                if (apps == null || apps.isEmpty()) {
-                    return;
-                }
-
-                // Update icon cache
-                HashSet<String> updatedPackages = mIconCache.updateDBIcons(user, apps);
-
-                // If any package icon has changed (app was updated while launcher was dead),
-                // update the corresponding shortcuts.
-                if (!updatedPackages.isEmpty()) {
-                    final ArrayList<ShortcutInfo> updatedShortcuts = new ArrayList<>();
-                    synchronized (sBgLock) {
-                        for (ItemInfo info : sBgItemsIdMap) {
-                            if (info instanceof ShortcutInfo && user.equals(info.user)
-                                    && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
-                                ShortcutInfo si = (ShortcutInfo) info;
-                                ComponentName cn = si.getTargetComponent();
-                                if (cn != null && updatedPackages.contains(cn.getPackageName())) {
-                                    si.updateIcon(mIconCache);
-                                    updatedShortcuts.add(si);
-                                }
-                            }
-                        }
-                        mBgAllAppsList.updateIconsAndLabels(updatedPackages, user, updatedApps);
-                    }
-
-                    if (!updatedShortcuts.isEmpty()) {
-                        final UserHandleCompat userFinal = user;
-                        mHandler.post(new Runnable() {
-
-                            public void run() {
-                                Callbacks cb = getCallback();
-                                if (cb != null) {
-                                    cb.bindShortcutsChanged(updatedShortcuts,
-                                            new ArrayList<ShortcutInfo>(), userFinal);
-                                }
-                            }
-                        });
-                    }
-                }
-            }
-            if (!updatedApps.isEmpty()) {
-                mHandler.post(new Runnable() {
-
-                    public void run() {
-                        Callbacks cb = getCallback();
-                        if (cb != null) {
-                            cb.bindAppsUpdated(updatedApps);
-                        }
-                    }
-                });
-            }
-        }
-
         public void dumpState() {
             synchronized (sBgLock) {
                 Log.d(TAG, "mLoaderTask.mContext=" + mContext);
@@ -2955,6 +2882,58 @@
         }
     }
 
+    /**
+     * Called when the icons for packages have been updated in the icon cache.
+     */
+    public void onPackageIconsUpdated(HashSet<String> updatedPackages, UserHandleCompat user) {
+        final Callbacks callbacks = getCallback();
+        final ArrayList<AppInfo> updatedApps = new ArrayList<>();
+        final ArrayList<ShortcutInfo> updatedShortcuts = new ArrayList<>();
+
+        // If any package icon has changed (app was updated while launcher was dead),
+        // update the corresponding shortcuts.
+        synchronized (sBgLock) {
+            for (ItemInfo info : sBgItemsIdMap) {
+                if (info instanceof ShortcutInfo && user.equals(info.user)
+                        && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+                    ShortcutInfo si = (ShortcutInfo) info;
+                    ComponentName cn = si.getTargetComponent();
+                    if (cn != null && updatedPackages.contains(cn.getPackageName())) {
+                        si.updateIcon(mIconCache);
+                        updatedShortcuts.add(si);
+                    }
+                }
+            }
+            mBgAllAppsList.updateIconsAndLabels(updatedPackages, user, updatedApps);
+        }
+
+        if (!updatedShortcuts.isEmpty()) {
+            final UserHandleCompat userFinal = user;
+            mHandler.post(new Runnable() {
+
+                public void run() {
+                    Callbacks cb = getCallback();
+                    if (cb != null && callbacks == cb) {
+                        cb.bindShortcutsChanged(updatedShortcuts,
+                                new ArrayList<ShortcutInfo>(), userFinal);
+                    }
+                }
+            });
+        }
+
+        if (!updatedApps.isEmpty()) {
+            mHandler.post(new Runnable() {
+
+                public void run() {
+                    Callbacks cb = getCallback();
+                    if (cb != null && callbacks == cb) {
+                        cb.bindAppsUpdated(updatedApps);
+                    }
+                }
+            });
+        }
+    }
+
     void enqueuePackageUpdated(PackageUpdatedTask task) {
         sWorker.post(task);
     }
@@ -3414,10 +3393,10 @@
      * to a package that is not yet installed on the system.
      */
     public ShortcutInfo getRestoredItemInfo(Cursor cursor, int titleIndex, Intent intent,
-            int promiseType, boolean useLowResIcon) {
+            int promiseType) {
         final ShortcutInfo info = new ShortcutInfo();
         info.user = UserHandleCompat.myUserHandle();
-        mIconCache.getTitleAndIcon(info, intent, info.user, useLowResIcon);
+        mIconCache.getTitleAndIcon(info, intent, info.user, false /* useLowResIcon */);
 
         if ((promiseType & ShortcutInfo.FLAG_RESTORED_ICON) != 0) {
             String title = (cursor != null) ? cursor.getString(titleIndex) : null;
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 8bc8988..2751152 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -65,7 +65,7 @@
     private static final String TAG = "Launcher.LauncherProvider";
     private static final boolean LOGD = false;
 
-    private static final int DATABASE_VERSION = 25;
+    private static final int DATABASE_VERSION = 26;
 
     static final String OLD_AUTHORITY = "com.android.launcher2.settings";
     static final String AUTHORITY = ProviderConfig.AUTHORITY;
@@ -364,7 +364,7 @@
 
     private DefaultLayoutParser getDefaultLayoutParser() {
         int defaultLayout = LauncherAppState.getInstance()
-                .getDynamicGrid().getDeviceProfile().defaultLayoutId;
+                .getInvariantDeviceProfile().defaultLayoutId;
         return new DefaultLayoutParser(getContext(), mOpenHelper.mAppWidgetHost,
                 mOpenHelper, getContext().getResources(), defaultLayout);
     }
@@ -378,6 +378,11 @@
         mOpenHelper.updateFolderItemsRank(mOpenHelper.getWritableDatabase(), false);
     }
 
+    public void convertShortcutsToLauncherActivities() {
+        mOpenHelper.convertShortcutsToLauncherActivities(mOpenHelper.getWritableDatabase());
+    }
+
+
     public void deleteDatabase() {
         // Are you sure? (y/n)
         final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
@@ -623,10 +628,12 @@
                     }
                 }
                 case 23:
-                    convertShortcutsToLauncherActivities(db);
+                    // No-op
                 case 24:
                     ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mContext);
-                case 25: {
+                case 25:
+                    convertShortcutsToLauncherActivities(db);
+                case 26: {
                     // DB Upgraded successfully
                     return;
                 }
@@ -1035,10 +1042,10 @@
                         int curY = 0;
 
                         final LauncherAppState app = LauncherAppState.getInstance();
-                        final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-                        final int width = (int) grid.numColumns;
-                        final int height = (int) grid.numRows;
-                        final int hotseatWidth = (int) grid.numHotseatIcons;
+                        final InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
+                        final int width = (int) profile.numColumns;
+                        final int height = (int) profile.numRows;
+                        final int hotseatWidth = (int) profile.numHotseatIcons;
 
                         final HashSet<String> seenIntents = new HashSet<String>(c.getCount());
 
@@ -1180,7 +1187,7 @@
                             int hotseatX = hotseat.keyAt(idx);
                             ContentValues values = hotseat.valueAt(idx);
 
-                            if (hotseatX == grid.hotseatAllAppsRank) {
+                            if (hotseatX == profile.hotseatAllAppsRank) {
                                 // let's drop this in the next available hole in the hotseat
                                 while (++hotseatX < hotseatWidth) {
                                     if (hotseat.get(hotseatX) == null) {
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index 3243754..f373fde 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -23,14 +23,13 @@
 import android.animation.PropertyValuesHolder;
 import android.animation.TimeInterpolator;
 import android.annotation.SuppressLint;
-import android.annotation.TargetApi;
 import android.content.res.Resources;
-import android.os.Build;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewAnimationUtils;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
+import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.widget.WidgetsContainerView;
 import java.util.HashMap;
@@ -129,7 +128,7 @@
      * Starts an animation to the apps view.
      */
     public void startAnimationToAllApps(final boolean animated) {
-        final AppsContainerView toView = mLauncher.getAppsView();
+        final AllAppsContainerView toView = mLauncher.getAppsView();
         PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
             private int[] mAllAppsToPanelDelta;
 
@@ -156,8 +155,7 @@
             }
             @Override
             public float getMaterialRevealViewStartFinalRadius() {
-                int allAppsButtonSize = LauncherAppState.getInstance().
-                        getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize;
+                int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
                 return allAppsButtonSize / 2;
             }
             @Override
@@ -234,9 +232,9 @@
              final boolean hideSearchBar, final PrivateTransitionCallbacks pCb) {
         final Resources res = mLauncher.getResources();
         final boolean material = Utilities.isLmpOrAbove();
-        final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime);
+        final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
         final int itemsAlphaStagger =
-                res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger);
+                res.getInteger(R.integer.config_overlayItemsAlphaStagger);
 
         final View allAppsButtonView = mLauncher.getAllAppsButton();
         final View fromView = mLauncher.getWorkspace();
@@ -429,7 +427,7 @@
     private void startAnimationToWorkspaceFromAllApps(final Launcher.State fromState,
               final Workspace.State toWorkspaceState, final int toWorkspacePage,
               final boolean animated, final Runnable onCompleteRunnable) {
-        AppsContainerView appsView = mLauncher.getAppsView();
+        AllAppsContainerView appsView = mLauncher.getAppsView();
         PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
             int[] mAllAppsToPanelDelta;
 
@@ -456,8 +454,7 @@
             }
             @Override
             float getMaterialRevealViewStartFinalRadius() {
-                int allAppsButtonSize = LauncherAppState.getInstance().
-                        getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize;
+                int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
                 return allAppsButtonSize / 2;
             }
             @Override
@@ -532,9 +529,9 @@
               final PrivateTransitionCallbacks pCb) {
         final Resources res = mLauncher.getResources();
         final boolean material = Utilities.isLmpOrAbove();
-        final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime);
+        final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
         final int itemsAlphaStagger =
-                res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger);
+                res.getInteger(R.integer.config_overlayItemsAlphaStagger);
 
         final View allAppsButtonView = mLauncher.getAllAppsButton();
         final View toView = mLauncher.getWorkspace();
diff --git a/src/com/android/launcher3/LauncherTransitionable.java b/src/com/android/launcher3/LauncherTransitionable.java
new file mode 100644
index 0000000..9442abc
--- /dev/null
+++ b/src/com/android/launcher3/LauncherTransitionable.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.view.View;
+
+/**
+ * An interface to get callbacks during a launcher transition.
+ */
+public interface LauncherTransitionable {
+    View getContent();
+    void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace);
+    void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace);
+    void onLauncherTransitionStep(Launcher l, float t);
+    void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace);
+}
diff --git a/src/com/android/launcher3/Partner.java b/src/com/android/launcher3/Partner.java
index e191319..380078b 100644
--- a/src/com/android/launcher3/Partner.java
+++ b/src/com/android/launcher3/Partner.java
@@ -47,8 +47,6 @@
     public static final String RES_REQUIRE_FIRST_RUN_FLOW = "requires_first_run_flow";
 
     /** These resources are used to override the device profile  */
-    public static final String RES_GRID_AA_SHORT_EDGE_COUNT = "grid_aa_short_edge_count";
-    public static final String RES_GRID_AA_LONG_EDGE_COUNT = "grid_aa_long_edge_count";
     public static final String RES_GRID_NUM_ROWS = "grid_num_rows";
     public static final String RES_GRID_NUM_COLUMNS = "grid_num_columns";
     public static final String RES_GRID_ICON_SIZE_DP = "grid_icon_size_dp";
@@ -116,56 +114,42 @@
         return resId != 0 && getResources().getBoolean(resId);
     }
 
-    public DeviceProfile getDeviceProfileOverride(DisplayMetrics dm) {
-        boolean containsProfileOverrides = false;
-
-        DeviceProfile dp = new DeviceProfile();
-
-        // We initialize customizable fields to be invalid
-        dp.numRows = -1;
-        dp.numColumns = -1;
-        dp.allAppsShortEdgeCount = -1;
-        dp.allAppsLongEdgeCount = -1;
+    public void applyInvariantDeviceProfileOverrides(InvariantDeviceProfile inv, DisplayMetrics dm) {
+        int numRows = -1;
+        int numColumns = -1;
+        float iconSize = -1;
 
         try {
             int resId = getResources().getIdentifier(RES_GRID_NUM_ROWS,
                     "integer", getPackageName());
             if (resId > 0) {
-                containsProfileOverrides = true;
-                dp.numRows = getResources().getInteger(resId);
+                numRows = getResources().getInteger(resId);
             }
 
             resId = getResources().getIdentifier(RES_GRID_NUM_COLUMNS,
                     "integer", getPackageName());
             if (resId > 0) {
-                containsProfileOverrides = true;
-                dp.numColumns = getResources().getInteger(resId);
-            }
-
-            resId = getResources().getIdentifier(RES_GRID_AA_SHORT_EDGE_COUNT,
-                    "integer", getPackageName());
-            if (resId > 0) {
-                containsProfileOverrides = true;
-                dp.allAppsShortEdgeCount = getResources().getInteger(resId);
-            }
-
-            resId = getResources().getIdentifier(RES_GRID_AA_LONG_EDGE_COUNT,
-                    "integer", getPackageName());
-            if (resId > 0) {
-                containsProfileOverrides = true;
-                dp.allAppsLongEdgeCount = getResources().getInteger(resId);
+                numColumns = getResources().getInteger(resId);
             }
 
             resId = getResources().getIdentifier(RES_GRID_ICON_SIZE_DP,
                     "dimen", getPackageName());
             if (resId > 0) {
-                containsProfileOverrides = true;
                 int px = getResources().getDimensionPixelSize(resId);
-                dp.iconSize = DynamicGrid.dpiFromPx(px, dm);
+                iconSize = Utilities.dpiFromPx(px, dm);
             }
         } catch (Resources.NotFoundException ex) {
             Log.e(TAG, "Invalid Partner grid resource!", ex);
+            return;
         }
-        return containsProfileOverrides ? dp : null;
+
+        if (numRows > 0 && numColumns > 0) {
+            inv.numRows = numRows;
+            inv.numColumns = numColumns;
+        }
+
+        if (iconSize > 0) {
+            inv.iconSize = iconSize;
+        }
     }
 }
diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java
index 179c60a..c8b27ef 100644
--- a/src/com/android/launcher3/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/PendingAppWidgetHostView.java
@@ -42,6 +42,7 @@
     private final int mStartState;
     private final Intent mIconLookupIntent;
     private final boolean mDisabledForSafeMode;
+    private Launcher mLauncher;
 
     private Bitmap mIcon;
 
@@ -56,6 +57,8 @@
     public PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info,
             boolean disabledForSafeMode) {
         super(context);
+
+        mLauncher = (Launcher) context;
         mInfo = info;
         mStartState = info.restoreStatus;
         mIconLookupIntent = new Intent().setComponent(info.providerName);
@@ -64,7 +67,7 @@
         mPaint = new TextPaint();
         mPaint.setColor(0xFFFFFFFF);
         mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
-                getDeviceProfile().iconTextSizePx, getResources().getDisplayMetrics()));
+                mLauncher.getDeviceProfile().iconTextSizePx, getResources().getDisplayMetrics()));
         setBackgroundResource(R.drawable.quantum_panel_dark);
         setWillNotDraw(false);
     }
@@ -173,12 +176,12 @@
             return;
         }
 
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         if (mTopCornerDrawable == null) {
             if (mDrawableSizeChanged) {
                 int outset = (mCenterDrawable instanceof PreloadIconDrawable) ?
                         ((PreloadIconDrawable) mCenterDrawable).getOutset() : 0;
-                int maxSize = LauncherAppState.getInstance().getDynamicGrid()
-                        .getDeviceProfile().iconSizePx + 2 * outset;
+                int maxSize = grid.iconSizePx + 2 * outset;
                 int size = Math.min(maxSize, Math.min(
                         getWidth() - getPaddingLeft() - getPaddingRight(),
                         getHeight() - getPaddingTop() - getPaddingBottom()));
@@ -193,7 +196,6 @@
         } else  {
             // Draw the top corner icon and "Setup" text is possible
             if (mDrawableSizeChanged) {
-                DeviceProfile grid = getDeviceProfile();
                 int iconSize = grid.iconSizePx;
                 int paddingTop = getPaddingTop();
                 int paddingBottom = getPaddingBottom();
@@ -251,8 +253,4 @@
             }
         }
     }
-
-    private DeviceProfile getDeviceProfile() {
-        return LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
-    }
 }
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 490ed6a..157b48a 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -45,10 +45,13 @@
     private int mCountX;
     private int mCountY;
 
+    private Launcher mLauncher;
+
     private boolean mInvertIfRtl = false;
 
     public ShortcutAndWidgetContainer(Context context) {
         super(context);
+        mLauncher = (Launcher) context;
         mWallpaperManager = WallpaperManager.getInstance(context);
     }
 
@@ -125,22 +128,19 @@
     }
 
     int getCellContentWidth() {
-        final LauncherAppState app = LauncherAppState.getInstance();
-        final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        final DeviceProfile grid = mLauncher.getDeviceProfile();
         return Math.min(getMeasuredHeight(), mIsHotseatLayout ?
                 grid.hotseatCellWidthPx: grid.cellWidthPx);
     }
 
     int getCellContentHeight() {
-        final LauncherAppState app = LauncherAppState.getInstance();
-        final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        final DeviceProfile grid = mLauncher.getDeviceProfile();
         return Math.min(getMeasuredHeight(), mIsHotseatLayout ?
                 grid.hotseatCellHeightPx : grid.cellHeightPx);
     }
 
     public void measureChild(View child) {
-        final LauncherAppState app = LauncherAppState.getInstance();
-        final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        final DeviceProfile grid = mLauncher.getDeviceProfile();
         final int cellWidth = mCellWidth;
         final int cellHeight = mCellHeight;
         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java
index 0fc8f32..4199390 100644
--- a/src/com/android/launcher3/UninstallDropTarget.java
+++ b/src/com/android/launcher3/UninstallDropTarget.java
@@ -28,7 +28,7 @@
         // Get the hover color
         mHoverColor = getResources().getColor(R.color.uninstall_target_hover_tint);
 
-        setDrawable(R.drawable.uninstall_target_selector);
+        setDrawable(R.drawable.ic_launcher_uninstall_normal);
     }
 
     @Override
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 1f8a6f2..6c4b720 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -45,12 +45,17 @@
 import android.graphics.drawable.PaintDrawable;
 import android.os.Build;
 import android.os.Process;
+import android.text.TextUtils;
+import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
+import android.util.TypedValue;
 import android.view.View;
 import android.widget.Toast;
 
+import junit.framework.Assert;
+
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -58,8 +63,6 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import junit.framework.Assert;
-
 /**
  * Various utilities shared amongst the Launcher's classes.
  */
@@ -650,4 +653,35 @@
             Assert.assertTrue(LauncherModel.sWorkerThread.getThreadId() == Process.myTid());
         }
     }
+
+    /**
+     * Returns true if the intent is a valid launch intent for a launcher activity of an app.
+     * This is used to identify shortcuts which are different from the ones exposed by the
+     * applications' manifest file.
+     *
+     * @param launchIntent The intent that will be launched when the shortcut is clicked.
+     */
+    public static boolean isLauncherAppTarget(Intent launchIntent) {
+        return launchIntent != null
+                && Intent.ACTION_MAIN.equals(launchIntent.getAction())
+                && launchIntent.getComponent() != null
+                && launchIntent.getCategories() != null
+                && launchIntent.getCategories().size() == 1
+                && launchIntent.hasCategory(Intent.CATEGORY_LAUNCHER)
+                && launchIntent.getExtras() == null
+                && TextUtils.isEmpty(launchIntent.getDataString());
+    }
+
+    public static float dpiFromPx(int size, DisplayMetrics metrics){
+        float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
+        return (size / densityRatio);
+    }
+    public static int pxFromDp(float size, DisplayMetrics metrics) {
+        return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                size, metrics));
+    }
+    public static int pxFromSp(float size, DisplayMetrics metrics) {
+        return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
+                size, metrics));
+    }
 }
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 5ee1f26..e8cc486 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -25,7 +25,6 @@
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
 import android.os.Handler;
-import android.os.Process;
 import android.util.Log;
 import android.util.LongSparseArray;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
@@ -34,7 +33,6 @@
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.widget.WidgetCell;
-import junit.framework.Assert;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -66,17 +64,19 @@
     private final UserManagerCompat mUserManager;
     private final AppWidgetManagerCompat mManager;
     private final CacheDb mDb;
+    private final InvariantDeviceProfile mDeviceProfile;
 
     private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
     private final Handler mWorkerHandler;
 
-    public WidgetPreviewLoader(Context context, IconCache iconCache) {
+    public WidgetPreviewLoader(Context context, InvariantDeviceProfile inv, IconCache iconCache) {
         mContext = context;
         mIconCache = iconCache;
         mManager = AppWidgetManagerCompat.getInstance(context);
         mUserManager = UserManagerCompat.getInstance(context);
         mDb = new CacheDb(context);
         mWorkerHandler = new Handler(LauncherModel.getWorkerLooper());
+        mDeviceProfile = inv;
     }
 
     /**
@@ -86,8 +86,8 @@
      * @param o either {@link LauncherAppWidgetProviderInfo} or {@link ResolveInfo}
      * @return a request id which can be used to cancel the request.
      */
-    public PreviewLoadRequest getPreview(final Object o, int previewWidth, int previewHeight,
-            WidgetCell caller) {
+    public PreviewLoadRequest getPreview(final Object o, int previewWidth,
+            int previewHeight, WidgetCell caller) {
         String size = previewWidth + "x" + previewHeight;
         WidgetCacheKey key = getObjectKey(o, size);
 
@@ -329,23 +329,18 @@
         return null;
     }
 
-    private Bitmap generatePreview(Object info, Bitmap recycle, int previewWidth, int previewHeight) {
+    private Bitmap generatePreview(Launcher launcher, Object info, Bitmap recycle,
+            int previewWidth, int previewHeight) {
         if (info instanceof LauncherAppWidgetProviderInfo) {
-            return generateWidgetPreview((LauncherAppWidgetProviderInfo) info, previewWidth, recycle);
+            return generateWidgetPreview(launcher, (LauncherAppWidgetProviderInfo) info,
+                    previewWidth, recycle, null);
         } else {
-            return generateShortcutPreview(
+            return generateShortcutPreview(launcher,
                     (ResolveInfo) info, previewWidth, previewHeight, recycle);
         }
     }
 
-    public Bitmap generateWidgetPreview(LauncherAppWidgetProviderInfo info,
-            int previewWidth, Bitmap preview) {
-        int maxWidth = Math.min(previewWidth, info.spanX
-                * LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile().cellWidthPx);
-        return generateWidgetPreview(info, maxWidth, preview, null);
-    }
-
-    public Bitmap generateWidgetPreview(LauncherAppWidgetProviderInfo info,
+    public Bitmap generateWidgetPreview(Launcher launcher, LauncherAppWidgetProviderInfo info,
             int maxPreviewWidth, Bitmap preview, int[] preScaledWidthOut) {
         // Load the preview image if possible
         if (maxPreviewWidth < 0) maxPreviewWidth = Integer.MAX_VALUE;
@@ -362,8 +357,8 @@
         }
 
         final boolean widgetPreviewExists = (drawable != null);
-        final int spanX = info.spanX < 1 ? 1 : info.spanX;
-        final int spanY = info.spanY < 1 ? 1 : info.spanY;
+        final int spanX = info.getSpanX(launcher) < 1 ? 1 : info.getSpanX(launcher);
+        final int spanY = info.getSpanY(launcher) < 1 ? 1 : info.getSpanY(launcher);
 
         int previewWidth;
         int previewHeight;
@@ -413,8 +408,7 @@
         } else {
             final Paint p = new Paint();
             p.setFilterBitmap(true);
-            int appIconSize = LauncherAppState.getInstance().getDynamicGrid()
-                    .getDeviceProfile().iconSizePx;
+            int appIconSize = launcher.getDeviceProfile().iconSizePx;
 
             // draw the spanX x spanY tiles
             final Rect src = new Rect(0, 0, tileBitmap.getWidth(), tileBitmap.getHeight());
@@ -455,7 +449,7 @@
     }
 
     private Bitmap generateShortcutPreview(
-            ResolveInfo info, int maxWidth, int maxHeight, Bitmap preview) {
+            Launcher launcher, ResolveInfo info, int maxWidth, int maxHeight, Bitmap preview) {
         final Canvas c = new Canvas();
         if (preview == null) {
             preview = Bitmap.createBitmap(maxWidth, maxHeight, Config.ARGB_8888);
@@ -488,8 +482,8 @@
 
         // Draw the final icon at top left corner.
         // TODO: use top right for RTL
-        int appIconSize = LauncherAppState.getInstance().getDynamicGrid()
-                .getDeviceProfile().iconSizePx;
+        int appIconSize = launcher.getDeviceProfile().iconSizePx;
+
         icon.setAlpha(255);
         icon.setColorFilter(null);
         icon.setBounds(0, 0, appIconSize, appIconSize);
@@ -626,8 +620,10 @@
                 // which would gets re-written next time.
                 mVersions = getPackageVersion(mKey.componentName.getPackageName());
 
+                Launcher launcher = (Launcher) mCaller.getContext();
+
                 // it's not in the db... we need to generate it
-                preview = generatePreview(mInfo, unusedBitmap, mPreviewWidth, mPreviewHeight);
+                preview = generatePreview(launcher, mInfo, unusedBitmap, mPreviewWidth, mPreviewHeight);
             }
             return preview;
         }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 55742f6..d2c37d2 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -307,18 +307,16 @@
         mLauncher = (Launcher) context;
         mStateTransitionAnimation = new WorkspaceStateTransitionAnimation(mLauncher, this);
         final Resources res = getResources();
-        mWorkspaceFadeInAdjacentScreens = LauncherAppState.getInstance().getDynamicGrid().
-                getDeviceProfile().shouldFadeAdjacentWorkspaceScreens();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
+        mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens();
         mFadeInAdjacentScreens = false;
         mWallpaperManager = WallpaperManager.getInstance(context);
 
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
         TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.Workspace, defStyle, 0);
         mSpringLoadedShrinkFactor =
             res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
-        mOverviewModeShrinkFactor = grid.getOverviewModeScale();
+        mOverviewModeShrinkFactor = grid.getOverviewModeScale(mIsRtl);
         mOriginalDefaultPage = mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1);
         a.recycle();
 
@@ -422,7 +420,7 @@
     protected void initWorkspace() {
         mCurrentPage = mDefaultPage;
         LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         mIconCache = app.getIconCache();
         setWillNotDraw(false);
         setClipChildren(false);
@@ -1068,7 +1066,7 @@
         case MotionEvent.ACTION_UP:
             if (mTouchState == TOUCH_STATE_REST) {
                 final CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage);
-                if (currentPage != null && !currentPage.lastDownOnOccupiedCell()) {
+                if (currentPage != null) {
                     onWallpaperTap(ev);
                 }
             }
@@ -1901,8 +1899,7 @@
 
     public void onExternalDragStartedWithItem(View v) {
         // Compose a drag bitmap with the view scaled to the icon size
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         int iconSize = grid.iconSizePx;
         int bmpWidth = v.getMeasuredWidth();
         int bmpHeight = v.getMeasuredHeight();
@@ -1983,8 +1980,7 @@
     }
 
     int getOverviewModeTranslationY() {
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         Rect overviewBar = grid.getOverviewModeButtonBarRect();
 
         int availableHeight = getViewportHeight();
@@ -2285,8 +2281,7 @@
         int dragLayerY = Math.round(mTempXY[1] - (bmpHeight - scale * bmpHeight) / 2
                         - padding.get() / 2);
 
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         Point dragVisualizeOffset = null;
         Rect dragRect = null;
         if (child instanceof BubbleTextView) {
@@ -2342,8 +2337,7 @@
     }
 
     public void beginExternalDragShared(View child, DragSource source) {
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         int iconSize = grid.iconSizePx;
 
         // Notify launcher of drag start
@@ -2852,34 +2846,35 @@
      * widthGap/heightGap (right, bottom) */
     static Rect getCellLayoutMetrics(Launcher launcher, int orientation) {
         LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        InvariantDeviceProfile inv = app.getInvariantDeviceProfile();
 
         Display display = launcher.getWindowManager().getDefaultDisplay();
         Point smallestSize = new Point();
         Point largestSize = new Point();
         display.getCurrentSizeRange(smallestSize, largestSize);
-        int countX = (int) grid.numColumns;
-        int countY = (int) grid.numRows;
+        int countX = (int) inv.numColumns;
+        int countY = (int) inv.numRows;
+        boolean isLayoutRtl = Utilities.isRtl(launcher.getResources());
         if (orientation == CellLayout.LANDSCAPE) {
             if (mLandscapeCellLayoutMetrics == null) {
-                Rect padding = grid.getWorkspacePadding(CellLayout.LANDSCAPE);
+                Rect padding = inv.landscapeProfile.getWorkspacePadding(isLayoutRtl);
                 int width = largestSize.x - padding.left - padding.right;
                 int height = smallestSize.y - padding.top - padding.bottom;
                 mLandscapeCellLayoutMetrics = new Rect();
                 mLandscapeCellLayoutMetrics.set(
-                        grid.calculateCellWidth(width, countX),
-                        grid.calculateCellHeight(height, countY), 0, 0);
+                        DeviceProfile.calculateCellWidth(width, countX),
+                        DeviceProfile.calculateCellHeight(height, countY), 0, 0);
             }
             return mLandscapeCellLayoutMetrics;
         } else if (orientation == CellLayout.PORTRAIT) {
             if (mPortraitCellLayoutMetrics == null) {
-                Rect padding = grid.getWorkspacePadding(CellLayout.PORTRAIT);
+                Rect padding = inv.portraitProfile.getWorkspacePadding(isLayoutRtl);
                 int width = smallestSize.x - padding.left - padding.right;
                 int height = largestSize.y - padding.top - padding.bottom;
                 mPortraitCellLayoutMetrics = new Rect();
                 mPortraitCellLayoutMetrics.set(
-                        grid.calculateCellWidth(width, countX),
-                        grid.calculateCellHeight(height, countY), 0, 0);
+                        DeviceProfile.calculateCellWidth(width, countX),
+                        DeviceProfile.calculateCellHeight(height, countY), 0, 0);
             }
             return mPortraitCellLayoutMetrics;
         }
@@ -3022,8 +3017,7 @@
        mTempPt[1] = y;
        mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempPt, true);
 
-       LauncherAppState app = LauncherAppState.getInstance();
-       DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+       DeviceProfile grid = mLauncher.getDeviceProfile();
        r = grid.getHotseatRect();
        if (r.contains(mTempPt[0], mTempPt[1])) {
            return true;
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 61a64e3..340066d 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -161,16 +161,15 @@
         mLauncher = launcher;
         mWorkspace = workspace;
 
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         Resources res = launcher.getResources();
-        mAllAppsTransitionTime = res.getInteger(R.integer.config_workspaceUnshrinkTime);
+        mAllAppsTransitionTime = res.getInteger(R.integer.config_allAppsTransitionTime);
         mOverviewTransitionTime = res.getInteger(R.integer.config_overviewTransitionTime);
-        mOverlayTransitionTime = res.getInteger(R.integer.config_appsCustomizeWorkspaceShrinkTime);
+        mOverlayTransitionTime = res.getInteger(R.integer.config_overlayTransitionTime);
         mSpringLoadedShrinkFactor =
                 res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100f;
         mWorkspaceScrimAlpha = res.getInteger(R.integer.config_workspaceScrimAlpha) / 100f;
-        mOverviewModeShrinkFactor = grid.getOverviewModeScale();
+        mOverviewModeShrinkFactor = grid.getOverviewModeScale(Utilities.isRtl(res));
         mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens();
     }
 
diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
similarity index 88%
rename from src/com/android/launcher3/AppsContainerView.java
rename to src/com/android/launcher3/allapps/AllAppsContainerView.java
index 5ccb6c6..60f9ab3 100644
--- a/src/com/android/launcher3/AppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3;
+package com.android.launcher3.allapps;
 
 import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
@@ -22,6 +22,7 @@
 import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.InsetDrawable;
 import android.os.Build;
@@ -40,7 +41,24 @@
 import android.view.inputmethod.InputMethodManager;
 import android.widget.FrameLayout;
 import android.widget.TextView;
-
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.BaseContainerView;
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.CheckLongPressHelper;
+import com.android.launcher3.DeleteDropTarget;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.Folder;
+import com.android.launcher3.Insettable;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherTransitionable;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.Workspace;
 import com.android.launcher3.util.Thunk;
 
 import java.util.List;
@@ -52,6 +70,7 @@
  */
 interface HeaderElevationController {
     void onScroll(int scrollY);
+    void updateBackgroundPadding(Drawable bg);
     void disable();
 }
 
@@ -62,8 +81,8 @@
 final class HeaderElevationControllerV16 implements HeaderElevationController {
 
     private final View mShadow;
-
     private final float mScrollToElevation;
+    private final Rect mTmpRect = new Rect();
 
     public HeaderElevationControllerV16(View header) {
         Resources res = header.getContext().getResources();
@@ -90,6 +109,15 @@
     }
 
     @Override
+    public void updateBackgroundPadding(Drawable bg) {
+        bg.getPadding(mTmpRect);
+        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mShadow.getLayoutParams();
+        lp.leftMargin = mTmpRect.left;
+        lp.rightMargin = mTmpRect.right;
+        mShadow.requestLayout();
+    }
+
+    @Override
     public void disable() {
         ViewGroup parent = (ViewGroup) mShadow.getParent();
         if (parent != null) {
@@ -128,15 +156,20 @@
     }
 
     @Override
+    public void updateBackgroundPadding(Drawable bg) {
+        // Do nothing, the background padding on the header view is already applied
+    }
+
+    @Override
     public void disable() { }
 }
 
 /**
  * The all apps view container.
  */
-public class AppsContainerView extends BaseContainerView implements DragSource, Insettable,
+public class AllAppsContainerView extends BaseContainerView implements DragSource, Insettable,
         TextWatcher, TextView.OnEditorActionListener, LauncherTransitionable,
-        AlphabeticalAppsList.FilterChangedCallback, AppsGridAdapter.PredictionBarSpacerCallbacks,
+        AlphabeticalAppsList.AdapterChangedCallback, AllAppsGridAdapter.PredictionBarSpacerCallbacks,
         View.OnTouchListener, View.OnClickListener, View.OnLongClickListener,
         ViewTreeObserver.OnPreDrawListener {
 
@@ -155,18 +188,18 @@
     @Thunk Launcher mLauncher;
     @Thunk AlphabeticalAppsList mApps;
     private LayoutInflater mLayoutInflater;
-    private AppsGridAdapter mAdapter;
+    private AllAppsGridAdapter mAdapter;
     private RecyclerView.LayoutManager mLayoutManager;
     private RecyclerView.ItemDecoration mItemDecoration;
 
     private FrameLayout mContentView;
-    @Thunk AppsContainerRecyclerView mAppsRecyclerView;
+    @Thunk AllAppsRecyclerView mAppsRecyclerView;
     private ViewGroup mPredictionBarView;
     private View mHeaderView;
     private View mSearchBarContainerView;
     private View mSearchButtonView;
     private View mDismissSearchButtonView;
-    private AppsContainerSearchEditTextView mSearchBarEditView;
+    private AllAppsSearchEditView mSearchBarEditView;
 
     private HeaderElevationController mElevationController;
 
@@ -183,40 +216,47 @@
     private int mContainerInset;
     private int mPredictionBarHeight;
     private int mLastRecyclerViewScrollPos = -1;
+    private boolean mFocusPredictionBarOnFirstBind;
 
     private CheckLongPressHelper mPredictionIconCheckForLongPress;
     private View mPredictionIconUnderTouch;
 
-    public AppsContainerView(Context context) {
+    public AllAppsContainerView(Context context) {
         this(context, null);
     }
 
-    public AppsContainerView(Context context, AttributeSet attrs) {
+    public AllAppsContainerView(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public AppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public AllAppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
         Resources res = context.getResources();
 
-        mContainerInset = context.getResources().getDimensionPixelSize(
-                R.dimen.apps_container_inset);
-        mPredictionBarHeight = grid.allAppsCellHeightPx +
-                2 * res.getDimensionPixelSize(R.dimen.apps_prediction_icon_top_bottom_padding);
         mLauncher = (Launcher) context;
+        DeviceProfile grid = mLauncher.getDeviceProfile();
+
+        mContainerInset = context.getResources().getDimensionPixelSize(
+                R.dimen.all_apps_container_inset);
+        mPredictionBarHeight = grid.allAppsIconSizePx + grid.iconDrawablePaddingOriginalPx +
+                grid.allAppsIconTextSizePx +
+                2 * res.getDimensionPixelSize(R.dimen.all_apps_prediction_icon_top_bottom_padding);
+
         mLayoutInflater = LayoutInflater.from(context);
-        mNumAppsPerRow = grid.appsViewNumCols;
-        mNumPredictedAppsPerRow = grid.appsViewNumPredictiveCols;
-        mApps = new AlphabeticalAppsList(context, this, mNumAppsPerRow, mNumPredictedAppsPerRow);
-        mAdapter = new AppsGridAdapter(context, mApps, mNumAppsPerRow, this, this, mLauncher, this);
-        mAdapter.setEmptySearchText(res.getString(R.string.loading_apps_message));
+
+        mNumAppsPerRow = grid.allAppsNumCols;
+        mNumPredictedAppsPerRow = grid.allAppsNumPredictiveCols;
+        mApps = new AlphabeticalAppsList(context, mNumAppsPerRow, mNumPredictedAppsPerRow);
+        mApps.setAdapterChangedCallback(this);
+        mAdapter = new AllAppsGridAdapter(context, mApps, mNumAppsPerRow, this, this, mLauncher, this);
+        mAdapter.setEmptySearchText(res.getString(R.string.all_apps_loading_message));
         mAdapter.setNumAppsPerRow(mNumAppsPerRow);
         mAdapter.setPredictionRowHeight(mPredictionBarHeight);
         mLayoutManager = mAdapter.getLayoutManager();
         mItemDecoration = mAdapter.getItemDecoration();
         mContentMarginStart = mAdapter.getContentMarginStart();
+
         mApps.setAdapter(mAdapter);
     }
 
@@ -298,7 +338,17 @@
             @Override
             public void onFocusChange(View v, boolean hasFocus) {
                 if (v == mContentView && hasFocus) {
-                    mAppsRecyclerView.requestFocus();
+                    if (!mApps.getPredictedApps().isEmpty()) {
+                        // If the prediction bar is going to be bound, then defer focusing until
+                        // it is first bound
+                        if (mPredictionBarView.getChildCount() == 0) {
+                            mFocusPredictionBarOnFirstBind = true;
+                        } else {
+                            mPredictionBarView.requestFocus();
+                        }
+                    } else {
+                        mAppsRecyclerView.requestFocus();
+                    }
                 }
             }
         });
@@ -324,13 +374,13 @@
         mSearchBarContainerView = findViewById(R.id.app_search_container);
         mDismissSearchButtonView = mSearchBarContainerView.findViewById(R.id.dismiss_search_button);
         mDismissSearchButtonView.setOnClickListener(this);
-        mSearchBarEditView = (AppsContainerSearchEditTextView) findViewById(R.id.app_search_box);
+        mSearchBarEditView = (AllAppsSearchEditView) findViewById(R.id.apps_search_box);
         if (mSearchBarEditView != null) {
             mSearchBarEditView.addTextChangedListener(this);
             mSearchBarEditView.setOnEditorActionListener(this);
             if (DISMISS_SEARCH_ON_BACK) {
                 mSearchBarEditView.setOnBackKeyListener(
-                        new AppsContainerSearchEditTextView.OnBackKeyListener() {
+                        new AllAppsSearchEditView.OnBackKeyListener() {
                             @Override
                             public void onBackKey() {
                                 // Only hide the search field if there is no query, or if there
@@ -344,7 +394,7 @@
                         });
             }
         }
-        mAppsRecyclerView = (AppsContainerRecyclerView) findViewById(R.id.apps_list_view);
+        mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.apps_list_view);
         mAppsRecyclerView.setApps(mApps);
         mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow);
         mAppsRecyclerView.setPredictionBarHeight(mPredictionBarHeight);
@@ -372,7 +422,7 @@
             } else {
                 // Otherwise, inflate a new icon
                 icon = (BubbleTextView) mLayoutInflater.inflate(
-                        R.layout.apps_prediction_bar_icon_view, mPredictionBarView, false);
+                        R.layout.all_apps_prediction_bar_icon, mPredictionBarView, false);
                 icon.setFocusable(true);
                 mPredictionBarView.addView(icon);
             }
@@ -387,16 +437,21 @@
                 icon.setVisibility(View.INVISIBLE);
             }
         }
+
+        if (mFocusPredictionBarOnFirstBind) {
+            mFocusPredictionBarOnFirstBind = false;
+            mPredictionBarView.requestFocus();
+        }
     }
 
     @Override
     protected void onFixedBoundsUpdated() {
         // Update the number of items in the grid
         LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         if (grid.updateAppsViewNumCols(getContext().getResources(), mFixedBounds.width())) {
-            mNumAppsPerRow = grid.appsViewNumCols;
-            mNumPredictedAppsPerRow = grid.appsViewNumPredictiveCols;
+            mNumAppsPerRow = grid.allAppsNumCols;
+            mNumPredictedAppsPerRow = grid.allAppsNumPredictiveCols;
             mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow);
             mAdapter.setNumAppsPerRow(mNumAppsPerRow);
             mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow);
@@ -431,8 +486,8 @@
         }
 
         // Update the apps recycler view, inset it by the container inset as well
-        DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
-        int startMargin = grid.isPhone() ? mContentMarginStart : 0;
+        DeviceProfile grid = mLauncher.getDeviceProfile();
+        int startMargin = grid.isPhone ? mContentMarginStart : 0;
         int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset;
         if (isRtl) {
             mAppsRecyclerView.setPadding(inset + mAppsRecyclerView.getScrollbarWidth(), inset,
@@ -472,6 +527,7 @@
         mContentView.setBackground(background);
         mAppsRecyclerView.updateBackgroundPadding(background);
         mAdapter.updateBackgroundPadding(background);
+        mElevationController.updateBackgroundPadding(background);
         getRevealView().setBackground(background.getConstantState().newDrawable());
     }
 
@@ -548,8 +604,7 @@
 
     @Override
     public float getIntrinsicIconScaleFactor() {
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         return (float) grid.allAppsIconSizePx / grid.iconSizePx;
     }
 
@@ -612,7 +667,7 @@
         if (queryText.isEmpty()) {
             mApps.setFilter(null);
         } else {
-            String formatStr = getResources().getString(R.string.apps_view_no_search_results);
+            String formatStr = getResources().getString(R.string.all_apps_no_search_results);
             mAdapter.setEmptySearchText(String.format(formatStr, queryText));
 
             // Do an intersection of the words in the query and each title, and filter out all the
@@ -659,7 +714,7 @@
             List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
             for (int i = 0; i < items.size(); i++) {
                 AlphabeticalAppsList.AdapterItem item = items.get(i);
-                if (item.viewType == AppsGridAdapter.ICON_VIEW_TYPE) {
+                if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE) {
                     mAppsRecyclerView.getChildAt(i).performClick();
                     getInputMethodManager().hideSoftInputFromWindow(getWindowToken(), 0);
                     return true;
@@ -670,7 +725,7 @@
     }
 
     @Override
-    public void onFilterChanged() {
+    public void onAdapterItemsChanged() {
         updatePredictionBarVisibility();
     }
 
@@ -743,7 +798,7 @@
      */
     private boolean handleTouchEvent(MotionEvent ev) {
         LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         int x = (int) ev.getX();
         int y = (int) ev.getY();
 
@@ -866,7 +921,7 @@
      */
     private void showSearchField() {
         // Show the search bar and focus the search
-        final int translationX = DynamicGrid.pxFromDp(SEARCH_TRANSLATION_X_DP,
+        final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP,
                 getContext().getResources().getDisplayMetrics());
         mSearchBarContainerView.setVisibility(View.VISIBLE);
         mSearchBarContainerView.setAlpha(0f);
@@ -896,7 +951,7 @@
      */
     private void hideSearchField(boolean animated, final boolean returnFocusToRecyclerView) {
         final boolean resetTextField = mSearchBarEditView.getText().toString().length() > 0;
-        final int translationX = DynamicGrid.pxFromDp(SEARCH_TRANSLATION_X_DP,
+        final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP,
                 getContext().getResources().getDisplayMetrics());
         if (animated) {
             // Hide the search bar and focus the recycler view
diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
similarity index 89%
rename from src/com/android/launcher3/AppsGridAdapter.java
rename to src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 580930c..e010270 100644
--- a/src/com/android/launcher3/AppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3;
+package com.android.launcher3.allapps;
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -29,6 +29,12 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.util.Thunk;
 
 import java.util.HashMap;
@@ -38,7 +44,7 @@
 /**
  * The grid view adapter of all the apps.
  */
-class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
+class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHolder> {
 
     public static final String TAG = "AppsGridAdapter";
     private static final boolean DEBUG = false;
@@ -48,7 +54,7 @@
     // A normal icon
     public static final int ICON_VIEW_TYPE = 1;
     // The message shown when there are no filtered results
-    public static final int EMPTY_VIEW_TYPE = 2;
+    public static final int EMPTY_SEARCH_VIEW_TYPE = 2;
     // The spacer used for the prediction bar
     public static final int PREDICTION_BAR_SPACER_TYPE = 3;
 
@@ -88,7 +94,7 @@
                 return mAppsPerRow;
             }
 
-            if (mApps.getAdapterItems().get(position).viewType != AppsGridAdapter.ICON_VIEW_TYPE) {
+            if (mApps.getAdapterItems().get(position).viewType != AllAppsGridAdapter.ICON_VIEW_TYPE) {
                 // Both the section breaks and predictive bar span the full width
                 return mAppsPerRow;
             } else {
@@ -106,6 +112,11 @@
 
         private HashMap<String, PointF> mCachedSectionBounds = new HashMap<>();
         private Rect mTmpBounds = new Rect();
+        private Launcher mLauncher;
+
+        public GridItemDecoration(Context context) {
+            mLauncher = (Launcher) context;
+        }
 
         @Override
         public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
@@ -113,7 +124,7 @@
                 return;
             }
 
-            DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
+            DeviceProfile grid = mLauncher.getDeviceProfile();
             List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
             boolean hasDrawnPredictedAppsDivider = false;
             int childCount = parent.getChildCount();
@@ -134,7 +145,7 @@
                             mPredictedAppsDividerPaint);
                     hasDrawnPredictedAppsDivider = true;
 
-                } else if (grid.isPhone() && shouldDrawItemSection(holder, i, items)) {
+                } else if (grid.isPhone && shouldDrawItemSection(holder, i, items)) {
                     // At this point, we only draw sections for each section break;
                     int viewTopOffset = (2 * child.getPaddingTop());
                     int pos = holder.getPosition();
@@ -250,7 +261,7 @@
         private boolean shouldDrawItemDivider(ViewHolder holder,
                 List<AlphabeticalAppsList.AdapterItem> items) {
             int pos = holder.getPosition();
-            return items.get(pos).viewType == AppsGridAdapter.PREDICTION_BAR_SPACER_TYPE;
+            return items.get(pos).viewType == AllAppsGridAdapter.PREDICTION_BAR_SPACER_TYPE;
         }
 
         /**
@@ -262,12 +273,12 @@
             AlphabeticalAppsList.AdapterItem item = items.get(pos);
 
             // Ensure it's an icon
-            if (item.viewType != AppsGridAdapter.ICON_VIEW_TYPE) {
+            if (item.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE) {
                 return false;
             }
             // Draw the section header for the first item in each section
             return (childIndex == 0) ||
-                    (items.get(pos - 1).viewType == AppsGridAdapter.SECTION_BREAK_VIEW_TYPE);
+                    (items.get(pos - 1).viewType == AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE);
         }
     }
 
@@ -294,8 +305,7 @@
     @Thunk Paint mSectionTextPaint;
     @Thunk Paint mPredictedAppsDividerPaint;
 
-
-    public AppsGridAdapter(Context context, AlphabeticalAppsList apps, int appsPerRow,
+    public AllAppsGridAdapter(Context context, AlphabeticalAppsList apps, int appsPerRow,
             PredictionBarSpacerCallbacks pbCb, View.OnTouchListener touchListener,
             View.OnClickListener iconClickListener, View.OnLongClickListener iconLongClickListener) {
         Resources res = context.getResources();
@@ -307,23 +317,23 @@
         mGridLayoutMgr = new GridLayoutManager(context, appsPerRow, GridLayoutManager.VERTICAL,
                 false);
         mGridLayoutMgr.setSpanSizeLookup(mGridSizer);
-        mItemDecoration = new GridItemDecoration();
+        mItemDecoration = new GridItemDecoration(context);
         mLayoutInflater = LayoutInflater.from(context);
         mTouchListener = touchListener;
         mIconClickListener = iconClickListener;
         mIconLongClickListener = iconLongClickListener;
-        mStartMargin = res.getDimensionPixelSize(R.dimen.apps_grid_view_start_margin);
-        mSectionHeaderOffset = res.getDimensionPixelSize(R.dimen.apps_grid_section_y_offset);
-        mPaddingStart = res.getDimensionPixelSize(R.dimen.apps_container_inset);
+        mStartMargin = res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin);
+        mSectionHeaderOffset = res.getDimensionPixelSize(R.dimen.all_apps_grid_section_y_offset);
+        mPaddingStart = res.getDimensionPixelSize(R.dimen.all_apps_container_inset);
 
         mSectionTextPaint = new Paint();
         mSectionTextPaint.setTextSize(res.getDimensionPixelSize(
-                R.dimen.apps_view_section_text_size));
-        mSectionTextPaint.setColor(res.getColor(R.color.apps_view_section_text_color));
+                R.dimen.all_apps_grid_section_text_size));
+        mSectionTextPaint.setColor(res.getColor(R.color.all_apps_grid_section_text_color));
         mSectionTextPaint.setAntiAlias(true);
 
         mPredictedAppsDividerPaint = new Paint();
-        mPredictedAppsDividerPaint.setStrokeWidth(DynamicGrid.pxFromDp(1f, res.getDisplayMetrics()));
+        mPredictedAppsDividerPaint.setStrokeWidth(Utilities.pxFromDp(1f, res.getDisplayMetrics()));
         mPredictedAppsDividerPaint.setColor(0x1E000000);
         mPredictedAppsDividerPaint.setAntiAlias(true);
     }
@@ -390,8 +400,8 @@
     @Override
     public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
         switch (viewType) {
-            case EMPTY_VIEW_TYPE:
-                return new ViewHolder(mLayoutInflater.inflate(R.layout.apps_empty_view, parent,
+            case EMPTY_SEARCH_VIEW_TYPE:
+                return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search, parent,
                         false));
             case SECTION_BREAK_VIEW_TYPE:
                 return new ViewHolder(new View(parent.getContext()));
@@ -404,7 +414,7 @@
                 return new ViewHolder(v);
             case ICON_VIEW_TYPE:
                 BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
-                        R.layout.apps_grid_icon_view, parent, false);
+                        R.layout.all_apps_icon, parent, false);
                 icon.setOnTouchListener(mTouchListener);
                 icon.setOnClickListener(mIconClickListener);
                 icon.setOnLongClickListener(mIconLongClickListener);
@@ -433,7 +443,7 @@
                     }
                 });
                 break;
-            case EMPTY_VIEW_TYPE:
+            case EMPTY_SEARCH_VIEW_TYPE:
                 TextView emptyViewText = (TextView) holder.mContent.findViewById(R.id.empty_text);
                 emptyViewText.setText(mEmptySearchText);
                 break;
@@ -452,7 +462,7 @@
     @Override
     public int getItemViewType(int position) {
         if (mApps.hasNoFilteredResults()) {
-            return EMPTY_VIEW_TYPE;
+            return EMPTY_SEARCH_VIEW_TYPE;
         }
 
         AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position);
diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
similarity index 91%
rename from src/com/android/launcher3/AppsContainerRecyclerView.java
rename to src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 31942b3..e95fa32 100644
--- a/src/com/android/launcher3/AppsContainerRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3;
+package com.android.launcher3.allapps;
 
 import android.animation.ObjectAnimator;
 import android.content.Context;
@@ -29,6 +29,11 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
+import com.android.launcher3.BaseRecyclerView;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 
 import java.util.List;
 
@@ -36,7 +41,7 @@
  * A RecyclerView with custom fastscroll support.  This is the main container for the all apps
  * icons.
  */
-public class AppsContainerRecyclerView extends BaseContainerRecyclerView {
+public class AllAppsRecyclerView extends BaseRecyclerView {
 
     /**
      * The current scroll state of the recycler view.  We use this in updateVerticalScrollbarBounds()
@@ -80,37 +85,40 @@
     private Rect mBackgroundPadding = new Rect();
     private ScrollPositionState mScrollPosState = new ScrollPositionState();
 
-    public AppsContainerRecyclerView(Context context) {
+    private Launcher mLauncher;
+
+    public AllAppsRecyclerView(Context context) {
         this(context, null);
     }
 
-    public AppsContainerRecyclerView(Context context, AttributeSet attrs) {
+    public AllAppsRecyclerView(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public AppsContainerRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public AllAppsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
         this(context, attrs, defStyleAttr, 0);
     }
 
-    public AppsContainerRecyclerView(Context context, AttributeSet attrs, int defStyleAttr,
+    public AllAppsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr);
 
+        mLauncher = (Launcher) context;
         Resources res = context.getResources();
-        int fastScrollerSize = res.getDimensionPixelSize(R.dimen.apps_view_fast_scroll_popup_size);
-        mScrollbar = res.getDrawable(R.drawable.apps_list_scrollbar_thumb);
-        mFastScrollerBg = res.getDrawable(R.drawable.apps_list_fastscroll_bg);
+        int fastScrollerSize = res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_popup_size);
+        mScrollbar = res.getDrawable(R.drawable.all_apps_scrollbar_thumb);
+        mFastScrollerBg = res.getDrawable(R.drawable.all_apps_fastscroll_bg);
         mFastScrollerBg.setBounds(0, 0, fastScrollerSize, fastScrollerSize);
         mFastScrollTextPaint = new Paint();
         mFastScrollTextPaint.setColor(Color.WHITE);
         mFastScrollTextPaint.setAntiAlias(true);
         mFastScrollTextPaint.setTextSize(res.getDimensionPixelSize(
-                R.dimen.apps_view_fast_scroll_text_size));
-        mScrollbarWidth = res.getDimensionPixelSize(R.dimen.apps_view_fast_scroll_bar_width);
+                R.dimen.all_apps_fast_scroll_text_size));
+        mScrollbarWidth = res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_bar_width);
         mScrollbarMinHeight =
-                res.getDimensionPixelSize(R.dimen.apps_view_fast_scroll_bar_min_height);
+                res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_bar_min_height);
         mScrollbarInset =
-                res.getDimensionPixelSize(R.dimen.apps_view_fast_scroll_scrubber_touch_inset);
+                res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_scrubber_touch_inset);
         setFastScrollerAlpha(getFastScrollerAlpha());
         setOverScrollMode(View.OVER_SCROLL_NEVER);
     }
@@ -129,13 +137,13 @@
         mNumAppsPerRow = numAppsPerRow;
         mNumPredictedAppsPerRow = numPredictedAppsPerRow;
 
-        DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         RecyclerView.RecycledViewPool pool = getRecycledViewPool();
         int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx);
-        pool.setMaxRecycledViews(AppsGridAdapter.PREDICTION_BAR_SPACER_TYPE, 1);
-        pool.setMaxRecycledViews(AppsGridAdapter.EMPTY_VIEW_TYPE, 1);
-        pool.setMaxRecycledViews(AppsGridAdapter.ICON_VIEW_TYPE, approxRows * mNumAppsPerRow);
-        pool.setMaxRecycledViews(AppsGridAdapter.SECTION_BREAK_VIEW_TYPE, approxRows);
+        pool.setMaxRecycledViews(AllAppsGridAdapter.PREDICTION_BAR_SPACER_TYPE, 1);
+        pool.setMaxRecycledViews(AllAppsGridAdapter.EMPTY_SEARCH_VIEW_TYPE, 1);
+        pool.setMaxRecycledViews(AllAppsGridAdapter.ICON_VIEW_TYPE, approxRows * mNumAppsPerRow);
+        pool.setMaxRecycledViews(AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE, approxRows);
     }
 
     public void updateBackgroundPadding(Drawable background) {
@@ -508,7 +516,7 @@
             int position = getChildPosition(child);
             if (position != NO_POSITION) {
                 AlphabeticalAppsList.AdapterItem item = items.get(position);
-                if (item.viewType == AppsGridAdapter.ICON_VIEW_TYPE) {
+                if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE) {
                     stateOut.rowIndex = findRowForAppIndex(item.appIndex);
                     stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child);
                     stateOut.rowHeight = child.getHeight();
diff --git a/src/com/android/launcher3/AppsRecyclerViewContainer.java b/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
similarity index 68%
rename from src/com/android/launcher3/AppsRecyclerViewContainer.java
rename to src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
index cf4beca..8a8afde 100644
--- a/src/com/android/launcher3/AppsRecyclerViewContainer.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
@@ -13,33 +13,41 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3;
+package com.android.launcher3.allapps;
 
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.util.AttributeSet;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
-
+import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.BubbleTextView.BubbleTextShadowHandler;
+import com.android.launcher3.ClickShadowView;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
 
-public class AppsRecyclerViewContainer extends FrameLayout implements BubbleTextShadowHandler {
+/**
+ * A container for RecyclerView to allow for the click shadow view to be shown behind an icon that
+ * is launching.
+ */
+public class AllAppsRecyclerViewContainerView extends FrameLayout
+        implements BubbleTextShadowHandler {
 
     private final ClickShadowView mTouchFeedbackView;
 
-    public AppsRecyclerViewContainer(Context context) {
+    public AllAppsRecyclerViewContainerView(Context context) {
         this(context, null);
     }
 
-    public AppsRecyclerViewContainer(Context context, AttributeSet attrs) {
+    public AllAppsRecyclerViewContainerView(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public AppsRecyclerViewContainer(Context context, AttributeSet attrs, int defStyleAttr) {
+    public AllAppsRecyclerViewContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        Launcher launcher = (Launcher) context;
+        DeviceProfile grid = launcher.getDeviceProfile();
 
         mTouchFeedbackView = new ClickShadowView(context);
 
diff --git a/src/com/android/launcher3/AppsContainerSearchEditTextView.java b/src/com/android/launcher3/allapps/AllAppsSearchEditView.java
similarity index 82%
rename from src/com/android/launcher3/AppsContainerSearchEditTextView.java
rename to src/com/android/launcher3/allapps/AllAppsSearchEditView.java
index c688237..b7dcd66 100644
--- a/src/com/android/launcher3/AppsContainerSearchEditTextView.java
+++ b/src/com/android/launcher3/allapps/AllAppsSearchEditView.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3;
+package com.android.launcher3.allapps;
 
 import android.content.Context;
 import android.util.AttributeSet;
@@ -24,7 +24,7 @@
 /**
  * The edit text for the search container
  */
-public class AppsContainerSearchEditTextView extends EditText {
+public class AllAppsSearchEditView extends EditText {
 
     /**
      * Implemented by listeners of the back key.
@@ -35,15 +35,15 @@
 
     private OnBackKeyListener mBackKeyListener;
 
-    public AppsContainerSearchEditTextView(Context context) {
+    public AllAppsSearchEditView(Context context) {
         this(context, null);
     }
 
-    public AppsContainerSearchEditTextView(Context context, AttributeSet attrs) {
+    public AllAppsSearchEditView(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public AppsContainerSearchEditTextView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public AllAppsSearchEditView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
     }
 
diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
similarity index 81%
rename from src/com/android/launcher3/AlphabeticalAppsList.java
rename to src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 82aaeb9..3d1503d 100644
--- a/src/com/android/launcher3/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -1,18 +1,17 @@
-package com.android.launcher3;
+package com.android.launcher3.allapps;
 
 import android.content.ComponentName;
 import android.content.Context;
 import android.support.v7.widget.RecyclerView;
 import android.util.Log;
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.compat.AlphabeticIndexCompat;
-import com.android.launcher3.compat.UserHandleCompat;
-import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.model.AppNameComparator;
 
-import java.text.Collator;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
@@ -85,7 +84,7 @@
 
         public static AdapterItem asSectionBreak(int pos, SectionInfo section) {
             AdapterItem item = new AdapterItem();
-            item.viewType = AppsGridAdapter.SECTION_BREAK_VIEW_TYPE;
+            item.viewType = AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE;
             item.position = pos;
             item.sectionInfo = section;
             section.sectionBreakItem = item;
@@ -94,7 +93,7 @@
 
         public static AdapterItem asPredictionBarSpacer(int pos) {
             AdapterItem item = new AdapterItem();
-            item.viewType = AppsGridAdapter.PREDICTION_BAR_SPACER_TYPE;
+            item.viewType = AllAppsGridAdapter.PREDICTION_BAR_SPACER_TYPE;
             item.position = pos;
             return item;
         }
@@ -102,7 +101,7 @@
         public static AdapterItem asApp(int pos, SectionInfo section, String sectionName,
                                         int sectionAppIndex, AppInfo appInfo, int appIndex) {
             AdapterItem item = new AdapterItem();
-            item.viewType = AppsGridAdapter.ICON_VIEW_TYPE;
+            item.viewType = AllAppsGridAdapter.ICON_VIEW_TYPE;
             item.position = pos;
             item.sectionInfo = section;
             item.sectionName = sectionName;
@@ -121,10 +120,10 @@
     }
 
     /**
-     * A callback to notify of changes to the filter.
+     * Callback to notify when the set of adapter items have changed.
      */
-    public interface FilterChangedCallback {
-        void onFilterChanged();
+    public interface AdapterChangedCallback {
+        void onAdapterItemsChanged();
     }
 
     /**
@@ -177,13 +176,21 @@
     private static final int MIN_ROWS_IN_MERGED_SECTION_PHONE = 3;
     private static final int MAX_NUM_MERGES_PHONE = 2;
 
-    private Context mContext;
+    private Launcher mLauncher;
+
+    // The set of apps from the system not including predictions
     private List<AppInfo> mApps = new ArrayList<>();
+    // The set of filtered apps with the current filter
     private List<AppInfo> mFilteredApps = new ArrayList<>();
-    private List<AdapterItem> mSectionedFilteredApps = new ArrayList<>();
+    // The current set of adapter items
+    private List<AdapterItem> mAdapterItems = new ArrayList<>();
+    // The set of sections for the apps with the current filter
     private List<SectionInfo> mSections = new ArrayList<>();
+    // The set of sections that we allow fast-scrolling to (includes non-merged sections)
     private List<FastScrollSectionInfo> mFastScrollerSections = new ArrayList<>();
+    // The set of predicted app component names
     private List<ComponentName> mPredictedAppComponents = new ArrayList<>();
+    // The set of predicted apps resolved from the component names and the current set of apps
     private List<AppInfo> mPredictedApps = new ArrayList<>();
     private HashMap<CharSequence, String> mCachedSectionNames = new HashMap<>();
     private RecyclerView.Adapter mAdapter;
@@ -191,26 +198,31 @@
     private AlphabeticIndexCompat mIndexer;
     private AppNameComparator mAppNameComparator;
     private MergeAlgorithm mMergeAlgorithm;
-    private FilterChangedCallback mFilterChangedCallback;
+    private AdapterChangedCallback mAdapterChangedCallback;
     private int mNumAppsPerRow;
     private int mNumPredictedAppsPerRow;
 
-    public AlphabeticalAppsList(Context context, FilterChangedCallback cb, int numAppsPerRow,
-            int numPredictedAppsPerRow) {
-        mContext = context;
+    public AlphabeticalAppsList(Context context, int numAppsPerRow, int numPredictedAppsPerRow) {
+        mLauncher = (Launcher) context;
         mIndexer = new AlphabeticIndexCompat(context);
         mAppNameComparator = new AppNameComparator(context);
-        mFilterChangedCallback = cb;
         setNumAppsPerRow(numAppsPerRow, numPredictedAppsPerRow);
     }
 
     /**
+     * Sets the apps updated callback.
+     */
+    public void setAdapterChangedCallback(AdapterChangedCallback cb) {
+        mAdapterChangedCallback = cb;
+    }
+
+    /**
      * Sets the number of apps per row.  Used only for AppsContainerView.SECTIONED_GRID_COALESCED.
      */
     public void setNumAppsPerRow(int numAppsPerRow, int numPredictedAppsPerRow) {
         // Update the merge algorithm
-        DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
-        if (grid.isPhone()) {
+        DeviceProfile grid = mLauncher.getDeviceProfile();
+        if (grid.isPhone) {
             mMergeAlgorithm = new PhoneMergeAlgorithm((int) Math.ceil(numAppsPerRow / 2f),
                     MIN_ROWS_IN_MERGED_SECTION_PHONE, MAX_NUM_MERGES_PHONE);
         } else {
@@ -248,7 +260,7 @@
      * Returns the current filtered list of applications broken down into their sections.
      */
     public List<AdapterItem> getAdapterItems() {
-        return mSectionedFilteredApps;
+        return mAdapterItems;
     }
 
     /**
@@ -278,11 +290,7 @@
     public void setFilter(Filter f) {
         if (mFilter != f) {
             mFilter = f;
-            onAppsUpdated();
-            mAdapter.notifyDataSetChanged();
-            if (mFilterChangedCallback != null){
-                mFilterChangedCallback.onFilterChanged();
-            }
+            updateAdapterItems();
         }
     }
 
@@ -294,7 +302,6 @@
         mPredictedAppComponents.clear();
         mPredictedAppComponents.addAll(apps);
         onAppsUpdated();
-        mAdapter.notifyDataSetChanged();
     }
 
     /**
@@ -311,7 +318,6 @@
         mApps.clear();
         mApps.addAll(apps);
         onAppsUpdated();
-        mAdapter.notifyDataSetChanged();
     }
 
     /**
@@ -320,10 +326,9 @@
     public void addApps(List<AppInfo> apps) {
         // We add it in place, in alphabetical order
         for (AppInfo info : apps) {
-            addApp(info);
+            mApps.add(info);
         }
         onAppsUpdated();
-        mAdapter.notifyDataSetChanged();
     }
 
     /**
@@ -335,11 +340,10 @@
             if (index != -1) {
                 mApps.set(index, info);
             } else {
-                addApp(info);
+                mApps.add(info);
             }
         }
         onAppsUpdated();
-        mAdapter.notifyDataSetChanged();
     }
 
     /**
@@ -353,7 +357,6 @@
             }
         }
         onAppsUpdated();
-        mAdapter.notifyDataSetChanged();
     }
 
     /**
@@ -373,34 +376,68 @@
     }
 
     /**
-     * Implementation to actually add an app to the alphabetic list, but does not notify.
-     */
-    private void addApp(AppInfo info) {
-        int index = Collections.binarySearch(mApps, info, mAppNameComparator.getAppInfoComparator());
-        if (index < 0) {
-            mApps.add(-(index + 1), info);
-        }
-    }
-
-    /**
      * Updates internals when the set of apps are updated.
      */
     private void onAppsUpdated() {
         // Sort the list of apps
         Collections.sort(mApps, mAppNameComparator.getAppInfoComparator());
 
-        // Prepare to update the list of sections, filtered apps, etc.
-        mFilteredApps.clear();
-        mSections.clear();
-        mSectionedFilteredApps.clear();
-        mFastScrollerSections.clear();
+        // As a special case for some languages (currently only Simplified Chinese), we may need to
+        // coalesce sections
+        Locale curLocale = mLauncher.getResources().getConfiguration().locale;
+        TreeMap<String, ArrayList<AppInfo>> sectionMap = null;
+        boolean localeRequiresSectionSorting = curLocale.equals(Locale.SIMPLIFIED_CHINESE);
+        if (localeRequiresSectionSorting) {
+            // Compute the section headers.  We use a TreeMap with the section name comparator to
+            // ensure that the sections are ordered when we iterate over it later
+            sectionMap = new TreeMap<>(mAppNameComparator.getSectionNameComparator());
+            for (AppInfo info : mApps) {
+                // Add the section to the cache
+                String sectionName = getAndUpdateCachedSectionName(info.title);
+
+                // Add it to the mapping
+                ArrayList<AppInfo> sectionApps = sectionMap.get(sectionName);
+                if (sectionApps == null) {
+                    sectionApps = new ArrayList<>();
+                    sectionMap.put(sectionName, sectionApps);
+                }
+                sectionApps.add(info);
+            }
+
+            // Add each of the section apps to the list in order
+            List<AppInfo> allApps = new ArrayList<>(mApps.size());
+            for (Map.Entry<String, ArrayList<AppInfo>> entry : sectionMap.entrySet()) {
+                allApps.addAll(entry.getValue());
+            }
+            mApps = allApps;
+        } else {
+            // Just compute the section headers for use below
+            for (AppInfo info : mApps) {
+                // Add the section to the cache
+                getAndUpdateCachedSectionName(info.title);
+            }
+        }
+
+        // Recompose the set of adapter items from the current set of apps
+        updateAdapterItems();
+    }
+
+    /**
+     * Updates the set of filtered apps with the current filter.  At this point, we expect
+     * mCachedSectionNames to have been calculated for the set of all apps in mApps.
+     */
+    private void updateAdapterItems() {
         SectionInfo lastSectionInfo = null;
         String lastSectionName = null;
         FastScrollSectionInfo lastFastScrollerSectionInfo = null;
         int position = 0;
         int appIndex = 0;
-        List<AppInfo> allApps = new ArrayList<>();
 
+        // Prepare to update the list of sections, filtered apps, etc.
+        mFilteredApps.clear();
+        mFastScrollerSections.clear();
+        mAdapterItems.clear();
+        mSections.clear();
 
         // Process the predicted app components
         mPredictedApps.clear();
@@ -421,61 +458,16 @@
             if (!mPredictedApps.isEmpty()) {
                 // Create a new spacer for the prediction bar
                 AdapterItem sectionItem = AdapterItem.asPredictionBarSpacer(position++);
-                mSectionedFilteredApps.add(sectionItem);
+                mAdapterItems.add(sectionItem);
             }
         }
 
-        // As a special case for some languages (currently only Simplified Chinese), we may need to
-        // coalesce sections
-        Locale curLocale = mContext.getResources().getConfiguration().locale;
-        TreeMap<String, ArrayList<AppInfo>> sectionMap = null;
-        boolean localeRequiresSectionSorting = curLocale.equals(Locale.SIMPLIFIED_CHINESE);
-        if (localeRequiresSectionSorting) {
-            // Compute the section headers.  We use a TreeMap with the section name comparator to
-            // ensure that the sections are ordered when we iterate over it later
-            sectionMap = new TreeMap<>(mAppNameComparator.getSectionNameComparator());
-            for (AppInfo info : mApps) {
-                // Add the section to the cache
-                String sectionName = mCachedSectionNames.get(info.title);
-                if (sectionName == null) {
-                    sectionName = mIndexer.computeSectionName(info.title);
-                    mCachedSectionNames.put(info.title, sectionName);
-                }
-
-                // Add it to the mapping
-                ArrayList<AppInfo> sectionApps = sectionMap.get(sectionName);
-                if (sectionApps == null) {
-                    sectionApps = new ArrayList<>();
-                    sectionMap.put(sectionName, sectionApps);
-                }
-                sectionApps.add(info);
-            }
-
-            // Add it to the list
-            for (Map.Entry<String, ArrayList<AppInfo>> entry : sectionMap.entrySet()) {
-                allApps.addAll(entry.getValue());
-            }
-        } else {
-            // Just compute the section headers for use below
-            for (AppInfo info : mApps) {
-                // Add the section to the cache
-                String sectionName = mCachedSectionNames.get(info.title);
-                if (sectionName == null) {
-                    sectionName = mIndexer.computeSectionName(info.title);
-                    mCachedSectionNames.put(info.title, sectionName);
-                }
-            }
-            // Add it to the list
-            allApps.addAll(mApps);
-        }
-
         // Recreate the filtered and sectioned apps (for convenience for the grid layout) from the
         // ordered set of sections
-        int numApps = allApps.size();
+        int numApps = mApps.size();
         for (int i = 0; i < numApps; i++) {
-            AppInfo info = allApps.get(i);
-            // The section name was computed above so this should be find
-            String sectionName = mCachedSectionNames.get(info.title);
+            AppInfo info = mApps.get(i);
+            String sectionName = getAndUpdateCachedSectionName(info.title);
 
             // Check if we want to retain this app
             if (mFilter != null && !mFilter.retainApp(info, sectionName)) {
@@ -494,7 +486,7 @@
                 // Create a new section item to break the flow of items in the list
                 if (!hasFilter()) {
                     AdapterItem sectionItem = AdapterItem.asSectionBreak(position++, lastSectionInfo);
-                    mSectionedFilteredApps.add(sectionItem);
+                    mAdapterItems.add(sectionItem);
                 }
             }
 
@@ -505,12 +497,21 @@
                 lastSectionInfo.firstAppItem = appItem;
                 lastFastScrollerSectionInfo.appItem = appItem;
             }
-            mSectionedFilteredApps.add(appItem);
+            mAdapterItems.add(appItem);
             mFilteredApps.add(info);
         }
 
         // Merge multiple sections together as requested by the merge strategy for this device
         mergeSections();
+
+        // Refresh the recycler view
+        if (mAdapter != null) {
+            mAdapter.notifyDataSetChanged();
+        }
+
+        if (mAdapterChangedCallback != null) {
+            mAdapterChangedCallback.onAdapterItemsChanged();
+        }
     }
 
     /**
@@ -518,7 +519,7 @@
      */
     private void mergeSections() {
         // Go through each section and try and merge some of the sections
-        if (AppsContainerView.GRID_MERGE_SECTIONS && !hasFilter()) {
+        if (AllAppsContainerView.GRID_MERGE_SECTIONS && !hasFilter()) {
             int sectionAppCount = 0;
             for (int i = 0; i < mSections.size(); i++) {
                 SectionInfo section = mSections.get(i);
@@ -531,20 +532,20 @@
                     SectionInfo nextSection = mSections.remove(i + 1);
 
                     // Remove the next section break
-                    mSectionedFilteredApps.remove(nextSection.sectionBreakItem);
-                    int pos = mSectionedFilteredApps.indexOf(section.firstAppItem);
+                    mAdapterItems.remove(nextSection.sectionBreakItem);
+                    int pos = mAdapterItems.indexOf(section.firstAppItem);
                     // Point the section for these new apps to the merged section
                     int nextPos = pos + section.numApps;
                     for (int j = nextPos; j < (nextPos + nextSection.numApps); j++) {
-                        AdapterItem item = mSectionedFilteredApps.get(j);
+                        AdapterItem item = mAdapterItems.get(j);
                         item.sectionInfo = section;
                         item.sectionAppIndex += section.numApps;
                     }
 
                     // Update the following adapter items of the removed section item
-                    pos = mSectionedFilteredApps.indexOf(nextSection.firstAppItem);
-                    for (int j = pos; j < mSectionedFilteredApps.size(); j++) {
-                        AdapterItem item = mSectionedFilteredApps.get(j);
+                    pos = mAdapterItems.indexOf(nextSection.firstAppItem);
+                    for (int j = pos; j < mAdapterItems.size(); j++) {
+                        AdapterItem item = mAdapterItems.get(j);
                         item.position--;
                     }
                     section.numApps += nextSection.numApps;
@@ -560,4 +561,17 @@
             }
         }
     }
+
+    /**
+     * Returns the cached section name for the given title, recomputing and updating the cache if
+     * the title has no cached section name.
+     */
+    private String getAndUpdateCachedSectionName(CharSequence title) {
+        String sectionName = mCachedSectionNames.get(title);
+        if (sectionName == null) {
+            sectionName = mIndexer.computeSectionName(title);
+            mCachedSectionNames.put(title, sectionName);
+        }
+        return sectionName;
+    }
 }
diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java
index a84e7df..696eabe 100644
--- a/src/com/android/launcher3/util/FocusLogic.java
+++ b/src/com/android/launcher3/util/FocusLogic.java
@@ -23,6 +23,7 @@
 
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.ShortcutAndWidgetContainer;
 
@@ -80,8 +81,8 @@
                 keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL);
     }
 
-    public static int handleKeyEvent(int keyCode, int cntX, int cntY, int [][] map,
-            int iconIdx, int pageIndex, int pageCount) {
+    public static int handleKeyEvent(int keyCode, int cntX, int cntY,
+            int [][] map, int iconIdx, int pageIndex, int pageCount, boolean isRtl) {
 
         if (DEBUG) {
             Log.v(TAG, String.format(
@@ -89,23 +90,21 @@
                     cntX, cntY, iconIdx, pageIndex, pageCount));
         }
 
-        DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid()
-                .getDeviceProfile();
         int newIndex = NOOP;
         switch (keyCode) {
             case KeyEvent.KEYCODE_DPAD_LEFT:
                 newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, -1 /*increment*/);
-                if (!profile.isLayoutRtl && newIndex == NOOP && pageIndex > 0) {
+                if (isRtl && newIndex == NOOP && pageIndex > 0) {
                     newIndex = PREVIOUS_PAGE_RIGHT_COLUMN;
-                } else if (profile.isLayoutRtl && newIndex == NOOP && pageIndex < pageCount - 1) {
+                } else if (isRtl && newIndex == NOOP && pageIndex < pageCount - 1) {
                     newIndex = NEXT_PAGE_RIGHT_COLUMN;
                 }
                 break;
             case KeyEvent.KEYCODE_DPAD_RIGHT:
                 newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, 1 /*increment*/);
-                if (!profile.isLayoutRtl && newIndex == NOOP && pageIndex < pageCount - 1) {
+                if (isRtl && newIndex == NOOP && pageIndex < pageCount - 1) {
                     newIndex = NEXT_PAGE_LEFT_COLUMN;
-                } else if (profile.isLayoutRtl && newIndex == NOOP && pageIndex > 0) {
+                } else if (isRtl && newIndex == NOOP && pageIndex > 0) {
                     newIndex = PREVIOUS_PAGE_LEFT_COLUMN;
                 }
                 break;
diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
index db16998..36cc2b1 100644
--- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
@@ -19,6 +19,7 @@
 import android.os.Bundle;
 import android.os.Parcelable;
 
+import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.PendingAddItemInfo;
@@ -39,7 +40,7 @@
     public AppWidgetHostView boundWidget;
     public Bundle bindOptions = null;
 
-    public PendingAddWidgetInfo(LauncherAppWidgetProviderInfo i, Parcelable data) {
+    public PendingAddWidgetInfo(Launcher launcher, LauncherAppWidgetProviderInfo i, Parcelable data) {
         if (i.isCustomWidget) {
             itemType = LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
         } else {
@@ -54,10 +55,10 @@
         previewImage = i.previewImage;
         icon = i.icon;
 
-        spanX = i.spanX;
-        spanY = i.spanY;
-        minSpanX = i.minSpanX;
-        minSpanY = i.minSpanY;
+        spanX = i.getSpanX(launcher);
+        spanY = i.getSpanY(launcher);
+        minSpanX = i.getMinSpanX(launcher);
+        minSpanY = i.getMinSpanY(launcher);
     }
 
     public boolean isCustomWidget() {
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index dcaf1f2..3ec1645 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -29,7 +29,9 @@
 import android.widget.TextView;
 
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.R;
@@ -72,6 +74,8 @@
     private WidgetPreviewLoader mWidgetPreviewLoader;
     private PreviewLoadRequest mActiveRequest;
 
+    private Launcher mLauncher;
+
     public WidgetCell(Context context) {
         this(context, null);
     }
@@ -84,8 +88,9 @@
         super(context, attrs, defStyle);
 
         final Resources r = context.getResources();
-        mDimensionsFormatString = r.getString(R.string.widget_dims_format);
+        mLauncher = (Launcher) context;
 
+        mDimensionsFormatString = r.getString(R.string.widget_dims_format);
         setContainerWidth();
         setWillNotDraw(false);
         setClipToPadding(false);
@@ -93,9 +98,9 @@
     }
 
     private void setContainerWidth() {
-        DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
+        DeviceProfile profile = mLauncher.getDeviceProfile();
         cellSize = (int) (profile.cellWidthPx * WIDTH_SCALE);
-        mPresetPreviewSize = (int) (profile.cellWidthPx * WIDTH_SCALE * PREVIEW_SCALE);
+        mPresetPreviewSize = (int) (cellSize * PREVIEW_SCALE);
     }
 
     @Override
@@ -130,14 +135,14 @@
      */
     public void applyFromAppWidgetProviderInfo(LauncherAppWidgetProviderInfo info,
             WidgetPreviewLoader loader) {
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
 
+        InvariantDeviceProfile profile =
+                LauncherAppState.getInstance().getInvariantDeviceProfile();
         mInfo = info;
         // TODO(hyunyoungs): setup a cache for these labels.
         mWidgetName.setText(AppWidgetManagerCompat.getInstance(getContext()).loadLabel(info));
-        int hSpan = Math.min(info.spanX, grid.numColumns);
-        int vSpan = Math.min(info.spanY, grid.numRows);
+        int hSpan = Math.min(info.getSpanX(mLauncher), profile.numColumns);
+        int vSpan = Math.min(info.getSpanY(mLauncher), profile.numRows);
         mWidgetDims.setText(String.format(mDimensionsFormatString, hSpan, vSpan));
         mWidgetPreviewLoader = loader;
     }
@@ -192,8 +197,7 @@
     public int getActualItemWidth() {
         ItemInfo info = (ItemInfo) getTag();
         int[] size = getPreviewSize();
-        int cellWidth = LauncherAppState.getInstance()
-                .getDynamicGrid().getDeviceProfile().cellWidthPx;
+        int cellWidth = mLauncher.getDeviceProfile().cellWidthPx;
 
         return Math.min(size[0], info.spanX * cellWidth);
     }
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index 5a879fa..05e842e 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -109,7 +109,7 @@
         mView.setLayoutManager(new LinearLayoutManager(getContext()) {
             @Override
             protected int getExtraLayoutSpace(State state) {
-                DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
+                DeviceProfile grid = mLauncher.getDeviceProfile();
                 return super.getExtraLayoutSpace(state)
                         + grid.availableHeightPx * PRELOAD_SCREEN_HEIGHT_MULTIPLE;
             }
@@ -230,8 +230,8 @@
             int maxWidth = Math.min((int) (icon.getWidth() * minScale), size[0]);
 
             int[] previewSizeBeforeScale = new int[1];
-            preview = getWidgetPreviewLoader().generateWidgetPreview(createWidgetInfo.info,
-                    maxWidth, null, previewSizeBeforeScale);
+            preview = getWidgetPreviewLoader().generateWidgetPreview(mLauncher,
+                    createWidgetInfo.info, maxWidth, null, previewSizeBeforeScale);
 
             if (previewSizeBeforeScale[0] < icon.getWidth()) {
                 // The icon has extra padding around it.
diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java
index 918ec1b..7439a44 100644
--- a/src/com/android/launcher3/widget/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java
@@ -32,11 +32,12 @@
 
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.DynamicGrid;
+import com.android.launcher3.IconCache;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.WidgetPreviewLoader;
 import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.model.WidgetsModel;
@@ -56,7 +57,6 @@
     private static final String TAG = "WidgetsListAdapter";
     private static final boolean DEBUG = true;
 
-    private Context mContext;
     private Launcher mLauncher;
     private LayoutInflater mLayoutInflater;
 
@@ -74,7 +74,6 @@
             View.OnLongClickListener iconLongClickListener,
             Launcher launcher) {
         mLayoutInflater = LayoutInflater.from(context);
-        mContext = context;
 
         mIconClickListener = iconClickListener;
         mIconLongClickListener = iconLongClickListener;
@@ -141,7 +140,7 @@
             WidgetCell widget = (WidgetCell) row.getChildAt(i);
             if (infoList.get(i) instanceof LauncherAppWidgetProviderInfo) {
                 LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo) infoList.get(i);
-                PendingAddWidgetInfo pawi = new PendingAddWidgetInfo(info, null);
+                PendingAddWidgetInfo pawi = new PendingAddWidgetInfo(mLauncher, info, null);
                 widget.setTag(pawi);
                 widget.applyFromAppWidgetProviderInfo(info, mWidgetPreviewLoader);
             } else if (infoList.get(i) instanceof ResolveInfo) {
@@ -206,10 +205,10 @@
     }
 
     private void setContainerHeight() {
-        Resources r = mContext.getResources();
-        DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
+        Resources r = mLauncher.getResources();
+        DeviceProfile profile = mLauncher.getDeviceProfile();
         if (profile.isLargeTablet || profile.isTablet) {
-            mIndent = DynamicGrid.pxFromDp(PRESET_INDENT_SIZE_TABLET, r.getDisplayMetrics());
+            mIndent = Utilities.pxFromDp(PRESET_INDENT_SIZE_TABLET, r.getDisplayMetrics());
         }
     }
 }
diff --git a/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
similarity index 67%
rename from src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java
rename to src/com/android/launcher3/widget/WidgetsRecyclerView.java
index 6f15324..31ef5d6 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java
+++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
@@ -18,22 +18,22 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
-import com.android.launcher3.BaseContainerRecyclerView;
+import com.android.launcher3.BaseRecyclerView;
 
 /**
- * The widgets recycler view container.
+ * The widgets recycler view.
  */
-public class WidgetsContainerRecyclerView extends BaseContainerRecyclerView {
+public class WidgetsRecyclerView extends BaseRecyclerView {
 
-    public WidgetsContainerRecyclerView(Context context) {
+    public WidgetsRecyclerView(Context context) {
         this(context, null);
     }
 
-    public WidgetsContainerRecyclerView(Context context, AttributeSet attrs) {
+    public WidgetsRecyclerView(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public WidgetsContainerRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public WidgetsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
     }
 
diff --git a/util/com/android/launcher3/DecoderRing.java b/util/com/android/launcher3/DecoderRing.java
index 86431d9..0732fe8 100644
--- a/util/com/android/launcher3/DecoderRing.java
+++ b/util/com/android/launcher3/DecoderRing.java
@@ -220,9 +220,6 @@
                     if (widget.icon != null) {
                         writeImageData(widget.icon.data, prefix + "_icon.png");
                     }
-                    if (widget.preview != null) {
-                        writeImageData(widget.preview.data, prefix + "_preview.png");
-                    }
                 }
             }
         }