am 9b5e37a8: (-s ours) am bb46697b: am e7d4f0f9: am 41f61f09: (-s ours) Import revised translations.  DO NOT MERGE

* commit '9b5e37a8881c3f352b27b6118773997491eed86b':
  Import revised translations.  DO NOT MERGE
diff --git a/proguard.flags b/proguard.flags
index 19c8fd5..94dd260 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -20,7 +20,7 @@
   public void setHoverAlpha(float);
 }
 
--keep class com.android.launcher2.CellLayout$LayoutParams {
+-keep class com.android.launcher2.DragLayer$LayoutParams {
   public void setWidth(int);
   public int getWidth();
   public void setHeight(int);
diff --git a/res/drawable-xlarge-mdpi/focused_bg.9.png b/res/drawable-xlarge-mdpi/focused_bg.9.png
new file mode 100644
index 0000000..1b0d3fa
--- /dev/null
+++ b/res/drawable-xlarge-mdpi/focused_bg.9.png
Binary files differ
diff --git a/res/drawable-xlarge/button_bg.xml b/res/drawable-xlarge/button_bg.xml
index 9e6e1ff..2cb7926 100644
--- a/res/drawable-xlarge/button_bg.xml
+++ b/res/drawable-xlarge/button_bg.xml
@@ -15,6 +15,7 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_focused="true" android:drawable="@drawable/focused_bg" />
     <item android:state_pressed="true" android:drawable="@drawable/home_press" />
     <item android:drawable="@android:color/transparent" />
 </selector>
diff --git a/res/drawable-xlarge/focusable_view_bg.xml b/res/drawable-xlarge/focusable_view_bg.xml
new file mode 100644
index 0000000..fb36bfc
--- /dev/null
+++ b/res/drawable-xlarge/focusable_view_bg.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_focused="true" android:drawable="@drawable/focused_bg" />
+</selector>
diff --git a/res/layout-land/user_folder.xml b/res/layout-land/user_folder.xml
index 5da4aa5..ca437bf 100644
--- a/res/layout-land/user_folder.xml
+++ b/res/layout-land/user_folder.xml
@@ -14,7 +14,9 @@
      limitations under the License.
 -->
 
-<com.android.launcher2.UserFolder xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher2.UserFolder
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
     android:orientation="vertical">
     
     <Button
@@ -29,17 +31,24 @@
 
     <FrameLayout
          android:layout_width="match_parent"
-	   android:layout_height="0dip"
+         android:layout_height="0dip"
          android:layout_weight="1"
          android:background="@drawable/box_launcher_bottom">
 
-        <GridView
+        <com.android.launcher2.CellLayout
              android:id="@id/folder_content"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
 
              android:cacheColorHint="#ff333333"
 
+             launcher:cellWidth="@dimen/folder_cell_width"
+             launcher:cellHeight="@dimen/folder_cell_height"
+             launcher:xAxisStartPadding="0dip"
+             launcher:xAxisEndPadding="0dip"
+             launcher:yAxisStartPadding="0dip"
+             launcher:yAxisEndPadding="0dip"
+
              android:scrollbarAlwaysDrawVerticalTrack="true"
              android:scrollbarStyle="insideInset"
              android:drawSelectorOnTop="false"
diff --git a/res/layout-port/user_folder.xml b/res/layout-port/user_folder.xml
index 0e6df66..c00b548 100644
--- a/res/layout-port/user_folder.xml
+++ b/res/layout-port/user_folder.xml
@@ -14,7 +14,9 @@
      limitations under the License.
 -->
 
-<com.android.launcher2.UserFolder xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher2.UserFolder
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
     android:orientation="vertical">
         
     <Button
@@ -29,16 +31,23 @@
 
     <FrameLayout
          android:layout_width="match_parent"
-	   android:layout_height="0dip"
+         android:layout_height="0dip"
          android:layout_weight="1"
          android:background="@drawable/box_launcher_bottom">
-        <GridView
+        <com.android.launcher2.CellLayout
              android:id="@id/folder_content"
              android:layout_width="match_parent"
-	       android:layout_height="match_parent"
+             android:layout_height="match_parent"
 
              android:cacheColorHint="#ff333333"
 
+             launcher:cellWidth="@dimen/folder_cell_width"
+             launcher:cellHeight="@dimen/folder_cell_height"
+             launcher:xAxisStartPadding="0dip"
+             launcher:xAxisEndPadding="0dip"
+             launcher:yAxisStartPadding="0dip"
+             launcher:yAxisEndPadding="0dip"
+
              android:scrollbarAlwaysDrawVerticalTrack="true"
              android:scrollbarStyle="insideInset"
              android:drawSelectorOnTop="false"
diff --git a/res/layout-xlarge-land/application.xml b/res/layout-xlarge-land/application.xml
index 2598e5a..9393f7e 100644
--- a/res/layout-xlarge-land/application.xml
+++ b/res/layout-xlarge-land/application.xml
@@ -15,4 +15,6 @@
 -->
 
 <com.android.launcher2.BubbleTextView xmlns:android="http://schemas.android.com/apk/res/android"
-   style="@style/WorkspaceIcon.Landscape" />
+   style="@style/WorkspaceIcon.Landscape"
+   android:focusable="true"
+   android:background="@drawable/focusable_view_bg" />
diff --git a/res/layout-xlarge-land/customization_drawer_tab_contents.xml b/res/layout-xlarge-land/customization_drawer_tab_contents.xml
deleted file mode 100644
index 50e7b66..0000000
--- a/res/layout-xlarge-land/customization_drawer_tab_contents.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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.
--->
-<com.android.launcher2.CustomizePagedView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
-
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    launcher:wallpaperCellSpanX="3"
-    launcher:wallpaperCellCountX="12"
-    launcher:widgetCellCountX="14"
-    launcher:cellCountX="8"
-    launcher:cellCountY="3"
-    launcher:pageLayoutWidthGap="32dp"
-    launcher:pageLayoutHeightGap="12dp"
-    launcher:pageLayoutPaddingTop="40dp"
-    launcher:pageLayoutPaddingBottom="25dp"
-    launcher:pageLayoutPaddingLeft="20dp"
-    launcher:pageLayoutPaddingRight="20dp" />
\ No newline at end of file
diff --git a/res/layout-xlarge-port/all_apps_tabbed.xml b/res/layout-xlarge-port/all_apps_tabbed.xml
deleted file mode 100644
index c357d2e..0000000
--- a/res/layout-xlarge-port/all_apps_tabbed.xml
+++ /dev/null
@@ -1,105 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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.
--->
-<com.android.launcher2.AllAppsTabbed
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher">
-    <com.android.launcher2.AllAppsBackground
-        android:id="@+id/all_apps_background"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent" />
-    <LinearLayout
-        android:orientation="vertical"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent">
-        <RelativeLayout
-            android:layout_width="700dp"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center_horizontal"
-            android:background="@drawable/tab_unselected_holo">
-            <TabWidget
-                android:id="@android:id/tabs"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_alignParentLeft="true"
-                android:tabStripEnabled="false" />
-            <FrameLayout
-                android:id="@+id/market_info_frame"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_alignParentRight="true"
-                android:layout_centerVertical="true">
-                <com.android.launcher2.ApplicationInfoDropTarget
-                    android:id="@+id/all_apps_info_target"
-                    android:drawableRight="@drawable/ic_home_info_holo_dark"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_gravity="center"
-                    android:visibility="invisible"/>
-                <TextView
-                    android:id="@+id/market_button"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_gravity="center"
-                    android:gravity="center"
-                    android:paddingRight="22dp"
-                    android:text="@string/market"
-                    android:textColor="@color/workspace_all_apps_and_delete_zone_text_color"
-                    android:textSize="18sp"
-                    android:shadowColor="@color/workspace_all_apps_and_delete_zone_text_shadow_color"
-                    android:shadowDx="0.0"
-                    android:shadowDy="0.0"
-                    android:shadowRadius="2.0" />
-            </FrameLayout>
-            <com.android.launcher2.DeleteZone
-                android:id="@+id/all_apps_delete_zone"
-                android:drawablePadding="@dimen/delete_zone_drawable_padding"
-                android:drawableLeft="@drawable/delete_zone_selector"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_toLeftOf="@id/market_info_frame"
-                android:layout_centerVertical="true"
-                android:visibility="invisible"
-                android:paddingRight="22dp"
-                launcher:direction="horizontal"
-
-                android:gravity="center"
-                android:textColor="@color/workspace_all_apps_and_delete_zone_text_color"
-                android:textSize="18sp"
-                android:shadowColor="@color/workspace_all_apps_and_delete_zone_text_shadow_color"
-                android:shadowDx="0.0"
-                android:shadowDy="0.0"
-                android:shadowRadius="2.0" />
-        </RelativeLayout>
-        <FrameLayout
-            android:id="@android:id/tabcontent"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent">
-            <com.android.launcher2.AllAppsPagedView
-                android:id="@+id/all_apps_paged_view"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                launcher:cellCountX="5"
-                launcher:cellCountY="7"
-                launcher:pageLayoutWidthGap="36dp"
-                launcher:pageLayoutHeightGap="36dp"
-                launcher:pageLayoutPaddingTop="25dp"
-                launcher:pageLayoutPaddingBottom="10dp"
-                launcher:pageLayoutPaddingLeft="20dp"
-                launcher:pageLayoutPaddingRight="20dp">
-            </com.android.launcher2.AllAppsPagedView>
-        </FrameLayout>
-    </LinearLayout>
-</com.android.launcher2.AllAppsTabbed>
diff --git a/res/layout-xlarge-port/customization_drawer_tab_contents.xml b/res/layout-xlarge-port/customization_drawer_tab_contents.xml
deleted file mode 100644
index 0381be0..0000000
--- a/res/layout-xlarge-port/customization_drawer_tab_contents.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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.
--->
-<com.android.launcher2.CustomizePagedView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
-
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    launcher:wallpaperCellSpanX="3"
-    launcher:wallpaperCellCountX="9"
-    launcher:widgetCellCountX="9"
-    launcher:cellCountX="5"
-    launcher:cellCountY="3"
-    launcher:pageLayoutWidthGap="36dp"
-    launcher:pageLayoutHeightGap="12dp"
-    launcher:pageLayoutPaddingTop="40dp"
-    launcher:pageLayoutPaddingBottom="25dp"
-    launcher:pageLayoutPaddingLeft="20dp"
-    launcher:pageLayoutPaddingRight="20dp" />
diff --git a/res/layout-xlarge/all_apps_no_items_placeholder.xml b/res/layout-xlarge/all_apps_no_items_placeholder.xml
index 247870c..b766df1 100644
--- a/res/layout-xlarge/all_apps_no_items_placeholder.xml
+++ b/res/layout-xlarge/all_apps_no_items_placeholder.xml
@@ -32,4 +32,5 @@
     android:drawablePadding="0dip"
 
     android:maxLines="2"
-    android:fadingEdge="horizontal" />
+    android:fadingEdge="horizontal"
+    android:focusable="false" />
diff --git a/res/layout-xlarge/all_apps_paged_view_application.xml b/res/layout-xlarge/all_apps_paged_view_application.xml
index e5f07bf..16e5d82 100644
--- a/res/layout-xlarge/all_apps_paged_view_application.xml
+++ b/res/layout-xlarge/all_apps_paged_view_application.xml
@@ -21,9 +21,12 @@
     launcher:blurColor="#FF6B8CF0"
     launcher:outlineColor="#FF8CD2FF"
 
+    style="@style/WorkspaceIcon.AllApps"
+
     android:id="@+id/application_icon"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:gravity="center_horizontal"
 
-    style="@style/WorkspaceIcon.AllApps" />
+    android:focusable="true"
+    android:background="@drawable/focusable_view_bg" />
diff --git a/res/layout-xlarge-land/all_apps_tabbed.xml b/res/layout-xlarge/all_apps_tabbed.xml
similarity index 75%
rename from res/layout-xlarge-land/all_apps_tabbed.xml
rename to res/layout-xlarge/all_apps_tabbed.xml
index 9244213..b00b3c3 100644
--- a/res/layout-xlarge-land/all_apps_tabbed.xml
+++ b/res/layout-xlarge/all_apps_tabbed.xml
@@ -24,12 +24,15 @@
         android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
+        <!-- The layout_width of this RelativeLayout gets overwritten in
+             AllAppsTabbed.onFinishInflate -->
         <RelativeLayout
-            android:layout_width="952dp"
+            android:id="@+id/all_apps_tab_bar"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="center_horizontal"
             android:background="@drawable/tab_unselected_holo">
-            <TabWidget
+            <com.android.launcher2.FocusOnlyTabWidget
                 android:id="@android:id/tabs"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
@@ -47,7 +50,9 @@
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:layout_gravity="center"
-                    android:visibility="invisible"/>
+                    android:visibility="invisible"
+                    android:background="@drawable/focusable_view_bg"
+                    android:focusable="true" />
                 <TextView
                     android:id="@+id/market_button"
                     android:layout_width="wrap_content"
@@ -61,7 +66,9 @@
                     android:shadowColor="@color/workspace_all_apps_and_delete_zone_text_shadow_color"
                     android:shadowDx="0.0"
                     android:shadowDy="0.0"
-                    android:shadowRadius="2.0" />
+                    android:shadowRadius="2.0"
+                    android:background="@drawable/focusable_view_bg"
+                    android:focusable="true" />
             </FrameLayout>
             <com.android.launcher2.DeleteZone
                 android:id="@+id/all_apps_delete_zone"
@@ -81,7 +88,10 @@
                 android:shadowColor="@color/workspace_all_apps_and_delete_zone_text_shadow_color"
                 android:shadowDx="0.0"
                 android:shadowDy="0.0"
-                android:shadowRadius="2.0" />
+                android:shadowRadius="2.0"
+
+                android:background="@drawable/focusable_view_bg"
+                android:focusable="true" />
         </RelativeLayout>
         <FrameLayout
             android:id="@android:id/tabcontent"
@@ -91,14 +101,14 @@
                 android:id="@+id/all_apps_paged_view"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                launcher:cellCountX="7"
-                launcher:cellCountY="5"
-                launcher:pageLayoutWidthGap="36dp"
-                launcher:pageLayoutHeightGap="6dp"
-                launcher:pageLayoutPaddingTop="20dp"
-                launcher:pageLayoutPaddingBottom="15dp"
-                launcher:pageLayoutPaddingLeft="40dp"
-                launcher:pageLayoutPaddingRight="40dp">
+                launcher:cellCountX="@integer/all_apps_view_cellCountX"
+                launcher:cellCountY="@integer/all_apps_view_cellCountY"
+                launcher:pageLayoutWidthGap="@dimen/all_apps_view_pageLayoutWidthGap"
+                launcher:pageLayoutHeightGap="@dimen/all_apps_view_pageLayoutHeightGap"
+                launcher:pageLayoutPaddingTop="@dimen/all_apps_view_pageLayoutPaddingTop"
+                launcher:pageLayoutPaddingBottom="@dimen/all_apps_view_pageLayoutPaddingBottom"
+                launcher:pageLayoutPaddingLeft="@dimen/all_apps_view_pageLayoutPaddingLeft"
+                launcher:pageLayoutPaddingRight="@dimen/all_apps_view_pageLayoutPaddingRight">
             </com.android.launcher2.AllAppsPagedView>
         </FrameLayout>
     </LinearLayout>
diff --git a/res/layout-xlarge/button_bar.xml b/res/layout-xlarge/button_bar.xml
index 5c96c5c..62115f7 100644
--- a/res/layout-xlarge/button_bar.xml
+++ b/res/layout-xlarge/button_bar.xml
@@ -15,31 +15,34 @@
 -->
 <RelativeLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher">
+    xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
+    android:focusable="false">
 
    <!-- Global search icon -->
    <ImageView
-       android:id="@+id/search_button"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content"
-       android:layout_alignParentTop="true"
-       android:layout_alignParentLeft="true"
-       android:paddingLeft="@dimen/toolbar_button_horizontal_padding"
-       android:paddingRight="@dimen/toolbar_button_horizontal_padding"
-       android:paddingTop="@dimen/toolbar_button_vertical_padding"
-       android:paddingBottom="@dimen/toolbar_button_vertical_padding"
-       android:src="@drawable/ic_generic_search"
-       android:background="@drawable/button_bg"
-       android:onClick="onClickSearchButton"
-       android:focusable="true"
-       android:clickable="true" />
+        android:id="@+id/search_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentLeft="true"
+        android:paddingLeft="@dimen/toolbar_button_horizontal_padding"
+        android:paddingRight="@dimen/toolbar_button_horizontal_padding"
+        android:paddingTop="@dimen/toolbar_button_vertical_padding"
+        android:paddingBottom="@dimen/toolbar_button_vertical_padding"
+        android:src="@drawable/ic_generic_search"
+        android:background="@drawable/button_bg"
+        android:onClick="onClickSearchButton"
+
+        android:focusable="true"
+        android:clickable="true"
+        android:contentDescription="@string/accessibility_search_button" />
 
     <ImageView
         android:id="@+id/search_divider"
         android:src="@drawable/divider_launcher_holo"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
-        android:layout_toRightOf="@id/search_button"
+        android:layout_toRightOf="@+id/search_button"
         android:paddingTop="@dimen/toolbar_button_vertical_padding"
         android:paddingBottom="@dimen/toolbar_button_vertical_padding"
 
@@ -52,7 +55,7 @@
         android:id="@+id/voice_button"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_toRightOf="@id/search_divider"
+        android:layout_toRightOf="@+id/search_divider"
         android:paddingLeft="@dimen/toolbar_button_horizontal_padding"
         android:paddingRight="@dimen/toolbar_button_horizontal_padding"
         android:paddingTop="@dimen/toolbar_button_vertical_padding"
@@ -60,35 +63,12 @@
         android:src="@drawable/ic_voice_search"
         android:background="@drawable/button_bg"
         android:onClick="onClickVoiceButton"
-        android:focusable="true"
-        android:clickable="true"/>
-
-    <ImageView
-        android:id="@+id/configure_button"
-        android:src="@drawable/ic_home_add_holo_dark"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentTop="true"
-        android:layout_alignParentRight="true"
-        android:paddingLeft="@dimen/toolbar_button_horizontal_padding"
-        android:paddingRight="@dimen/toolbar_button_horizontal_padding"
-        android:paddingTop="@dimen/toolbar_button_vertical_padding"
-        android:paddingBottom="@dimen/toolbar_button_vertical_padding"
-        android:background="@drawable/button_bg"
 
         android:focusable="true"
-        android:clickable="true" />
-    <ImageView
-        android:id="@+id/divider"
-        android:src="@drawable/divider_launcher_holo"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_toLeftOf="@id/configure_button"
-        android:paddingTop="@dimen/toolbar_button_vertical_padding"
-        android:paddingBottom="@dimen/toolbar_button_vertical_padding"
+        android:clickable="true"
+        android:contentDescription="@string/accessibility_voice_search_button" />
 
-        android:focusable="false"
-        android:clickable="true" />
+    <!-- AllApps icon -->
     <com.android.launcher2.StrokedTextView
         android:id="@+id/all_apps_button"
         android:text="@string/all_apps_button_label"
@@ -96,7 +76,7 @@
         android:drawableLeft="@drawable/ic_home_all_apps_holo_dark"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_toLeftOf="@id/divider"
+        android:layout_toLeftOf="@+id/all_apps_divider"
         android:paddingLeft="@dimen/toolbar_button_horizontal_padding"
         android:paddingRight="@dimen/toolbar_button_horizontal_padding"
         android:paddingTop="@dimen/all_apps_button_vertical_padding"
@@ -117,16 +97,38 @@
         android:shadowRadius="2.5"
 
         android:focusable="true"
-        android:clickable="true" />
+        android:clickable="true"
+        android:contentDescription="@string/accessibility_all_apps_button" />
+
     <ImageView
-        android:id="@+id/divider_during_drag"
+        android:id="@+id/all_apps_divider"
         android:src="@drawable/divider_launcher_holo"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
-        android:layout_toLeftOf="@id/configure_button"
+        android:layout_toLeftOf="@+id/configure_button"
         android:paddingTop="@dimen/toolbar_button_vertical_padding"
         android:paddingBottom="@dimen/toolbar_button_vertical_padding"
-        android:visibility="gone" />
+
+        android:focusable="false"
+        android:clickable="true" />
+
+    <!-- Customize icon -->
+    <ImageView
+        android:id="@+id/configure_button"
+        android:src="@drawable/ic_home_add_holo_dark"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentRight="true"
+        android:paddingLeft="@dimen/toolbar_button_horizontal_padding"
+        android:paddingRight="@dimen/toolbar_button_horizontal_padding"
+        android:paddingTop="@dimen/toolbar_button_vertical_padding"
+        android:paddingBottom="@dimen/toolbar_button_vertical_padding"
+        android:background="@drawable/button_bg"
+        android:focusable="true"
+        android:contentDescription="@string/accessibility_customize_button" />
+
+    <!-- Delete icon -->
     <com.android.launcher2.DeleteZone
         android:id="@+id/delete_zone"
         android:text="@string/delete_zone_label_workspace"
@@ -134,12 +136,12 @@
         android:drawableLeft="@drawable/delete_zone_selector"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_alignRight="@id/configure_button"
+        android:layout_alignRight="@+id/configure_button"
         android:paddingLeft="@dimen/toolbar_button_horizontal_padding"
         android:paddingRight="@dimen/toolbar_button_horizontal_padding"
         android:paddingTop="@dimen/toolbar_button_vertical_padding"
         android:paddingBottom="@dimen/toolbar_button_vertical_padding"
-        android:background="@drawable/button_bg"
+       android:background="@drawable/button_bg"
 
         android:gravity="center_horizontal|center_vertical"
         android:textColor="@color/workspace_all_apps_and_delete_zone_text_color"
@@ -150,5 +152,8 @@
         android:shadowRadius="2.0"
 
         android:visibility="gone"
-        launcher:direction="horizontal" />
-</RelativeLayout>
\ No newline at end of file
+        launcher:direction="horizontal"
+
+        android:focusable="true"
+        android:contentDescription="@string/accessibility_delete_button" />
+</RelativeLayout>
diff --git a/res/layout-xlarge/customization_drawer.xml b/res/layout-xlarge/customization_drawer.xml
index dd1234c..d8db066 100644
--- a/res/layout-xlarge/customization_drawer.xml
+++ b/res/layout-xlarge/customization_drawer.xml
@@ -13,14 +13,18 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<TabHost xmlns:android="http://schemas.android.com/apk/res/android">
+<com.android.launcher2.CustomizeTrayTabHost
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher">
     <LinearLayout
         android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
-        <TabWidget
+        <!-- The layout_width of this RelativeLayout gets overwritten in
+             CustomizeTrayTabHost.onFinishInflate -->
+        <com.android.launcher2.FocusOnlyTabWidget
             android:id="@android:id/tabs"
-            android:layout_width="@dimen/customization_drawer_tab_widget_width"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="center_horizontal"
             android:background="@drawable/tab_unselected_holo"
@@ -28,7 +32,23 @@
         <FrameLayout
             android:id="@android:id/tabcontent"
             android:layout_width="match_parent"
-            android:layout_height="@dimen/customization_drawer_content_height">
+            android:layout_height="match_parent">
+            <com.android.launcher2.CustomizePagedView
+                android:id="@+id/customization_drawer_tab_contents"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                launcher:wallpaperCellSpanX="@integer/customization_drawer_contents_wallpaperCellSpanX"
+                launcher:wallpaperCellCountX="@integer/customization_drawer_contents_wallpaperCellCountX"
+                launcher:widgetCellCountX="@integer/customization_drawer_contents_widgetCellCountX"
+                launcher:cellCountX="@integer/customization_drawer_contents_cellCountX"
+                launcher:cellCountY="@integer/customization_drawer_contents_cellCountY"
+                launcher:pageLayoutWidthGap="@dimen/customization_drawer_contents_pageLayoutWidthGap"
+                launcher:pageLayoutHeightGap="12dp"
+                launcher:pageLayoutPaddingTop="40dp"
+                launcher:pageLayoutPaddingBottom="25dp"
+                launcher:pageLayoutPaddingLeft="20dp"
+                launcher:pageLayoutPaddingRight="20dp"
+                launcher:pageLayoutMaxHeight="@dimen/customization_drawer_content_height" />
          </FrameLayout>
       </LinearLayout>
-</TabHost>
\ No newline at end of file
+</com.android.launcher2.CustomizeTrayTabHost>
\ No newline at end of file
diff --git a/res/layout-xlarge/customize_paged_view_item.xml b/res/layout-xlarge/customize_paged_view_item.xml
index b2e5f08..80d678e 100644
--- a/res/layout-xlarge/customize_paged_view_item.xml
+++ b/res/layout-xlarge/customize_paged_view_item.xml
@@ -25,4 +25,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
 
-    style="@style/WorkspaceIcon.Landscape" />
+    style="@style/WorkspaceIcon.Landscape"
+
+    android:background="@drawable/focusable_view_bg"
+    android:focusable="true" />
diff --git a/res/layout-xlarge/customize_paged_view_wallpaper.xml b/res/layout-xlarge/customize_paged_view_wallpaper.xml
index 8c5abc8..d6284c2 100644
--- a/res/layout-xlarge/customize_paged_view_wallpaper.xml
+++ b/res/layout-xlarge/customize_paged_view_wallpaper.xml
@@ -25,7 +25,10 @@
     android:paddingBottom="50dp"
 
     launcher:blurColor="#FF6B8CF0"
-    launcher:outlineColor="#FF8CD2FF">
+    launcher:outlineColor="#FF8CD2FF"
+
+    android:background="@drawable/focusable_view_bg"
+    android:focusable="true">
 
     <!-- The preview image for the wallpaper. -->
     <ImageView
diff --git a/res/layout-xlarge/customize_paged_view_widget.xml b/res/layout-xlarge/customize_paged_view_widget.xml
index c0b4552..35791f5 100644
--- a/res/layout-xlarge/customize_paged_view_widget.xml
+++ b/res/layout-xlarge/customize_paged_view_widget.xml
@@ -25,7 +25,10 @@
     android:paddingBottom="50dp"
 
     launcher:blurColor="#FF6B8CF0"
-    launcher:outlineColor="#FF8CD2FF">
+    launcher:outlineColor="#FF8CD2FF"
+
+    android:background="@drawable/focusable_view_bg"
+    android:focusable="true">
 
     <!-- The icon of the widget. -->
     <ImageView
diff --git a/res/layout-xlarge/launcher.xml b/res/layout-xlarge/launcher.xml
index acf62f9..c71d131 100644
--- a/res/layout-xlarge/launcher.xml
+++ b/res/layout-xlarge/launcher.xml
@@ -20,7 +20,8 @@
 
     android:id="@+id/drag_layer"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:focusable="false">
 
     <!-- The workspace contains 5 screens of cells -->
     <com.android.launcher2.Workspace
@@ -41,6 +42,12 @@
         <include android:id="@+id/cell5" layout="@layout/workspace_screen" />
     </com.android.launcher2.Workspace>
 
+    <include layout="@layout/button_bar"
+        android:id="@+id/all_apps_button_cluster"
+        android:layout_width="fill_parent"
+        android:layout_height="?android:attr/actionBarSize"
+        android:layout_gravity="top" />
+
     <include
         layout="@layout/all_apps_tabbed"
         android:id="@+id/all_apps_view"
@@ -48,12 +55,6 @@
         android:layout_height="match_parent"
         android:layout_gravity="top" />
 
-    <include layout="@layout/button_bar"
-        android:id="@+id/all_apps_button_cluster"
-        android:layout_width="fill_parent"
-        android:layout_height="?android:attr/actionBarSize"
-        android:layout_gravity="top" />
-
     <include layout="@layout/customization_drawer"
         android:id="@+id/customization_drawer"
         android:layout_width="match_parent"
diff --git a/res/layout-xlarge/tab_widget_indicator.xml b/res/layout-xlarge/tab_widget_indicator.xml
index 7794e29..8968c2a 100644
--- a/res/layout-xlarge/tab_widget_indicator.xml
+++ b/res/layout-xlarge/tab_widget_indicator.xml
@@ -14,6 +14,6 @@
      limitations under the License.
 -->
 
-<TextView
+<com.android.launcher2.AccessibleTabView
     xmlns:android="http://schemas.android.com/apk/res/android"
     style="@style/TabIndicator" />
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index df0a210..d4eecfd 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"الرئيسية"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"إزالة"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"إزالة"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"إزالة التحديث"</string>
     <string name="menu_add" msgid="3065046628354640854">"إضافة"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"إدارة التطبيقات"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 5be82f1..49e86d3 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Начало"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Премахване"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Деинсталиране"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Деинстал. на актуализацията"</string>
     <string name="menu_add" msgid="3065046628354640854">"Добавяне"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Управление на приложенията"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 56ada67..6a1fecb 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Pàgina d\'inici"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Elimina"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Desinstal·la"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Desinstal·la l\'actualització"</string>
     <string name="menu_add" msgid="3065046628354640854">"Afegeix"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Gestiona les aplicacions"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 47fb726..919488d 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Plocha"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Odebrat"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Odinstalovat"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Odinstalovat aktualizaci"</string>
     <string name="menu_add" msgid="3065046628354640854">"Přidat"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Spravovat aplikace"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index f481234..ae7da8a 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Start"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Fjern"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Afinstaller"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Afinstaller opdatering"</string>
     <string name="menu_add" msgid="3065046628354640854">"Tilføj"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Administrer programmer"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index f69ee43..21d020d 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -25,7 +25,7 @@
     <string name="chooser_wallpaper" msgid="5988031014201479733">"Hintergrund auswählen"</string>
     <string name="wallpaper_instructions" msgid="4215640646180727542">"Hintergrund festlegen"</string>
     <string name="pick_wallpaper" msgid="5630222540525626723">"Hintergrundbilder"</string>
-    <string name="activity_not_found" msgid="5591731020063337696">"Die Anwendung ist nicht installiert."</string>
+    <string name="activity_not_found" msgid="5591731020063337696">"Anwendung ist nicht installiert."</string>
     <string name="configure_wallpaper" msgid="2820186271419674623">"Konfigurieren..."</string>
     <string name="widgets_tab_label" msgid="9145860100000983599">"Widgets"</string>
     <string name="folders_tab_label" msgid="1145293785541489736">"Ordner"</string>
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Startseite"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Entfernen"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Deinstallieren"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Update deinstallieren"</string>
     <string name="menu_add" msgid="3065046628354640854">"Hinzufügen"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Apps verwalten"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 5e9b644..92d501e 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Αρχική σελίδα"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Κατάργηση"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Κατάργηση εγκατάστασης"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Κατάργηση εγκατάστασης ενημέρωσης"</string>
     <string name="menu_add" msgid="3065046628354640854">"Προσθήκη"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Διαχείριση εφαρμογών"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 7334133..2a22436 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Home"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Remove"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Uninstall"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Uninstall update"</string>
     <string name="menu_add" msgid="3065046628354640854">"Add"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Manage apps"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index eadcbae..94c921c 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Página principal"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Quitar"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Desinstalar"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Desinstalar la actualización"</string>
     <string name="menu_add" msgid="3065046628354640854">"Agregar"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Administrar aplicaciones"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 90b4e1d..dfc22b5 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Inicio"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Eliminar del  escritorio"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Desinstalar"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Desinstalar actualización"</string>
     <string name="menu_add" msgid="3065046628354640854">"Añadir"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Administrar aplicaciones"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 0d53ea5..867b9b4 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"صفحه اصلی"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"حذف"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"حذف نصب"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"حذف نصب به روزرسانی"</string>
     <string name="menu_add" msgid="3065046628354640854">"افزودن"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"مدیریت برنامه ها"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 7b02c8d..5174734 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Etusivu"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Poista"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Poista"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Poista päivitys"</string>
     <string name="menu_add" msgid="3065046628354640854">"Lisää"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Hallinnoi sovelluksia"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 7ff39d7..687e0d9 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Page d\'accueil"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Supprimer"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Désinstaller"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Désinstaller la mise à jour"</string>
     <string name="menu_add" msgid="3065046628354640854">"Ajouter"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Gérer les applications"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index ea09db0..1016271 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Početna"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Ukloni"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Deinstaliraj"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Deinstalacija ažuriranja"</string>
     <string name="menu_add" msgid="3065046628354640854">"Dodaj"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Upravljaj aplikacijama"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 77d9dda..3301158 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Főoldal"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Eltávolítás"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Eltávolítás"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Eltávolítja a frissítést"</string>
     <string name="menu_add" msgid="3065046628354640854">"Hozzáadás"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Alkalmazások kezelése"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index a7ed856..a2ddeee 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Rumah"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Hapus"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Copot pemasangan"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Copot pemasangan pemutakhiran"</string>
     <string name="menu_add" msgid="3065046628354640854">"Tambahkan"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Mengelola aplikasi"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 003db6f..b25b808 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Home"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Rimuovi"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Disinstalla"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Disinstalla aggiornamento"</string>
     <string name="menu_add" msgid="3065046628354640854">"Aggiungi"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Gestisci applicazioni"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 75df245..563c674 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"דף הבית"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"הסר"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"הסר התקנה"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"הסר את התקנת העדכון"</string>
     <string name="menu_add" msgid="3065046628354640854">"הוסף"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"נהל יישומים"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index ee6ab83..2ab244c 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"ホーム"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"削除"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"アンインストール"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"アップデートをアンインストール"</string>
     <string name="menu_add" msgid="3065046628354640854">"追加"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"アプリの管理"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index f2b8b0c..db937e9 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"홈"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"삭제"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"제거"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"업데이트 제거"</string>
     <string name="menu_add" msgid="3065046628354640854">"추가"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"애플리케이션 관리"</string>
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
index 159cbb4..9cb549b 100644
--- a/res/values-land/dimens.xml
+++ b/res/values-land/dimens.xml
@@ -17,5 +17,7 @@
 <resources>
     <dimen name="workspace_cell_width">106dip</dimen>
     <dimen name="workspace_cell_height">74dip</dimen>
+    <dimen name="folder_cell_width">100dip</dimen>
+    <dimen name="folder_cell_height">74dip</dimen>
     <dimen name="button_bar_height">62dip</dimen>
 </resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index acf0e27..30858e2 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Pagrindinis"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Pašalinti"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Pašalinti"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Pašalinti naujinį"</string>
     <string name="menu_add" msgid="3065046628354640854">"Pridėti"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Valdyti programas"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 9c1b389..9ca0aa1 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Sākums"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Noņemt"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Atinstalēt"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Atinstalēt atjauninājumu"</string>
     <string name="menu_add" msgid="3065046628354640854">"Pievienot"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Lietotņu pārvaldība"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index ccdab7e..557dfff 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Startsiden"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Fjern"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Avinstaller"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Avinstaller oppdateringen"</string>
     <string name="menu_add" msgid="3065046628354640854">"Legg til"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Administrer programmer"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 3d7c7ac..f576ae5 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Startpagina"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Verwijderen"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Verwijderen"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Update verwijderen"</string>
     <string name="menu_add" msgid="3065046628354640854">"Toevoegen"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Apps beheren"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index f116165..e51a25e 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Ekran główny"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Usuń"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Odinstaluj"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Odinstaluj aktualizację"</string>
     <string name="menu_add" msgid="3065046628354640854">"Dodaj"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Zarządzaj aplikacjami"</string>
diff --git a/res/values-port/dimens.xml b/res/values-port/dimens.xml
index 65a3fd3..ad5922d 100644
--- a/res/values-port/dimens.xml
+++ b/res/values-port/dimens.xml
@@ -17,4 +17,6 @@
 <resources>
     <dimen name="workspace_cell_width">80dip</dimen>
     <dimen name="workspace_cell_height">100dip</dimen>
+    <dimen name="folder_cell_width">74dip</dimen>
+    <dimen name="folder_cell_height">86dip</dimen>
 </resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index a3a5a8d..917119d 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Página inicial"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Remover"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Desinstalar"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Desinstalar atualização"</string>
     <string name="menu_add" msgid="3065046628354640854">"Adicionar"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Gerir aplicações"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 55deb94..309afe6 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Página inicial"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Remover"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Desinstalar"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Desinstalar atualização"</string>
     <string name="menu_add" msgid="3065046628354640854">"Adicionar"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Gerenciar aplicativos"</string>
diff --git a/res/values-rm/strings.xml b/res/values-rm/strings.xml
index 1b5729b..a8453ed 100644
--- a/res/values-rm/strings.xml
+++ b/res/values-rm/strings.xml
@@ -85,6 +85,16 @@
     <skip />
     <!-- no translation found for delete_zone_label_all_apps (6664588234817475108) -->
     <skip />
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <!-- no translation found for delete_zone_label_all_apps_system_app (3683920959591819044) -->
     <skip />
     <string name="menu_add" msgid="3065046628354640854">"Agiuntar"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 3a72c51..f175b4d 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Domiciliu"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Eliminaţi"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Dezinstalaţi"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Dezinstalaţi actualizarea"</string>
     <string name="menu_add" msgid="3065046628354640854">"Adăugaţi"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Gestionaţi aplicaţii"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 912c3f4..dd1f0af 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Главная"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Удалить"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Удалить"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Удалить обновление"</string>
     <string name="menu_add" msgid="3065046628354640854">"Добавить"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Приложения"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 3cadf3b..ba54979 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Domovská stránka"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Odstrániť"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Odinštalovať"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Odinštalovať aktualizáciu"</string>
     <string name="menu_add" msgid="3065046628354640854">"Pridať"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Spravovať aplikácie"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index daf0899..ba98aeb 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Začetni zaslon"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Odstrani"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Odstrani"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Odstrani posodobitev"</string>
     <string name="menu_add" msgid="3065046628354640854">"Dodaj"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Upravljaj programe"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 1d92080..52cf464 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Почетна"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Уклони"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Деинсталирај"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Деинсталирај ажурирања"</string>
     <string name="menu_add" msgid="3065046628354640854">"Додај"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Управљање апликацијама"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 8094f05..96ff34b 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Startsida"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Ta bort"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Avinstallera"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Avinstallera uppdatering"</string>
     <string name="menu_add" msgid="3065046628354640854">"Lägg till"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Hantera appar"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 26c33c9..875fa28 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"บ้าน"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"นำออก"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"ถอนการติดตั้ง"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"ถอนการติดตั้งการอัปเดต"</string>
     <string name="menu_add" msgid="3065046628354640854">"เพิ่ม"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"จัดการแอปพลิเคชัน"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index f21dc3c..a1789cb 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Home"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Alisin"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"I-uninstall"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"I-uninstall ang update"</string>
     <string name="menu_add" msgid="3065046628354640854">"Idagdag"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Pamahalaan ang apps"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index bf80bf5..9ae1c8e 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Ana Sayfa"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Kaldır"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Yüklemeyi Kaldır"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Güncelleme kaldırılsın mı?"</string>
     <string name="menu_add" msgid="3065046628354640854">"Ekle"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Uyglm yönet"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 267ca88..daee514 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Головна"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Видалити"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Видалити"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Видалити оновлення"</string>
     <string name="menu_add" msgid="3065046628354640854">"Додати"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Керув. прогр."</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index d3f549f..ca3e738 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Màn hình trang chủ"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"Xóa"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"Gỡ cài đặt"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"Gỡ cài đặt cập nhật"</string>
     <string name="menu_add" msgid="3065046628354640854">"Thêm"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"Quản lý ứng dụng"</string>
diff --git a/res/values-xlarge-land/dimens.xml b/res/values-xlarge-land/dimens.xml
index b3d40d5..8d25555 100644
--- a/res/values-xlarge-land/dimens.xml
+++ b/res/values-xlarge-land/dimens.xml
@@ -27,6 +27,20 @@
 
     <dimen name="customization_drawer_height">480dp</dimen>
     <dimen name="customization_drawer_content_height">420dp</dimen>
-    <dimen name="customization_drawer_content_min_width">952dp</dimen>
-    <dimen name="customization_drawer_tab_widget_width">952dp</dimen>
+
+    <integer name="all_apps_view_cellCountX">7</integer>
+    <integer name="all_apps_view_cellCountY">5</integer>
+    <dimen name="all_apps_view_pageLayoutWidthGap">36dp</dimen>
+    <dimen name="all_apps_view_pageLayoutHeightGap">6dp</dimen>
+    <dimen name="all_apps_view_pageLayoutPaddingTop">20dp</dimen>
+    <dimen name="all_apps_view_pageLayoutPaddingBottom">14dp</dimen>
+    <dimen name="all_apps_view_pageLayoutPaddingLeft">40dp</dimen>
+    <dimen name="all_apps_view_pageLayoutPaddingRight">40dp</dimen>
+
+    <integer name="customization_drawer_contents_wallpaperCellSpanX">3</integer>
+    <integer name="customization_drawer_contents_wallpaperCellCountX">12</integer>
+    <integer name="customization_drawer_contents_widgetCellCountX">14</integer>
+    <integer name="customization_drawer_contents_cellCountX">8</integer>
+    <integer name="customization_drawer_contents_cellCountY">3</integer>
+    <dimen name="customization_drawer_contents_pageLayoutWidthGap">32dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/res/values-xlarge-port/dimens.xml b/res/values-xlarge-port/dimens.xml
index 8a4c78b..e696e34 100644
--- a/res/values-xlarge-port/dimens.xml
+++ b/res/values-xlarge-port/dimens.xml
@@ -27,6 +27,20 @@
 
     <dimen name="customization_drawer_height">800dp</dimen>
     <dimen name="customization_drawer_content_height">420dp</dimen>
-    <dimen name="customization_drawer_content_min_width">640dp</dimen>
-    <dimen name="customization_drawer_tab_widget_width">700dp</dimen>
+
+    <integer name="all_apps_view_cellCountX">5</integer>
+    <integer name="all_apps_view_cellCountY">7</integer>
+    <dimen name="all_apps_view_pageLayoutWidthGap">36dp</dimen>
+    <dimen name="all_apps_view_pageLayoutHeightGap">36dp</dimen>
+    <dimen name="all_apps_view_pageLayoutPaddingTop">25dp</dimen>
+    <dimen name="all_apps_view_pageLayoutPaddingBottom">10dp</dimen>
+    <dimen name="all_apps_view_pageLayoutPaddingLeft">20dp</dimen>
+    <dimen name="all_apps_view_pageLayoutPaddingRight">20dp</dimen>
+
+    <integer name="customization_drawer_contents_wallpaperCellSpanX">3</integer>
+    <integer name="customization_drawer_contents_wallpaperCellCountX">9</integer>
+    <integer name="customization_drawer_contents_widgetCellCountX">9</integer>
+    <integer name="customization_drawer_contents_cellCountX">5</integer>
+    <integer name="customization_drawer_contents_cellCountY">3</integer>
+    <dimen name="customization_drawer_contents_pageLayoutWidthGap">36dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/res/values-xlarge/config.xml b/res/values-xlarge/config.xml
index 56c7bc6..b9880e3 100644
--- a/res/values-xlarge/config.xml
+++ b/res/values-xlarge/config.xml
@@ -53,11 +53,6 @@
          Should be an even number, for pixel alignment. -->
     <integer name="config_dragViewExtraPixels">0</integer>
 
-    <!-- When dragging items on the workspace, the number of pixels by which the position of
-         the drag view should be offset from the position of the original view. -->
-    <integer name="config_dragViewOffsetX">0</integer>
-    <integer name="config_dragViewOffsetY">-12</integer>
-
     <!-- When items are dropped on the mini screens in customize mode, we have a bounce animation
          of the bright green hover outline, and then fade out the outline at the end. These are
          the values used in that animation -->
diff --git a/res/values-xlarge/dimens.xml b/res/values-xlarge/dimens.xml
index 421c9e3..fffa0f2 100644
--- a/res/values-xlarge/dimens.xml
+++ b/res/values-xlarge/dimens.xml
@@ -28,6 +28,9 @@
     <!-- Size of icons in workspace -->
     <dimen name="app_icon_size">72dp</dimen>
 
+    <!-- Size of content of icons in workspace, as specified by the android icon guidelines -->
+    <dimen name="app_icon_content_size">60dp</dimen>
+
     <!-- extra horizontal spacing between mini screen thumbnails ie. in all
          apps and in customization mode -->
     <dimen name="smallScreenExtraSpacing">0dip</dimen>
@@ -59,4 +62,9 @@
     <!-- How much the content view of an alert dialog should be inset (currently used
         for the WallpaperChooser in XLarge mode) -->
     <dimen name="alert_dialog_content_inset">0dp</dimen>
+
+    <!-- When dragging items on the workspace, the number of dps by which the position of
+     the drag view should be offset from the position of the original view. -->
+    <dimen name="dragViewOffsetX">0dp</dimen>
+    <dimen name="dragViewOffsetY">-12dp</dimen>
 </resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index a3dc212..98b991f 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"主屏幕"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"删除"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"卸载"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"卸载更新"</string>
     <string name="menu_add" msgid="3065046628354640854">"添加"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"管理应用程序"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 2274274..d9f61fb 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -67,6 +67,16 @@
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"主螢幕"</string>
     <string name="delete_zone_label_workspace" msgid="7153615831493049150">"移除"</string>
     <string name="delete_zone_label_all_apps" msgid="6664588234817475108">"解除安裝"</string>
+    <!-- no translation found for accessibility_search_button (816822994629942611) -->
+    <skip />
+    <!-- no translation found for accessibility_voice_search_button (3938249215065842475) -->
+    <skip />
+    <!-- no translation found for accessibility_all_apps_button (1595097919145716305) -->
+    <skip />
+    <!-- no translation found for accessibility_customize_button (585539669413531163) -->
+    <skip />
+    <!-- no translation found for accessibility_delete_button (3628162007991023603) -->
+    <skip />
     <string name="delete_zone_label_all_apps_system_app" msgid="3683920959591819044">"解除安裝更新"</string>
     <string name="menu_add" msgid="3065046628354640854">"新增"</string>
     <string name="menu_manage_apps" msgid="2308685199463588895">"管理應用程式"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 2be5999..4f44253 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -104,6 +104,7 @@
         <attr name="pageLayoutPaddingBottom" format="dimension" />
         <attr name="pageLayoutPaddingLeft" format="dimension" />
         <attr name="pageLayoutPaddingRight" format="dimension" />
+        <attr name="pageLayoutMaxHeight" format="dimension" />
         <!-- The space between adjacent pages of the PagedView. -->
         <attr name="pageSpacing" format="dimension" />
     </declare-styleable>
diff --git a/res/values/config.xml b/res/values/config.xml
index c56a8ce..ec01fa8 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -12,12 +12,6 @@
           Should be an even number, for pixel alignment. -->
     <integer name="config_dragViewExtraPixels">40</integer>
 
-    <!-- When dragging items on the workspace, the number of pixels by which the position of
-         the drag view should be offset from the position of the original view.
-         Setting to 1/2 of config_dragViewExtraPixels keeps it centered on its old position. -->
-    <integer name="config_dragViewOffsetX">20</integer>
-    <integer name="config_dragViewOffsetY">20</integer>
-
     <!-- The duration (in ms) of the fade animation on the object outlines, used when
          we are dragging objects around on the home screen. -->
     <integer name="config_dragOutlineFadeTime">900</integer>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index d6cd3ee..e346c6a 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -44,4 +44,9 @@
     <!-- 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 items on the workspace, the number of dps by which the position of
+     the drag view should be offset from the position of the original view. -->
+    <dimen name="dragViewOffsetX">0dp</dimen>
+    <dimen name="dragViewOffsetY">-8dp</dimen>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index eedbd6a..9ad3e24 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -151,6 +151,17 @@
          device. [CHAR_LIMIT=30]-->
     <string name="delete_zone_label_all_apps">Uninstall</string>
 
+    <!-- Accessibility: Search button -->
+    <string name="accessibility_search_button">Search</string>
+    <!-- Accessibility: Voice Search button -->
+    <string name="accessibility_voice_search_button">Voice Search</string>
+    <!-- Accessibility: AllApps button -->
+    <string name="accessibility_all_apps_button">Applications</string>
+    <!-- Accessibility: Customize button -->
+    <string name="accessibility_customize_button">Customize</string>
+    <!-- Accessibility: Delete button -->
+    <string name="accessibility_delete_button">Remove</string>
+
     <!-- Label for trash icon in All Apps, when an updated system app is selected. The update will
          be uninstalled. [CHAR_LIMIT=30] -->
     <string name="delete_zone_label_all_apps_system_app">Uninstall update</string>
diff --git a/src/com/android/launcher2/AccessibleTabView.java b/src/com/android/launcher2/AccessibleTabView.java
new file mode 100644
index 0000000..a419911
--- /dev/null
+++ b/src/com/android/launcher2/AccessibleTabView.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+package com.android.launcher2;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.widget.TextView;
+
+public class AccessibleTabView extends TextView {
+    public AccessibleTabView(Context context) {
+        super(context);
+    }
+
+    public AccessibleTabView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public AccessibleTabView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        return FocusHelper.handleTabKeyEvent(this, keyCode, event)
+                || super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        return FocusHelper.handleTabKeyEvent(this, keyCode, event)
+                || super.onKeyUp(keyCode, event);
+    }
+}
diff --git a/src/com/android/launcher2/AllApps2D.java b/src/com/android/launcher2/AllApps2D.java
index 1cbb999..854c1ee 100644
--- a/src/com/android/launcher2/AllApps2D.java
+++ b/src/com/android/launcher2/AllApps2D.java
@@ -143,7 +143,7 @@
                 homeButton.setOnClickListener(
                     new View.OnClickListener() {
                         public void onClick(View v) {
-                            mLauncher.closeAllApps(true);
+                            mLauncher.showWorkspace(true);
                         }
                     });
             }
@@ -167,7 +167,7 @@
 
         switch (keyCode) {
             case KeyEvent.KEYCODE_BACK:
-                mLauncher.closeAllApps(true);
+                mLauncher.showWorkspace(true);
                 break;
             default:
                 return false;
@@ -190,7 +190,7 @@
         app = new ApplicationInfo(app);
 
         mDragController.startDrag(view, this, app, DragController.DRAG_ACTION_COPY);
-        mLauncher.closeAllApps(true);
+        mLauncher.showWorkspace(true);
 
         return true;
     }
@@ -341,6 +341,10 @@
     
     public void surrender() {
     }
+
+    public void reset() {
+        // Do nothing
+    }
 }
 
 
diff --git a/src/com/android/launcher2/AllApps3D.java b/src/com/android/launcher2/AllApps3D.java
index 29f49af..a8aea18 100644
--- a/src/com/android/launcher2/AllApps3D.java
+++ b/src/com/android/launcher2/AllApps3D.java
@@ -350,7 +350,7 @@
             if (mArrowNavigation) {
                 if (mLastSelection == SELECTION_HOME) {
                     reallyPlaySoundEffect(SoundEffectConstants.CLICK);
-                    mLauncher.closeAllApps(true);
+                    mLauncher.showWorkspace(true);
                 } else {
                     int whichApp = sRollo.mScript.get_gSelectedIconIndex();
                     if (whichApp >= 0) {
@@ -637,7 +637,7 @@
                     if ((isPortrait && y > mTouchYBorders[mTouchYBorders.length-1]) ||
                         (!isPortrait && x > mTouchXBorders[mTouchXBorders.length-1])) {
                         reallyPlaySoundEffect(SoundEffectConstants.CLICK);
-                        mLauncher.closeAllApps(true);
+                        mLauncher.showWorkspace(true);
                     }
                     sRollo.setHomeSelected(SELECTED_NONE);
                 }
@@ -691,10 +691,12 @@
             int screenX = mMotionDownRawX - (bmp.getWidth() / 2);
             int screenY = mMotionDownRawY - bmp.getHeight();
 
+            mLauncher.lockScreenOrientation();
+            mLauncher.getWorkspace().onDragStartedWithItemSpans(1, 1, bmp);
             mDragController.startDrag(
                     bmp, screenX, screenY, this, app, DragController.DRAG_ACTION_COPY);
 
-            mLauncher.closeAllApps(true);
+            mLauncher.showWorkspace(true);
         }
         return true;
     }
@@ -747,6 +749,8 @@
 
     @Override
     public void onDropCompleted(View target, Object dragInfo, boolean success) {
+        mLauncher.getWorkspace().onDragStopped(success);
+        mLauncher.unlockScreenOrientation();
     }
 
     /**
@@ -1466,4 +1470,8 @@
             sRS.contextDump();
         }
     }
+
+    public void reset() {
+        // Do nothing
+    }
 }
diff --git a/src/com/android/launcher2/AllAppsPagedView.java b/src/com/android/launcher2/AllAppsPagedView.java
index b9b38c3..3d3c1ff 100644
--- a/src/com/android/launcher2/AllAppsPagedView.java
+++ b/src/com/android/launcher2/AllAppsPagedView.java
@@ -24,6 +24,7 @@
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
@@ -65,6 +66,8 @@
     private final LayoutInflater mInflater;
     private boolean mAllowHardwareLayerCreation;
 
+    private int mPageContentWidth;
+    private boolean mHasMadeSuccessfulDrop;
 
     public AllAppsPagedView(Context context) {
         this(context, null);
@@ -88,6 +91,11 @@
         Resources r = context.getResources();
         setDragSlopeThreshold(
                 r.getInteger(R.integer.config_allAppsDrawerDragSlopeThreshold) / 100.0f);
+
+        // Create a dummy page and set it up to find out the content width (used by our parent)
+        PagedViewCellLayout layout = new PagedViewCellLayout(getContext());
+        setupPage(layout);
+        mPageContentWidth = layout.getContentWidth();
     }
 
     @Override
@@ -132,6 +140,10 @@
         }
     }
 
+    void resetSuccessfulDropFlag() {
+        mHasMadeSuccessfulDrop = false;
+    }
+
     @Override
     public void zoom(float zoom, boolean animate) {
         mZoom = zoom;
@@ -279,6 +291,16 @@
         c.translate((v.getWidth() - icon.getIntrinsicWidth()) / 2, v.getPaddingTop());
         icon.draw(c);
 
+        Rect dragRect = null;
+        if (v instanceof TextView) {
+            int iconSize = getResources().getDimensionPixelSize(R.dimen.app_icon_size);
+            int top = v.getPaddingTop();
+            int left = (b.getWidth() - iconSize) / 2;
+            int right = left + iconSize;
+            int bottom = top + iconSize;
+            dragRect = new Rect(left, top, right, bottom);
+        }
+
         // We toggle the checked state _after_ we create the view for the drag in case toggling the
         // checked state changes the view's look
         if (v instanceof Checkable) {
@@ -298,7 +320,7 @@
         // Start the drag
         mLauncher.lockScreenOrientation();
         mLauncher.getWorkspace().onDragStartedWithItemSpans(1, 1, b);
-        mDragController.startDrag(v, b, this, app, DragController.DRAG_ACTION_COPY, null);
+        mDragController.startDrag(v, b, this, app, DragController.DRAG_ACTION_COPY, dragRect);
         b.recycle();
         return true;
     }
@@ -316,6 +338,16 @@
         tearDownDragMode();
         mLauncher.getWorkspace().onDragStopped(success);
         mLauncher.unlockScreenOrientation();
+
+        if (!success && !mHasMadeSuccessfulDrop) {
+            mLauncher.getWorkspace().shrink(Workspace.ShrinkState.BOTTOM_HIDDEN);
+        } else {
+            mHasMadeSuccessfulDrop |= success;
+        }
+    }
+
+    int getPageContentWidth() {
+        return mPageContentWidth;
     }
 
     @Override
@@ -440,6 +472,18 @@
         // do nothing?
     }
 
+    public void reset() {
+        setCurrentPage(0);
+        invalidatePageData();
+    }
+
+    private void setupPage(PagedViewCellLayout layout) {
+        layout.setCellCount(mCellCountX, mCellCountY);
+        layout.setPadding(mPageLayoutPaddingLeft, mPageLayoutPaddingTop, mPageLayoutPaddingRight,
+                mPageLayoutPaddingBottom);
+        layout.setGap(mPageLayoutWidthGap, mPageLayoutHeightGap);
+    }
+
     @Override
     public void syncPages() {
         // ensure that we have the right number of pages (min of 1, since we have placeholders)
@@ -449,7 +493,6 @@
         // remove any extra pages after the "last" page
         int extraPageDiff = curNumPages - numPages;
         for (int i = 0; i < extraPageDiff; ++i) {
-            PagedViewCellLayout page = (PagedViewCellLayout) getChildAt(numPages);
             removeViewAt(numPages);
         }
         // add any necessary pages
@@ -458,10 +501,7 @@
             if (mAllowHardwareLayerCreation) {
                 layout.allowHardwareLayerCreation();
             }
-            layout.setCellCount(mCellCountX, mCellCountY);
-            layout.setPadding(mPageLayoutPaddingLeft, mPageLayoutPaddingTop,
-                    mPageLayoutPaddingRight, mPageLayoutPaddingBottom);
-            layout.setGap(mPageLayoutWidthGap, mPageLayoutHeightGap);
+            setupPage(layout);
             addView(layout);
         }
 
diff --git a/src/com/android/launcher2/AllAppsTabbed.java b/src/com/android/launcher2/AllAppsTabbed.java
index 0dd56ac..2deec8e 100644
--- a/src/com/android/launcher2/AllAppsTabbed.java
+++ b/src/com/android/launcher2/AllAppsTabbed.java
@@ -33,10 +33,9 @@
 import android.widget.TabHost;
 import android.widget.TabWidget;
 import android.widget.TextView;
-import android.widget.TabHost.OnTabChangeListener;
-import android.widget.TabHost.TabContentFactory;
 
 import java.util.ArrayList;
+import java.util.Random;
 
 /**
  * Implements a tabbed version of AllApps2D.
@@ -82,6 +81,8 @@
             }
         };
 
+        // Create the tabs and wire them up properly
+        AllAppsTabKeyEventListener keyListener = new AllAppsTabKeyEventListener();
         TextView tabView;
         TabWidget tabWidget = (TabWidget) findViewById(com.android.internal.R.id.tabs);
         tabView = (TextView) mInflater.inflate(R.layout.tab_widget_indicator, tabWidget, false);
@@ -92,6 +93,12 @@
         tabView.setText(mContext.getString(R.string.all_apps_tab_downloaded));
         addTab(newTabSpec(TAG_DOWNLOADED).setIndicator(tabView).setContent(contentFactory));
 
+        // Setup the key listener to jump between the last tab view and the market icon
+        View lastTab = tabWidget.getChildTabViewAt(tabWidget.getTabCount() - 1);
+        lastTab.setOnKeyListener(keyListener);
+        View shopButton = findViewById(R.id.market_button);
+        shopButton.setOnKeyListener(keyListener);
+
         setOnTabChangedListener(new OnTabChangeListener() {
             public void onTabChanged(String tabId) {
                 // animate the changing of the tab content by fading pages in and out
@@ -119,6 +126,17 @@
             }
         });
 
+        // Set the width of the tab bar properly
+        int pageWidth = mAllApps.getPageContentWidth();
+        View allAppsTabBar = (View) findViewById(R.id.all_apps_tab_bar);
+        if (allAppsTabBar == null) throw new Resources.NotFoundException();
+        int tabWidgetPadding = 0;
+        final int childCount = tabWidget.getChildCount();
+        if (childCount > 0) {
+            tabWidgetPadding += tabWidget.getChildAt(0).getPaddingLeft() * 2;
+        }
+        allAppsTabBar.getLayoutParams().width = pageWidth + tabWidgetPadding;
+
         // It needs to be INVISIBLE so that it will be measured in the layout.
         // Otherwise the animations is messed up when we show it for the first time.
         setVisibility(INVISIBLE);
@@ -239,6 +257,10 @@
         mAllApps.surrender();
     }
 
+    public void reset() {
+        mAllApps.reset();
+    }
+
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         if (ev.getY() > mAllApps.getBottom()) {
@@ -246,4 +268,12 @@
         }
         return true;
     }
+
+    @Override
+    public int getDescendantFocusability() {
+        if (getVisibility() != View.VISIBLE) {
+            return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
+        }
+        return super.getDescendantFocusability();
+    }
 }
diff --git a/src/com/android/launcher2/AllAppsView.java b/src/com/android/launcher2/AllAppsView.java
index 007ecf8..3a74c71 100644
--- a/src/com/android/launcher2/AllAppsView.java
+++ b/src/com/android/launcher2/AllAppsView.java
@@ -41,6 +41,9 @@
 
     public void updateApps(ArrayList<ApplicationInfo> list);
     
+    // Resets the AllApps page to the front
+    public void reset();
+
     public void dumpState();
 
     public void surrender();
diff --git a/src/com/android/launcher2/AppWidgetResizeFrame.java b/src/com/android/launcher2/AppWidgetResizeFrame.java
index 2b2662f..d975ed9 100644
--- a/src/com/android/launcher2/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher2/AppWidgetResizeFrame.java
@@ -18,6 +18,8 @@
     private ItemInfo mItemInfo;
     private LauncherAppWidgetHostView mWidgetView;
     private CellLayout mCellLayout;
+    private DragLayer mDragLayer;
+    private Workspace mWorkspace;
     private ImageView mLeftHandle;
     private ImageView mRightHandle;
     private ImageView mTopHandle;
@@ -57,7 +59,7 @@
     public static final int BOTTOM = 3;
 
     public AppWidgetResizeFrame(Context context, ItemInfo itemInfo, 
-            LauncherAppWidgetHostView widgetView, CellLayout cellLayout) {
+            LauncherAppWidgetHostView widgetView, CellLayout cellLayout, DragLayer dragLayer) {
 
         super(context);
         mContext = context;
@@ -65,6 +67,8 @@
         mCellLayout = cellLayout;
         mWidgetView = widgetView;
         mResizeMode = widgetView.getAppWidgetInfo().resizeMode;
+        mDragLayer = dragLayer;
+        mWorkspace = (Workspace) dragLayer.findViewById(R.id.workspace);
 
         final AppWidgetProviderInfo info = widgetView.getAppWidgetInfo();
         int[] result = mCellLayout.rectToCell(info.minWidth, info.minHeight, null);
@@ -150,7 +154,7 @@
             mDeltaX = Math.max(-mBaselineX, deltaX); 
             mDeltaX = Math.min(mBaselineWidth - 2 * mTouchTargetWidth, mDeltaX);
         } else if (mRightBorderActive) {
-            mDeltaX = Math.min(mCellLayout.getWidth() - (mBaselineX + mBaselineWidth), deltaX);
+            mDeltaX = Math.min(mDragLayer.getWidth() - (mBaselineX + mBaselineWidth), deltaX);
             mDeltaX = Math.max(-mBaselineWidth + 2 * mTouchTargetWidth, mDeltaX);
         }
 
@@ -158,7 +162,7 @@
             mDeltaY = Math.max(-mBaselineY, deltaY);
             mDeltaY = Math.min(mBaselineHeight - 2 * mTouchTargetWidth, mDeltaY);
         } else if (mBottomBorderActive) {
-            mDeltaY = Math.min(mCellLayout.getHeight() - (mBaselineY + mBaselineHeight), deltaY);
+            mDeltaY = Math.min(mDragLayer.getHeight() - (mBaselineY + mBaselineHeight), deltaY);
             mDeltaY = Math.max(-mBaselineHeight + 2 * mTouchTargetWidth, mDeltaY);
         }
     }
@@ -168,7 +172,8 @@
      */
     public void visualizeResizeForDelta(int deltaX, int deltaY) {
         updateDeltas(deltaX, deltaY);
-        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
+        DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
+
         if (mLeftBorderActive) {
             lp.x = mBaselineX + mDeltaX;
             lp.width = mBaselineWidth - mDeltaX;
@@ -261,6 +266,7 @@
 
         // Update the cells occupied by this widget
         mCellLayout.markCellsAsOccupiedForView(mWidgetView);
+        mWidgetView.requestLayout();
     }
 
     /**
@@ -284,20 +290,22 @@
     }
 
     public void snapToWidget(boolean animate) {
-        final CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
+        final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
+        int xOffset = mCellLayout.getLeft() - mWorkspace.getScrollX();
+        int yOffset = mCellLayout.getTop() - mWorkspace.getScrollY();
 
         int newWidth = mWidgetView.getWidth() + 2 * mBackgroundPadding;
         int newHeight = mWidgetView.getHeight() + 2 * mBackgroundPadding;
-        int newX = mWidgetView.getLeft() - mBackgroundPadding;
-        int newY = mWidgetView.getTop() - mBackgroundPadding;
+        int newX = mWidgetView.getLeft() - mBackgroundPadding + xOffset;
+        int newY = mWidgetView.getTop() - mBackgroundPadding + yOffset;
 
         // We need to make sure the frame stays within the bounds of the CellLayout
         if (newY < 0) {
             newHeight -= -newY;
             newY = 0;
         }
-        if (newY + newHeight > mCellLayout.getHeight()) {
-            newHeight -= newY + newHeight - mCellLayout.getHeight();
+        if (newY + newHeight > mDragLayer.getHeight()) {
+            newHeight -= newY + newHeight - mDragLayer.getHeight();
         }
 
         if (!animate) {
diff --git a/src/com/android/launcher2/BubbleTextView.java b/src/com/android/launcher2/BubbleTextView.java
index 1464854..703b3a8 100644
--- a/src/com/android/launcher2/BubbleTextView.java
+++ b/src/com/android/launcher2/BubbleTextView.java
@@ -29,6 +29,7 @@
 import android.graphics.Region.Op;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 import android.widget.TextView;
@@ -87,8 +88,6 @@
 
     private void init() {
         mBackground = getBackground();
-        setFocusable(true);
-        setBackgroundDrawable(null);
 
         final Resources res = getContext().getResources();
         int bubbleColor = res.getColor(R.color.bubble_dark_background);
@@ -330,4 +329,16 @@
         }
         return true;
     }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        return FocusHelper.handleBubbleTextViewKeyEvent(this, keyCode, event)
+                || super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        return FocusHelper.handleBubbleTextViewKeyEvent(this, keyCode, event)
+                || super.onKeyUp(keyCode, event);
+    }
 }
diff --git a/src/com/android/launcher2/CachedTextView.java b/src/com/android/launcher2/CachedTextView.java
index 403d856..d0f6dd8 100644
--- a/src/com/android/launcher2/CachedTextView.java
+++ b/src/com/android/launcher2/CachedTextView.java
@@ -18,10 +18,11 @@
 
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
 import android.graphics.Canvas;
 import android.graphics.Paint;
-import android.graphics.Bitmap.Config;
 import android.graphics.PorterDuff.Mode;
+import android.graphics.drawable.Drawable;
 import android.text.Layout;
 import android.util.AttributeSet;
 import android.widget.TextView;
@@ -163,6 +164,16 @@
         if (mPrevAlpha != alpha) {
             mPrevAlpha = alpha;
             mCachePaint.setAlpha(alpha);
+
+            // We manually update the drawables alpha since the default TextView implementation may
+            // not do this if there is a background set (which we may due to the focus bg)
+            final Drawable[] dr = getCompoundDrawables();
+            for (int i = 0; i < dr.length; ++i) {
+                if (dr[i] != null) {
+                    dr[i].mutate().setAlpha(alpha);
+                }
+            }
+
             super.onSetAlpha(alpha);
         }
         return true;
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index 1111c53..636b041 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -95,7 +95,7 @@
     private float mGlowBackgroundScale;
     private float mGlowBackgroundAlpha;
 
-    private boolean mAcceptsDrops = false;
+    private boolean mAcceptsDrops = true;
     // If we're actively dragging something over this screen, mIsDragOverlapping is true
     private boolean mIsDragOverlapping = false;
     private boolean mIsDragOccuring = false;
@@ -736,6 +736,22 @@
         result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap);
     }
 
+    /**
+     * Given a cell coordinate, return the point that represents the upper left corner of that cell
+     *
+     * @param cellX X coordinate of the cell
+     * @param cellY Y coordinate of the cell
+     *
+     * @param result Array of 2 ints to hold the x and y coordinate of the point
+     */
+    void cellToCenterPoint(int cellX, int cellY, int[] result) {
+        final int hStartPadding = getLeftPadding();
+        final int vStartPadding = getTopPadding();
+
+        result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap) + mCellWidth / 2;
+        result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap) + mCellHeight / 2;
+    }
+
     int getCellWidth() {
         return mCellWidth;
     }
@@ -956,13 +972,15 @@
             int top = topLeft[1];
 
             if (v != null) {
-                if (v.getParent() instanceof CellLayout) {
-                    LayoutParams lp = (LayoutParams) v.getLayoutParams();
-                    left += lp.leftMargin;
-                    top += lp.topMargin;
-                }
+                // When drawing the drag outline, it did not account for margin offsets
+                // added by the view's parent.
+                MarginLayoutParams lp = (MarginLayoutParams) v.getLayoutParams();
+                left += lp.leftMargin;
+                top += lp.topMargin;
 
-                // Offsets due to the size difference between the View and the dragOutline
+                // Offsets due to the size difference between the View and the dragOutline.
+                // There is a size difference to account for the outer blur, which may lie
+                // outside the bounds of the view.
                 left += (v.getWidth() - dragOutline.getWidth()) / 2;
                 top += (v.getHeight() - dragOutline.getHeight()) / 2;
             }
@@ -1008,16 +1026,23 @@
      * @param pixelY The Y location at which you want to search for a vacant area.
      * @param spanX Horizontal span of the object.
      * @param spanY Vertical span of the object.
-     * @param ignoreView Considers space occupied by this view as unoccupied
-     * @param result Previously returned value to possibly recycle.
+     * @param ignoreOccupied If true, the result can be an occupied cell
+     * @param result Array in which to place the result, or null (in which case a new array will
+     *        be allocated)
      * @return The X, Y cell of a vacant area that can contain this object,
      *         nearest the requested location.
      */
-    int[] findNearestVacantArea(
-            int pixelX, int pixelY, int spanX, int spanY, View ignoreView, int[] result) {
+    int[] findNearestArea(int pixelX, int pixelY, int spanX, int spanY, View ignoreView,
+            boolean ignoreOccupied, int[] result) {
         // mark space take by ignoreView as available (method checks if ignoreView is null)
         markCellsAsUnoccupiedForView(ignoreView);
 
+        // For items with a spanX / spanY > 1, the passed in point (pixelX, pixelY) corresponds
+        // to the center of the item, but we are searching based on the top-left cell, so
+        // we translate the point over to correspond to the top-left.
+        pixelX -= (mCellWidth + mWidthGap) * (spanX - 1) / 2f;
+        pixelY -= (mCellHeight + mHeightGap) * (spanY - 1) / 2f;
+
         // Keep track of best-scoring drop area
         final int[] bestXY = result != null ? result : new int[2];
         double bestDistance = Double.MAX_VALUE;
@@ -1029,18 +1054,20 @@
         for (int y = 0; y < countY - (spanY - 1); y++) {
             inner:
             for (int x = 0; x < countX - (spanX - 1); x++) {
-                for (int i = 0; i < spanX; i++) {
-                    for (int j = 0; j < spanY; j++) {
-                        if (occupied[x + i][y + j]) {
-                            // small optimization: we can skip to after the column we just found
-                            // an occupied cell
-                            x += i;
-                            continue inner;
+                if (ignoreOccupied) {
+                    for (int i = 0; i < spanX; i++) {
+                        for (int j = 0; j < spanY; j++) {
+                            if (occupied[x + i][y + j]) {
+                                // small optimization: we can skip to after the column we
+                                // just found an occupied cell
+                                x += i;
+                                continue inner;
+                            }
                         }
                     }
                 }
                 final int[] cellXY = mTmpCellXY;
-                cellToPoint(x, y, cellXY);
+                cellToCenterPoint(x, y, cellXY);
 
                 double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2)
                         + Math.pow(cellXY[1] - pixelY, 2));
@@ -1062,6 +1089,42 @@
         }
     }
 
+    /**
+     * Find a vacant area that will fit the given bounds nearest the requested
+     * cell location. Uses Euclidean distance to score multiple vacant areas.
+     *
+     * @param pixelX The X location at which you want to search for a vacant area.
+     * @param pixelY The Y location at which you want to search for a vacant area.
+     * @param spanX Horizontal span of the object.
+     * @param spanY Vertical span of the object.
+     * @param ignoreView Considers space occupied by this view as unoccupied
+     * @param result Previously returned value to possibly recycle.
+     * @return The X, Y cell of a vacant area that can contain this object,
+     *         nearest the requested location.
+     */
+    int[] findNearestVacantArea(
+            int pixelX, int pixelY, int spanX, int spanY, View ignoreView, int[] result) {
+        return findNearestArea(pixelX, pixelY, spanX, spanY, ignoreView, true, result);
+    }
+
+    /**
+     * Find a starting cell position that will fit the given bounds nearest the requested
+     * cell location. Uses Euclidean distance to score multiple vacant areas.
+     *
+     * @param pixelX The X location at which you want to search for a vacant area.
+     * @param pixelY The Y location at which you want to search for a vacant area.
+     * @param spanX Horizontal span of the object.
+     * @param spanY Vertical span of the object.
+     * @param ignoreView Considers space occupied by this view as unoccupied
+     * @param result Previously returned value to possibly recycle.
+     * @return The X, Y cell of a vacant area that can contain this object,
+     *         nearest the requested location.
+     */
+    int[] findNearestArea(
+            int pixelX, int pixelY, int spanX, int spanY, int[] result) {
+        return findNearestArea(pixelX, pixelY, spanX, spanY, null, false, result);
+    }
+
     boolean existsEmptyCell() {
         return findCellForSpan(null, 1, 1);
     }
@@ -1457,6 +1520,14 @@
         }
     }
 
+    public boolean isOccupied(int x, int y) {
+        if (x < mCountX && y < mCountY) {
+            return mOccupied[x][y];
+        } else {
+            throw new RuntimeException("Position exceeds the bound of this CellLayout");
+        }
+    }
+
     @Override
     public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
         return new CellLayout.LayoutParams(getContext(), attrs);
@@ -1587,38 +1658,6 @@
             }
         }
 
-        public void setWidth(int width) {
-            this.width = width;
-        }
-
-        public int getWidth() {
-            return width;
-        }
-
-        public void setHeight(int height) {
-            this.height = height;
-        }
-
-        public int getHeight() {
-            return height;
-        }
-
-        public void setX(int x) {
-            this.x = x;
-        }
-
-        public int getX() {
-            return x;
-        }
-
-        public void setY(int y) {
-            this.y = y;
-        }
-
-        public int getY() {
-            return y;
-        }
-
         public String toString() {
             return "(" + this.cellX + ", " + this.cellY + ")";
         }
@@ -1630,7 +1669,7 @@
     // 2. When long clicking on an empty cell in a CellLayout, we save information about the
     //    cellX and cellY coordinates and which page was clicked. We then set this as a tag on
     //    the CellLayout that was long clicked
-    static final class CellInfo implements ContextMenu.ContextMenuInfo {
+    static final class CellInfo {
         View cell;
         int cellX = -1;
         int cellY = -1;
diff --git a/src/com/android/launcher2/CellLayoutChildren.java b/src/com/android/launcher2/CellLayoutChildren.java
index 04996f3..e4e6fb1 100644
--- a/src/com/android/launcher2/CellLayoutChildren.java
+++ b/src/com/android/launcher2/CellLayoutChildren.java
@@ -43,12 +43,6 @@
     private int mWidthGap;
     private int mHeightGap;
 
-    // Variables relating to resizing widgets
-    private final ArrayList<AppWidgetResizeFrame> mResizeFrames =
-            new ArrayList<AppWidgetResizeFrame>();
-    private AppWidgetResizeFrame mCurrentResizeFrame;
-    private int mXDown, mYDown;
-
     public CellLayoutChildren(Context context) {
         super(context);
         mWallpaperManager = WallpaperManager.getInstance(context);
@@ -81,27 +75,31 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        final int cellWidth = mCellWidth;
-        final int cellHeight = mCellHeight;
         int count = getChildCount();
         for (int i = 0; i < count; i++) {
             View child = getChildAt(i);
-            CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
-
-            lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap,
-                    mLeftPadding, mTopPadding);
-
-            int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
-            int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height,
-                    MeasureSpec.EXACTLY);
-
-            child.measure(childWidthMeasureSpec, childheightMeasureSpec);
+            measureChild(child);
         }
         int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
         int heightSpecSize =  MeasureSpec.getSize(heightMeasureSpec);
         setMeasuredDimension(widthSpecSize, heightSpecSize);
     }
 
+    public void measureChild(View child) {
+        final int cellWidth = mCellWidth;
+        final int cellHeight = mCellHeight;
+        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+
+        lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap,
+                mLeftPadding, mTopPadding);
+
+        int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
+        int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height,
+                MeasureSpec.EXACTLY);
+
+        child.measure(childWidthMeasureSpec, childheightMeasureSpec);
+    }
+
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         int count = getChildCount();
@@ -176,95 +174,4 @@
     protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
         super.setChildrenDrawnWithCacheEnabled(enabled);
     }
-
-    public void clearAllResizeFrames() {
-        for (AppWidgetResizeFrame frame: mResizeFrames) {
-            removeView(frame);
-        }
-        mResizeFrames.clear();
-    }
-
-    public boolean hasResizeFrames() {
-        return mResizeFrames.size() > 0;
-    }
-
-    public boolean isWidgetBeingResized() {
-        return mCurrentResizeFrame != null;
-    }
-
-    private boolean handleTouchDown(MotionEvent ev) {
-        Rect hitRect = new Rect();
-
-        int x = (int) ev.getX();
-        int y = (int) ev.getY();
-
-        for (AppWidgetResizeFrame child: mResizeFrames) {
-            child.getHitRect(hitRect);
-            if (hitRect.contains(x, y)) {
-                if (child.beginResizeIfPointInRegion(x - child.getLeft(), y - child.getTop())) {
-                    mCurrentResizeFrame = child;
-                    mXDown = x;
-                    mYDown = y;
-                    requestDisallowInterceptTouchEvent(true);
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            if (handleTouchDown(ev)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        boolean handled = false;
-        int action = ev.getAction();
-
-        int x = (int) ev.getX();
-        int y = (int) ev.getY();
-
-        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-                if (handleTouchDown(ev)) {
-                    return true;
-                }
-            }
-        }
-
-        if (mCurrentResizeFrame != null) {
-            handled = true;
-            switch (action) {
-                case MotionEvent.ACTION_MOVE:
-                    mCurrentResizeFrame.visualizeResizeForDelta(x - mXDown, y - mYDown);
-                    break;
-                case MotionEvent.ACTION_CANCEL:
-                case MotionEvent.ACTION_UP:
-                    mCurrentResizeFrame.commitResizeForDelta(x - mXDown, y - mYDown);
-                    mCurrentResizeFrame = null;
-            }
-        }
-        return handled;
-    }
-
-    public void addResizeFrame(ItemInfo itemInfo, LauncherAppWidgetHostView widget,
-            CellLayout cellLayout) {
-        AppWidgetResizeFrame resizeFrame = new AppWidgetResizeFrame(getContext(),
-                itemInfo, widget, cellLayout);
-
-        CellLayout.LayoutParams lp = new CellLayout.LayoutParams(-1, -1, -1, -1);
-        lp.isLockedToGrid = false;
-
-        addView(resizeFrame, lp);
-        mResizeFrames.add(resizeFrame);
-
-        resizeFrame.snapToWidget(false);
-    }
 }
diff --git a/src/com/android/launcher2/CustomizePagedView.java b/src/com/android/launcher2/CustomizePagedView.java
index 5c61b0b..ca60361 100644
--- a/src/com/android/launcher2/CustomizePagedView.java
+++ b/src/com/android/launcher2/CustomizePagedView.java
@@ -140,6 +140,8 @@
 
     private int[] mDragViewOrigin = new int[2];
 
+    private int mPageContentWidth;
+
     public CustomizePagedView(Context context) {
         this(context, null, 0);
     }
@@ -170,7 +172,12 @@
         final Resources r = context.getResources();
         setDragSlopeThreshold(
                 r.getInteger(R.integer.config_customizationDrawerDragSlopeThreshold) / 100.0f);
-        mMinPageWidth = r.getDimensionPixelSize(R.dimen.customization_drawer_content_min_width);
+
+        // Create a dummy page and set it up to find out the content width (used by our parent)
+        PagedViewCellLayout layout = new PagedViewCellLayout(getContext());
+        setupPage(layout);
+        mPageContentWidth = layout.getContentWidth();
+        mMinPageWidth = layout.getWidthBeforeFirstLayout();
 
         setVisibility(View.GONE);
         setSoundEffectsEnabled(false);
@@ -481,8 +488,6 @@
 
     @Override
     public void onClick(final View v) {
-        // Return early if this is not initiated from a touch
-        if (!v.isInTouchMode()) return;
         // Return early if we are still animating the pages
         if (mNextPage != INVALID_PAGE) return;
 
@@ -1128,8 +1133,10 @@
         }
 
         // Set a min page width for PagedView layout if we have more than a single page
-        if (enforceMinimumPagedWidths) {
-            setMinimumWidthOverride((childCount > 1) ? mMinPageWidth : 0);
+        if (enforceMinimumPagedWidths && childCount > 1) {
+            setMinimumWidthOverride(mMinPageWidth);
+        } else {
+            resetMinimumWidthOverride();
         }
 
         // Bound the current page index
@@ -1161,6 +1168,10 @@
         }
     }
 
+    int getPageContentWidth() {
+        return mPageContentWidth;
+    }
+
     @Override
     protected int getAssociatedLowerPageBound(int page) {
         return 0;
diff --git a/src/com/android/launcher2/CustomizeTrayTabHost.java b/src/com/android/launcher2/CustomizeTrayTabHost.java
index 76cfc84..5c683c4 100644
--- a/src/com/android/launcher2/CustomizeTrayTabHost.java
+++ b/src/com/android/launcher2/CustomizeTrayTabHost.java
@@ -16,20 +16,116 @@
 
 package com.android.launcher2;
 
-import android.animation.Animator;
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.TabHost;
+import java.util.Random;
 
-public class CustomizeTrayTabHost extends TabHost implements LauncherTransitionable {
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TabHost;
+import android.widget.TabWidget;
+import android.widget.TextView;
+
+import com.android.launcher.R;
+import com.android.launcher2.CustomizePagedView.CustomizationType;
+
+public class CustomizeTrayTabHost extends TabHost implements LauncherTransitionable  {
+    // tags for the customization tabs
+    private static final String WIDGETS_TAG = "widgets";
+    private static final String APPLICATIONS_TAG = "applications";
+    private static final String SHORTCUTS_TAG = "shortcuts";
+    private static final String WALLPAPERS_TAG = "wallpapers";
+
     private boolean mFirstLayout = true;
 
-    public CustomizeTrayTabHost(Context context) {
-        super(context);
-    }
+    private final LayoutInflater mInflater;
+    private Context mContext;
 
     public CustomizeTrayTabHost(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mContext = context;
+        mInflater = LayoutInflater.from(context);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        setup();
+
+        final CustomizePagedView customizePagedView =
+            (CustomizePagedView) findViewById(R.id.customization_drawer_tab_contents);
+
+        // Configure tabs
+        TabContentFactory contentFactory = new TabContentFactory() {
+            public View createTabContent(String tag) {
+                return customizePagedView;
+            }
+        };
+
+        TextView tabView;
+        TabWidget tabWidget = (TabWidget) findViewById(com.android.internal.R.id.tabs);
+
+        tabView = (TextView) mInflater.inflate(R.layout.tab_widget_indicator, tabWidget, false);
+        tabView.setText(mContext.getString(R.string.widgets_tab_label));
+        addTab(newTabSpec(WIDGETS_TAG)
+                .setIndicator(tabView).setContent(contentFactory));
+        tabView = (TextView) mInflater.inflate(R.layout.tab_widget_indicator, tabWidget, false);
+        tabView.setText(mContext.getString(R.string.applications_tab_label));
+        addTab(newTabSpec(APPLICATIONS_TAG)
+                .setIndicator(tabView).setContent(contentFactory));
+        tabView = (TextView) mInflater.inflate(R.layout.tab_widget_indicator, tabWidget, false);
+        tabView.setText(mContext.getString(R.string.wallpapers_tab_label));
+        addTab(newTabSpec(WALLPAPERS_TAG)
+                .setIndicator(tabView).setContent(contentFactory));
+        tabView = (TextView) mInflater.inflate(R.layout.tab_widget_indicator, tabWidget, false);
+        tabView.setText(mContext.getString(R.string.shortcuts_tab_label));
+        addTab(newTabSpec(SHORTCUTS_TAG)
+                .setIndicator(tabView).setContent(contentFactory));
+
+        setOnTabChangedListener(new OnTabChangeListener() {
+            public void onTabChanged(String tabId) {
+                final CustomizePagedView.CustomizationType newType =
+                    getCustomizeFilterForTabTag(tabId);
+                if (newType != customizePagedView.getCustomizationFilter()) {
+                    // animate the changing of the tab content by fading pages in and out
+                    final Resources res = getResources();
+                    final int duration = res.getInteger(R.integer.config_tabTransitionTime);
+                    final float alpha = customizePagedView.getAlpha();
+                    ValueAnimator alphaAnim = ObjectAnimator.ofFloat(customizePagedView,
+                            "alpha", alpha, 0.0f);
+                    alphaAnim.setDuration(duration);
+                    alphaAnim.addListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            customizePagedView.setCustomizationFilter(newType);
+
+                            final float alpha = customizePagedView.getAlpha();
+                            ValueAnimator alphaAnim = ObjectAnimator.ofFloat(
+                                    customizePagedView, "alpha", alpha, 1.0f);
+                            alphaAnim.setDuration(duration);
+                            alphaAnim.start();
+                        }
+                    });
+                    alphaAnim.start();
+                }
+            }
+        });
+
+        // Set the width of the tab bar properly
+        int pageWidth = customizePagedView.getPageContentWidth();
+        TabWidget customizeTabBar = (TabWidget) findViewById(com.android.internal.R.id.tabs);
+        if (customizeTabBar == null) throw new Resources.NotFoundException();
+        int tabWidgetPadding = 0;
+        final int childCount = tabWidget.getChildCount();
+        if (childCount > 0) {
+            tabWidgetPadding += tabWidget.getChildAt(0).getPaddingLeft() * 2;
+        }
+        customizeTabBar.getLayoutParams().width = pageWidth + tabWidgetPadding;
     }
 
     @Override
@@ -57,4 +153,25 @@
         mFirstLayout = false;
         super.onLayout(changed, l, t, r, b);
     }
+
+    @Override
+    public int getDescendantFocusability() {
+        if (getVisibility() != View.VISIBLE) {
+            return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
+        }
+        return super.getDescendantFocusability();
+    }
+
+    CustomizationType getCustomizeFilterForTabTag(String tag) {
+        if (tag.equals(WIDGETS_TAG)) {
+            return CustomizationType.WidgetCustomization;
+        } else if (tag.equals(APPLICATIONS_TAG)) {
+            return CustomizationType.ApplicationCustomization;
+        } else if (tag.equals(WALLPAPERS_TAG)) {
+            return CustomizePagedView.CustomizationType.WallpaperCustomization;
+        } else if (tag.equals(SHORTCUTS_TAG)) {
+            return CustomizePagedView.CustomizationType.ShortcutCustomization;
+        }
+        return CustomizationType.WidgetCustomization;
+    }
 }
diff --git a/src/com/android/launcher2/DeleteZone.java b/src/com/android/launcher2/DeleteZone.java
index 98d2b83..6d3bed5 100644
--- a/src/com/android/launcher2/DeleteZone.java
+++ b/src/com/android/launcher2/DeleteZone.java
@@ -16,8 +16,10 @@
 
 package com.android.launcher2;
 
-import com.android.launcher.R;
-
+import android.animation.AnimatorSet;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -29,11 +31,10 @@
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.animation.AccelerateInterpolator;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
 import android.view.animation.TranslateAnimation;
 
+import com.android.launcher.R;
+
 public class DeleteZone extends IconDropTarget {
     private static final int ORIENTATION_HORIZONTAL = 1;
     private static final int TRANSITION_DURATION = 250;
@@ -42,10 +43,8 @@
     private static final int XLARGE_ANIMATION_DURATION = 200;
     private static final int LEFT_DRAWABLE = 0;
 
-    private AnimationSet mInAnimation;
-    private AnimationSet mOutAnimation;
-    private Animation mHandleInAnimation;
-    private Animation mHandleOutAnimation;
+    private AnimatorSet mInAnimation;
+    private AnimatorSet mOutAnimation;
 
     private int mOrientation;
     private DragController mDragController;
@@ -181,10 +180,10 @@
             mTransition.resetTransition();
 
             createAnimations();
-            startAnimation(mInAnimation);
+            mInAnimation.start();
             if (mOverlappingViews != null) {
                 for (View view : mOverlappingViews) {
-                    view.startAnimation(mHandleOutAnimation);
+                    createOutAlphaAnim(view).start();
                 }
             }
             setVisibility(VISIBLE);
@@ -196,68 +195,72 @@
             mActive = false;
             mDragController.setDeleteRegion(null);
 
-            if (mOutAnimation != null) startAnimation(mOutAnimation);
-            if (mHandleInAnimation != null && mOverlappingViews != null) {
+            mOutAnimation.start();
+            if (mOverlappingViews != null) {
                 for (View view : mOverlappingViews) {
-                    view.startAnimation(mHandleInAnimation);
+                    createInAlphaAnim(view).start();
                 }
             }
-            setVisibility(GONE);
         }
     }
 
+    private Animator createAlphaAnim(View v, float start, float end) {
+        Animator anim = ObjectAnimator.ofFloat(v, "alpha", start, end);
+        anim.setDuration(getAnimationDuration());
+        return anim;
+    }
+    private Animator createInAlphaAnim(View v) {
+        return createAlphaAnim(v, 0f, 1f);
+    }
+    private Animator createOutAlphaAnim(View v) {
+        return createAlphaAnim(v, 1f, 0f);
+    }
+
     private void createAnimations() {
         int duration = getAnimationDuration();
-        if (mHandleInAnimation == null) {
-            mHandleInAnimation = new AlphaAnimation(0.0f, 1.0f);
-            mHandleInAnimation.setDuration(duration);
-        }
 
+        Animator inAlphaAnim = createInAlphaAnim(this);
         if (mInAnimation == null) {
-            mInAnimation = new FastAnimationSet();
+            mInAnimation = new AnimatorSet();
+            mInAnimation.setInterpolator(new AccelerateInterpolator());
+            mInAnimation.setDuration(duration);
             if (!LauncherApplication.isScreenXLarge()) {
-                final AnimationSet animationSet = mInAnimation;
-                animationSet.setInterpolator(new AccelerateInterpolator());
-                animationSet.addAnimation(new AlphaAnimation(0.0f, 1.0f));
+                Animator translateAnim;
                 if (mOrientation == ORIENTATION_HORIZONTAL) {
-                    animationSet.addAnimation(new TranslateAnimation(Animation.ABSOLUTE, 0.0f,
-                            Animation.ABSOLUTE, 0.0f, Animation.RELATIVE_TO_SELF, 1.0f,
-                            Animation.RELATIVE_TO_SELF, 0.0f));
+                    translateAnim = ObjectAnimator.ofFloat(this, "translationY", 
+                            getMeasuredWidth(), 0f);
                 } else {
-                    animationSet.addAnimation(new TranslateAnimation(Animation.RELATIVE_TO_SELF,
-                            1.0f, Animation.RELATIVE_TO_SELF, 0.0f, Animation.ABSOLUTE, 0.0f,
-                            Animation.ABSOLUTE, 0.0f));
+                    translateAnim = ObjectAnimator.ofFloat(this, "translationX", 
+                            getMeasuredHeight(), 0f);
                 }
-                animationSet.setDuration(duration);
+                mInAnimation.playTogether(translateAnim, inAlphaAnim);
             } else {
-                mInAnimation.addAnimation(mHandleInAnimation);
+                mInAnimation.play(inAlphaAnim);
             }
         }
 
-        if (mHandleOutAnimation == null) {
-            mHandleOutAnimation = new AlphaAnimation(1.0f, 0.0f);
-            mHandleOutAnimation.setFillAfter(true);
-            mHandleOutAnimation.setDuration(duration);
-        }
-
+        Animator outAlphaAnim = createOutAlphaAnim(this);
         if (mOutAnimation == null) {
-            mOutAnimation = new FastAnimationSet();
+            mOutAnimation = new AnimatorSet();
+            mOutAnimation.setInterpolator(new AccelerateInterpolator());
+            mOutAnimation.setDuration(duration);
             if (!LauncherApplication.isScreenXLarge()) {
-                final AnimationSet animationSet = mOutAnimation;
-                animationSet.setInterpolator(new AccelerateInterpolator());
-                animationSet.addAnimation(new AlphaAnimation(1.0f, 0.0f));
+                Animator translateAnim;
                 if (mOrientation == ORIENTATION_HORIZONTAL) {
-                    animationSet.addAnimation(new FastTranslateAnimation(Animation.ABSOLUTE, 0.0f,
-                            Animation.ABSOLUTE, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f,
-                            Animation.RELATIVE_TO_SELF, 1.0f));
+                    translateAnim = ObjectAnimator.ofFloat(this, "translationY", 0f, 
+                            getMeasuredWidth());
                 } else {
-                    animationSet.addAnimation(new FastTranslateAnimation(Animation.RELATIVE_TO_SELF,
-                            0.0f, Animation.RELATIVE_TO_SELF, 1.0f, Animation.ABSOLUTE, 0.0f,
-                            Animation.ABSOLUTE, 0.0f));
+                    translateAnim = ObjectAnimator.ofFloat(this, "translationX", 0f, 
+                            getMeasuredHeight());
                 }
-                animationSet.setDuration(duration);
+                mOutAnimation.playTogether(translateAnim, outAlphaAnim);
             } else {
-                mOutAnimation.addAnimation(mHandleOutAnimation);
+                mOutAnimation.addListener(new AnimatorListenerAdapter() {
+                    public void onAnimationEnd(Animator animation) {
+                        setVisibility(GONE);
+                    }
+                });
+                mOutAnimation.play(outAlphaAnim);
             }
         }
     }
@@ -275,38 +278,4 @@
         return LauncherApplication.isScreenXLarge() ?
                 XLARGE_ANIMATION_DURATION : ANIMATION_DURATION;
     }
-
-    private static class FastTranslateAnimation extends TranslateAnimation {
-        public FastTranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue,
-                int fromYType, float fromYValue, int toYType, float toYValue) {
-            super(fromXType, fromXValue, toXType, toXValue,
-                    fromYType, fromYValue, toYType, toYValue);
-        }
-
-        @Override
-        public boolean willChangeTransformationMatrix() {
-            return true;
-        }
-
-        @Override
-        public boolean willChangeBounds() {
-            return false;
-        }
-    }
-
-    private static class FastAnimationSet extends AnimationSet {
-        FastAnimationSet() {
-            super(false);
-        }
-
-        @Override
-        public boolean willChangeTransformationMatrix() {
-            return true;
-        }
-
-        @Override
-        public boolean willChangeBounds() {
-            return false;
-        }
-    }
 }
diff --git a/src/com/android/launcher2/DragController.java b/src/com/android/launcher2/DragController.java
index cb4509b..5ca1e1c 100644
--- a/src/com/android/launcher2/DragController.java
+++ b/src/com/android/launcher2/DragController.java
@@ -73,10 +73,10 @@
     private boolean mDragging;
 
     /** X coordinate of the down event. */
-    private float mMotionDownX;
+    private int mMotionDownX;
 
     /** Y coordinate of the down event. */
-    private float mMotionDownY;
+    private int mMotionDownY;
 
     /** Info about the screen for clamping. */
     private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
@@ -85,10 +85,10 @@
     private View mOriginator;
 
     /** X offset from the upper-left corner of the cell to where we touched.  */
-    private float mTouchOffsetX;
+    private int mTouchOffsetX;
 
     /** Y offset from the upper-left corner of the cell to where we touched.  */
-    private float mTouchOffsetY;
+    private int mTouchOffsetY;
 
     /** the area at the edge of the screen that makes the workspace go left
      *   or right while you're dragging.
@@ -289,13 +289,14 @@
             listener.onDragStart(source, dragInfo, dragAction);
         }
 
-        int registrationX = ((int)mMotionDownX) - screenX;
-        int registrationY = ((int)mMotionDownY) - screenY;
+        final int registrationX = ((int)mMotionDownX) - screenX;
+        final int registrationY = ((int)mMotionDownY) - screenY;
 
         final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left;
         final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top;
-        mTouchOffsetX = mMotionDownX - screenX - dragRegionLeft;
-        mTouchOffsetY = mMotionDownY - screenY - dragRegionTop;
+
+        mTouchOffsetX = mMotionDownX - (screenX + dragRegionLeft);
+        mTouchOffsetY = mMotionDownY - (screenY + dragRegionTop);
 
         mDragging = true;
         mDragSource = source;
@@ -314,8 +315,7 @@
         });
 
         if (dragRegion != null) {
-            dragView.setDragRegion(dragRegionLeft, dragRegion.top,
-                    dragRegion.right - dragRegionLeft, dragRegion.bottom - dragRegionTop);
+            dragView.setDragRegion(new Rect(dragRegion));
         }
 
         dragView.show(mWindowToken, (int)mMotionDownX, (int)mMotionDownY);
diff --git a/src/com/android/launcher2/DragLayer.java b/src/com/android/launcher2/DragLayer.java
index eb53945..c2b710e 100644
--- a/src/com/android/launcher2/DragLayer.java
+++ b/src/com/android/launcher2/DragLayer.java
@@ -16,10 +16,11 @@
 
 package com.android.launcher2;
 
-import com.android.launcher.R;
+import java.util.ArrayList;
 
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -27,6 +28,8 @@
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 
+import com.android.launcher.R;
+
 /**
  * A ViewGroup that coordinates dragging across its descendants
  */
@@ -34,6 +37,12 @@
     private DragController mDragController;
     private int[] mTmpXY = new int[2];
 
+    // Variables relating to resizing widgets
+    private final ArrayList<AppWidgetResizeFrame> mResizeFrames =
+            new ArrayList<AppWidgetResizeFrame>();
+    private AppWidgetResizeFrame mCurrentResizeFrame;
+    private int mXDown, mYDown;
+
     /**
      * Used to create a new DragLayer from XML.
      *
@@ -56,31 +65,66 @@
         return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);
     }
 
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        // If the current CellLayoutChildren has a resize frame, we need to detect if any touch
-        // event has occurred which doesn't result in resizing a widget. In this case, we
-        // dismiss any visible resize frames.
-        final Workspace w = (Workspace) findViewById(R.id.workspace);
-        if (w != null) {
-            final CellLayout currentPage = (CellLayout) w.getChildAt(w.getCurrentPage());
-            final CellLayoutChildren childrenLayout = currentPage.getChildrenLayout();
+    private boolean handleTouchDown(MotionEvent ev) {
+        Rect hitRect = new Rect();
+        int x = (int) ev.getX();
+        int y = (int) ev.getY();
 
-            if (childrenLayout.hasResizeFrames() && !childrenLayout.isWidgetBeingResized()) {
-                post(new Runnable() {
-                    public void run() {
-                        if (!childrenLayout.isWidgetBeingResized()) {
-                            childrenLayout.clearAllResizeFrames();
-                        }
-                    }
-                });
+        for (AppWidgetResizeFrame child: mResizeFrames) {
+            child.getHitRect(hitRect);
+            if (hitRect.contains(x, y)) {
+                if (child.beginResizeIfPointInRegion(x - child.getLeft(), y - child.getTop())) {
+                    mCurrentResizeFrame = child;
+                    mXDown = x;
+                    mYDown = y;
+                    requestDisallowInterceptTouchEvent(true);
+                    return true;
+                }
             }
         }
+        return false;
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            if (handleTouchDown(ev)) {
+                return true;
+            }
+        }
+        clearAllResizeFrames();
         return mDragController.onInterceptTouchEvent(ev);
     }
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
+        boolean handled = false;
+        int action = ev.getAction();
+
+        int x = (int) ev.getX();
+        int y = (int) ev.getY();
+
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+                if (handleTouchDown(ev)) {
+                    return true;
+                }
+            }
+        }
+
+        if (mCurrentResizeFrame != null) {
+            handled = true;
+            switch (action) {
+                case MotionEvent.ACTION_MOVE:
+                    mCurrentResizeFrame.visualizeResizeForDelta(x - mXDown, y - mYDown);
+                    break;
+                case MotionEvent.ACTION_CANCEL:
+                case MotionEvent.ACTION_UP:
+                    mCurrentResizeFrame.commitResizeForDelta(x - mXDown, y - mYDown);
+                    mCurrentResizeFrame = null;
+            }
+        }
+        if (handled) return true;
         return mDragController.onTouchEvent(ev);
     }
 
@@ -103,4 +147,94 @@
         v.getLocationOnScreen(mTmpXY);
         return createDragView(mDragController.getViewBitmap(v), mTmpXY[0], mTmpXY[1]);
     }
+
+    public static class LayoutParams extends FrameLayout.LayoutParams {
+        public int x, y;
+        public boolean customPosition = false;
+
+        /**
+         * {@inheritDoc}
+         */
+        public LayoutParams(int width, int height) {
+            super(width, height);
+        }
+
+        public void setWidth(int width) {
+            this.width = width;
+        }
+
+        public int getWidth() {
+            return width;
+        }
+
+        public void setHeight(int height) {
+            this.height = height;
+        }
+
+        public int getHeight() {
+            return height;
+        }
+
+        public void setX(int x) {
+            this.x = x;
+        }
+
+        public int getX() {
+            return x;
+        }
+
+        public void setY(int y) {
+            this.y = y;
+        }
+
+        public int getY() {
+            return y;
+        }
+    }
+
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+        int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            View child = getChildAt(i);
+            final FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams) child.getLayoutParams();
+            if (flp instanceof LayoutParams) {
+                final LayoutParams lp = (LayoutParams) flp;
+                if (lp.customPosition) {
+                    child.layout(lp.x, lp.y, lp.x + lp.width, lp.y + lp.height);
+                }
+            }
+        }
+    }
+
+    public void clearAllResizeFrames() {
+        if (mResizeFrames.size() > 0) {
+            for (AppWidgetResizeFrame frame: mResizeFrames) {
+                removeView(frame);
+            }
+            mResizeFrames.clear();
+        }
+    }
+
+    public boolean hasResizeFrames() {
+        return mResizeFrames.size() > 0;
+    }
+
+    public boolean isWidgetBeingResized() {
+        return mCurrentResizeFrame != null;
+    }
+
+    public void addResizeFrame(ItemInfo itemInfo, LauncherAppWidgetHostView widget,
+            CellLayout cellLayout) {
+        AppWidgetResizeFrame resizeFrame = new AppWidgetResizeFrame(getContext(),
+                itemInfo, widget, cellLayout, this);
+
+        LayoutParams lp = new LayoutParams(-1, -1);
+        lp.customPosition = true;
+
+        addView(resizeFrame, lp);
+        mResizeFrames.add(resizeFrame);
+
+        resizeFrame.snapToWidget(false);
+    }
 }
diff --git a/src/com/android/launcher2/DragView.java b/src/com/android/launcher2/DragView.java
index 45620b9..b02e22b 100644
--- a/src/com/android/launcher2/DragView.java
+++ b/src/com/android/launcher2/DragView.java
@@ -26,6 +26,7 @@
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
+import android.graphics.Rect;
 import android.os.IBinder;
 import android.view.Gravity;
 import android.view.View;
@@ -42,10 +43,7 @@
     private int mRegistrationX;
     private int mRegistrationY;
 
-    private int mDragRegionLeft = 0;
-    private int mDragRegionTop = 0;
-    private int mDragRegionWidth;
-    private int mDragRegionHeight;
+    private Rect mDragRegion = null;
 
     ValueAnimator mAnim;
     private float mOffsetX = 0.0f;
@@ -87,8 +85,8 @@
             scale.setScale(scaleFactor, scaleFactor);
         }
 
-        final int offsetX = res.getInteger(R.integer.config_dragViewOffsetX);
-        final int offsetY = res.getInteger(R.integer.config_dragViewOffsetY);
+        final int offsetX = res.getDimensionPixelSize(R.dimen.dragViewOffsetX);
+        final int offsetY = res.getDimensionPixelSize(R.dimen.dragViewOffsetY);
 
         // Animate the view into the correct position
         mAnim = ValueAnimator.ofFloat(0.0f, 1.0f);
@@ -117,7 +115,7 @@
         });
 
         mBitmap = Bitmap.createBitmap(bitmap, left, top, width, height, scale, true);
-        setDragRegion(0, 0, width, height);
+        setDragRegion(new Rect(0, 0, width, height));
 
         // The point in our scaled bitmap that the touch events are located
         mRegistrationX = registrationX;
@@ -132,31 +130,32 @@
         return mOffsetY;
     }
 
-    public void setDragRegion(int left, int top, int width, int height) {
-        mDragRegionLeft = left;
-        mDragRegionTop = top;
-        mDragRegionWidth = width;
-        mDragRegionHeight = height;
-    }
-
     public void setOnDrawRunnable(Runnable r) {
         mOnDrawRunnable = r;
     }
 
     public int getDragRegionLeft() {
-        return mDragRegionLeft;
+        return mDragRegion.left;
     }
 
     public int getDragRegionTop() {
-        return mDragRegionTop;
+        return mDragRegion.top;
     }
 
     public int getDragRegionWidth() {
-        return mDragRegionWidth;
+        return mDragRegion.width();
     }
 
     public int getDragRegionHeight() {
-        return mDragRegionHeight;
+        return mDragRegion.height();
+    }
+
+    public void setDragRegion(Rect r) {
+        mDragRegion = r;
+    }
+
+    public Rect getDragRegion() {
+        return mDragRegion;
     }
 
     @Override
diff --git a/src/com/android/launcher2/FocusHelper.java b/src/com/android/launcher2/FocusHelper.java
new file mode 100644
index 0000000..c9bd58c
--- /dev/null
+++ b/src/com/android/launcher2/FocusHelper.java
@@ -0,0 +1,772 @@
+/*
+ * 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.
+ */
+
+package com.android.launcher2;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.widget.TabHost;
+import android.widget.TabWidget;
+
+import com.android.launcher.R;
+
+/**
+ * A keyboard listener we set on all the button bar buttons.
+ */
+class ButtonBarKeyEventListener implements View.OnKeyListener {
+    @Override
+    public boolean onKey(View v, int keyCode, KeyEvent event) {
+        return FocusHelper.handleButtonBarButtonKeyEvent(v, keyCode, event);
+    }
+}
+
+/**
+ * A keyboard listener we set on the last tab button in AllApps to jump to then
+ * market icon and vice versa.
+ */
+class AllAppsTabKeyEventListener implements View.OnKeyListener {
+    @Override
+    public boolean onKey(View v, int keyCode, KeyEvent event) {
+        return FocusHelper.handleAllAppsTabKeyEvent(v, keyCode, event);
+    }
+}
+
+public class FocusHelper {
+    /**
+     * Private helper to get the parent TabHost in the view hiearchy.
+     */
+    private static TabHost findTabHostParent(View v) {
+        ViewParent p = v.getParent();
+        while (p != null && !(p instanceof TabHost)) {
+            p = p.getParent();
+        }
+        return (TabHost) p;
+    }
+
+    /**
+     * Handles key events in a AllApps tab between the last tab view and the shop button.
+     */
+    static boolean handleAllAppsTabKeyEvent(View v, int keyCode, KeyEvent e) {
+        final TabHost tabHost = findTabHostParent(v);
+        final ViewGroup contents = (ViewGroup)
+                tabHost.findViewById(com.android.internal.R.id.tabcontent);
+        final View shop = tabHost.findViewById(R.id.market_button);
+
+        final int action = e.getAction();
+        final boolean handleKeyEvent = (action != KeyEvent.ACTION_UP);
+        boolean wasHandled = false;
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+                if (handleKeyEvent) {
+                    // Select the shop button if we aren't on it
+                    if (v != shop) {
+                        shop.requestFocus();
+                    }
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+                if (handleKeyEvent) {
+                    // Select the content view (down is handled by the tab key handler otherwise)
+                    if (v == shop) {
+                        contents.requestFocus();
+                        wasHandled = true;
+                    }
+                }
+                break;
+            default: break;
+        }
+        return wasHandled;
+    }
+
+    /**
+     * Private helper to determine whether a view is visible.
+     */
+    private static boolean isVisible(View v) {
+        return v.getVisibility() == View.VISIBLE;
+    }
+
+    /**
+     * Handles key events in a PageViewExtendedLayout containing PagedViewWidgets.
+     */
+    static boolean handlePagedViewWidgetKeyEvent(PagedViewWidget w, int keyCode, KeyEvent e) {
+        if (!LauncherApplication.isScreenXLarge()) return false;
+
+        final PagedViewExtendedLayout parent = (PagedViewExtendedLayout) w.getParent();
+        final ViewGroup container = (ViewGroup) parent.getParent();
+        final TabHost tabHost = findTabHostParent(container);
+        final TabWidget tabs = (TabWidget) tabHost.findViewById(com.android.internal.R.id.tabs);
+        final int widgetIndex = parent.indexOfChild(w);
+        final int widgetCount = parent.getChildCount();
+        final int pageIndex = container.indexOfChild(parent);
+        final int pageCount = container.getChildCount();
+
+        final int action = e.getAction();
+        final boolean handleKeyEvent = (action != KeyEvent.ACTION_UP);
+        PagedViewExtendedLayout newParent = null;
+        boolean wasHandled = false;
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+                if (handleKeyEvent) {
+                    // Select the previous widget or the last widget on the previous page
+                    if (widgetIndex > 0) {
+                        parent.getChildAt(widgetIndex - 1).requestFocus();
+                    } else {
+                        if (pageIndex > 0) {
+                            newParent = (PagedViewExtendedLayout)
+                                    container.getChildAt(pageIndex - 1);
+                            newParent.getChildAt(newParent.getChildCount() - 1).requestFocus();
+                        }
+                    }
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+                if (handleKeyEvent) {
+                    // Select the next widget or the first widget on the next page
+                    if (widgetIndex < (widgetCount - 1)) {
+                        parent.getChildAt(widgetIndex + 1).requestFocus();
+                    } else {
+                        if (pageIndex < (pageCount - 1)) {
+                            newParent = (PagedViewExtendedLayout)
+                                    container.getChildAt(pageIndex + 1);
+                            newParent.getChildAt(0).requestFocus();
+                        }
+                    }
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_DPAD_UP:
+                if (handleKeyEvent) {
+                    // Select widgets tab on the tab bar
+                    tabs.requestFocus();
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+                if (handleKeyEvent) {
+                    // TODO: Should focus the global search bar
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_ENTER:
+            case KeyEvent.KEYCODE_DPAD_CENTER:
+                if (handleKeyEvent) {
+                    // Simulate a click on the widget
+                    View.OnClickListener clickListener = (View.OnClickListener) container;
+                    clickListener.onClick(w);
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_PAGE_UP:
+                if (handleKeyEvent) {
+                    // Select the first item on the previous page, or the first item on this page
+                    // if there is no previous page
+                    if (pageIndex > 0) {
+                        newParent = (PagedViewExtendedLayout) container.getChildAt(pageIndex - 1);
+                        newParent.getChildAt(0).requestFocus();
+                    } else {
+                        parent.getChildAt(0).requestFocus();
+                    }
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_PAGE_DOWN:
+                if (handleKeyEvent) {
+                    // Select the first item on the next page, or the last item on this page
+                    // if there is no next page
+                    if (pageIndex < (pageCount - 1)) {
+                        newParent = (PagedViewExtendedLayout) container.getChildAt(pageIndex + 1);
+                        newParent.getChildAt(0).requestFocus();
+                    } else {
+                        parent.getChildAt(widgetCount - 1).requestFocus();
+                    }
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_MOVE_HOME:
+                if (handleKeyEvent) {
+                    // Select the first item on this page
+                    parent.getChildAt(0).requestFocus();
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_MOVE_END:
+                if (handleKeyEvent) {
+                    // Select the last item on this page
+                    parent.getChildAt(widgetCount - 1).requestFocus();
+                }
+                wasHandled = true;
+                break;
+            default: break;
+        }
+        return wasHandled;
+    }
+
+    /**
+     * Private helper method to get the PagedViewCellLayoutChildren given a PagedViewCellLayout
+     * index.
+     */
+    private static PagedViewCellLayoutChildren getPagedViewCellLayoutChildrenForIndex(
+            ViewGroup container, int i) {
+        ViewGroup parent = (ViewGroup) container.getChildAt(i);
+        return (PagedViewCellLayoutChildren) parent.getChildAt(0);
+    }
+
+    /**
+     * Handles key events in a PageViewCellLayout containing PagedViewIcons.
+     */
+    static boolean handlePagedViewIconKeyEvent(PagedViewIcon v, int keyCode, KeyEvent e) {
+        if (!LauncherApplication.isScreenXLarge()) return false;
+
+        final PagedViewCellLayoutChildren parent = (PagedViewCellLayoutChildren) v.getParent();
+        final PagedViewCellLayout parentLayout = (PagedViewCellLayout) parent.getParent();
+        // Note we have an extra parent because of the
+        // PagedViewCellLayout/PagedViewCellLayoutChildren relationship
+        final ViewGroup container = (ViewGroup) parentLayout.getParent();
+        final TabHost tabHost = findTabHostParent(container);
+        final TabWidget tabs = (TabWidget) tabHost.findViewById(com.android.internal.R.id.tabs);
+        final int widgetIndex = parent.indexOfChild(v);
+        final int widgetCount = parent.getChildCount();
+        final int pageIndex = container.indexOfChild(parentLayout);
+        final int pageCount = container.getChildCount();
+        final int cellCountX = parentLayout.getCellCountX();
+        final int cellCountY = parentLayout.getCellCountY();
+        final int x = widgetIndex % cellCountX;
+        final int y = widgetIndex / cellCountX;
+
+        final int action = e.getAction();
+        final boolean handleKeyEvent = (action != KeyEvent.ACTION_UP);
+        PagedViewCellLayoutChildren newParent = null;
+        boolean wasHandled = false;
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+                if (handleKeyEvent) {
+                    // Select the previous icon or the last icon on the previous page
+                    if (widgetIndex > 0) {
+                        parent.getChildAt(widgetIndex - 1).requestFocus();
+                    } else {
+                        if (pageIndex > 0) {
+                            newParent = getPagedViewCellLayoutChildrenForIndex(container,
+                                    pageIndex - 1);
+                            newParent.getChildAt(newParent.getChildCount() - 1).requestFocus();
+                        }
+                    }
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+                if (handleKeyEvent) {
+                    // Select the next icon or the first icon on the next page
+                    if (widgetIndex < (widgetCount - 1)) {
+                        parent.getChildAt(widgetIndex + 1).requestFocus();
+                    } else {
+                        if (pageIndex < (pageCount - 1)) {
+                            newParent = getPagedViewCellLayoutChildrenForIndex(container,
+                                    pageIndex + 1);
+                            newParent.getChildAt(0).requestFocus();
+                        }
+                    }
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_DPAD_UP:
+                if (handleKeyEvent) {
+                    // Select the closest icon in the previous row, otherwise select the tab bar
+                    if (y > 0) {
+                        int newWidgetIndex = ((y - 1) * cellCountX) + x;
+                        parent.getChildAt(newWidgetIndex).requestFocus();
+                    } else {
+                        tabs.requestFocus();
+                    }
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+                if (handleKeyEvent) {
+                    // Select the closest icon in the previous row, otherwise do nothing
+                    if (y < (cellCountY - 1)) {
+                        int newWidgetIndex = Math.min(widgetCount - 1, ((y + 1) * cellCountX) + x);
+                        parent.getChildAt(newWidgetIndex).requestFocus();
+                    }
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_ENTER:
+            case KeyEvent.KEYCODE_DPAD_CENTER:
+                if (handleKeyEvent) {
+                    // Simulate a click on the icon
+                    View.OnClickListener clickListener = (View.OnClickListener) container;
+                    clickListener.onClick(v);
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_PAGE_UP:
+                if (handleKeyEvent) {
+                    // Select the first icon on the previous page, or the first icon on this page
+                    // if there is no previous page
+                    if (pageIndex > 0) {
+                        newParent = getPagedViewCellLayoutChildrenForIndex(container,
+                                pageIndex - 1);
+                        newParent.getChildAt(0).requestFocus();
+                    } else {
+                        parent.getChildAt(0).requestFocus();
+                    }
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_PAGE_DOWN:
+                if (handleKeyEvent) {
+                    // Select the first icon on the next page, or the last icon on this page
+                    // if there is no next page
+                    if (pageIndex < (pageCount - 1)) {
+                        newParent = getPagedViewCellLayoutChildrenForIndex(container,
+                                pageIndex + 1);
+                        newParent.getChildAt(0).requestFocus();
+                    } else {
+                        parent.getChildAt(widgetCount - 1).requestFocus();
+                    }
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_MOVE_HOME:
+                if (handleKeyEvent) {
+                    // Select the first icon on this page
+                    parent.getChildAt(0).requestFocus();
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_MOVE_END:
+                if (handleKeyEvent) {
+                    // Select the last icon on this page
+                    parent.getChildAt(widgetCount - 1).requestFocus();
+                }
+                wasHandled = true;
+                break;
+            default: break;
+        }
+        return wasHandled;
+    }
+
+    /**
+     * Handles key events in the tab widget.
+     */
+    static boolean handleTabKeyEvent(AccessibleTabView v, int keyCode, KeyEvent e) {
+        if (!LauncherApplication.isScreenXLarge()) return false;
+
+        final FocusOnlyTabWidget parent = (FocusOnlyTabWidget) v.getParent();
+        final TabHost tabHost = findTabHostParent(parent);
+        final ViewGroup contents = (ViewGroup)
+                tabHost.findViewById(com.android.internal.R.id.tabcontent);
+        final int tabCount = parent.getTabCount();
+        final int tabIndex = parent.getChildTabIndex(v);
+
+        final int action = e.getAction();
+        final boolean handleKeyEvent = (action != KeyEvent.ACTION_UP);
+        boolean wasHandled = false;
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+                if (handleKeyEvent) {
+                    // Select the previous tab
+                    if (tabIndex > 0) {
+                        parent.getChildTabViewAt(tabIndex - 1).requestFocus();
+                    }
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+                if (handleKeyEvent) {
+                    // Select the next tab, or if the last tab has a focus right id, select that
+                    if (tabIndex < (tabCount - 1)) {
+                        parent.getChildTabViewAt(tabIndex + 1).requestFocus();
+                    } else {
+                        if (v.getNextFocusRightId() != View.NO_ID) {
+                            tabHost.findViewById(v.getNextFocusRightId()).requestFocus();
+                        }
+                    }
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_DPAD_UP:
+                // Do nothing
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+                if (handleKeyEvent) {
+                    // Select the content view
+                    contents.requestFocus();
+                }
+                wasHandled = true;
+                break;
+            default: break;
+        }
+        return wasHandled;
+    }
+
+    /**
+     * Handles key events in a the workspace button bar.
+     */
+    static boolean handleButtonBarButtonKeyEvent(View v, int keyCode, KeyEvent e) {
+        if (!LauncherApplication.isScreenXLarge()) return false;
+
+        final ViewGroup parent = (ViewGroup) v.getParent();
+        final ViewGroup launcher = (ViewGroup) parent.getParent();
+        final Workspace workspace = (Workspace) launcher.findViewById(R.id.workspace);
+        final int buttonIndex = parent.indexOfChild(v);
+        final int buttonCount = parent.getChildCount();
+        final int pageIndex = workspace.getCurrentPage();
+        final int pageCount = workspace.getChildCount();
+        final int firstButtonIndex = parent.indexOfChild(parent.findViewById(R.id.search_button));
+        final int lastButtonIndex = parent.indexOfChild(parent.findViewById(R.id.configure_button));
+
+        final int action = e.getAction();
+        final boolean handleKeyEvent = (action != KeyEvent.ACTION_UP);
+        boolean wasHandled = false;
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+                if (handleKeyEvent) {
+                    // Select the previous button, otherwise do nothing (since the button bar is
+                    // static)
+                    if (buttonIndex > firstButtonIndex) {
+                        int newButtonIndex = buttonIndex - 1;
+                        while (newButtonIndex >= firstButtonIndex) {
+                            View prev = parent.getChildAt(newButtonIndex);
+                            if (isVisible(prev) && prev.isFocusable()) {
+                                prev.requestFocus();
+                                break;
+                            }
+                            --newButtonIndex;
+                        }
+                    } else {
+                        if (pageIndex > 0) {
+                            // Snap to previous page and clear focus
+                            workspace.snapToPage(pageIndex - 1);
+                        }
+                    }
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+                if (handleKeyEvent) {
+                    // Select the next button, otherwise do nothing (since the button bar is
+                    // static)
+                    if (buttonIndex < lastButtonIndex) {
+                        int newButtonIndex = buttonIndex + 1;
+                        while (newButtonIndex <= lastButtonIndex) {
+                            View next = parent.getChildAt(newButtonIndex);
+                            if (isVisible(next) && next.isFocusable()) {
+                                next.requestFocus();
+                                break;
+                            }
+                            ++newButtonIndex;
+                        }
+                    } else {
+                        if (pageIndex < (pageCount - 1)) {
+                            // Snap to next page and clear focus
+                            workspace.snapToPage(pageIndex + 1);
+                        }
+                    }
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_DPAD_UP:
+                // Do nothing
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+                if (handleKeyEvent) {
+                    // Select the first bubble text view in the current page of the workspace
+                    final CellLayout layout = (CellLayout) workspace.getChildAt(pageIndex);
+                    final CellLayoutChildren children = layout.getChildrenLayout();
+                    final View newIcon = getBubbleTextViewInDirection(layout, children, -1, 1);
+                    if (newIcon != null) {
+                        newIcon.requestFocus();
+                    } else {
+                        workspace.requestFocus();
+                    }
+                }
+                wasHandled = true;
+                break;
+            default: break;
+        }
+        return wasHandled;
+    }
+
+    /**
+     * Private helper method to get the CellLayoutChildren given a CellLayout index.
+     */
+    private static CellLayoutChildren getCellLayoutChildrenForIndex(ViewGroup container, int i) {
+        ViewGroup parent = (ViewGroup) container.getChildAt(i);
+        return (CellLayoutChildren) parent.getChildAt(0);
+    }
+
+    /**
+     * Private helper method to sort all the CellLayout children in order of their (x,y) spatially
+     * from top left to bottom right.
+     */
+    private static ArrayList<View> getCellLayoutChildrenSortedSpatially(CellLayout layout,
+            ViewGroup parent) {
+        // First we order each the CellLayout children by their x,y coordinates
+        final int cellCountX = layout.getCountX();
+        final int count = parent.getChildCount();
+        ArrayList<View> views = new ArrayList<View>();
+        for (int j = 0; j < count; ++j) {
+            views.add(parent.getChildAt(j));
+        }
+        Collections.sort(views, new Comparator<View>() {
+            @Override
+            public int compare(View lhs, View rhs) {
+                CellLayout.LayoutParams llp = (CellLayout.LayoutParams) lhs.getLayoutParams();
+                CellLayout.LayoutParams rlp = (CellLayout.LayoutParams) rhs.getLayoutParams();
+                int lvIndex = (llp.cellY * cellCountX) + llp.cellX;
+                int rvIndex = (rlp.cellY * cellCountX) + rlp.cellX;
+                return lvIndex - rvIndex;
+            }
+        });
+        return views;
+    }
+    /**
+     * Private helper method to find the index of the next BubbleTextView in the delta direction.
+     * @param delta either -1 or 1 depending on the direction we want to search
+     */
+    private static View findIndexOfBubbleTextView(ArrayList<View> views, int i, int delta) {
+        // Then we find the next BubbleTextView offset by delta from i
+        final int count = views.size();
+        int newI = i + delta;
+        while (0 <= newI && newI < count) {
+            View newV = views.get(newI);
+            if (newV instanceof BubbleTextView) {
+                return newV;
+            }
+            newI += delta;
+        }
+        return null;
+    }
+    private static View getBubbleTextViewInDirection(CellLayout layout, ViewGroup parent, int i,
+            int delta) {
+        final ArrayList<View> views = getCellLayoutChildrenSortedSpatially(layout, parent);
+        return findIndexOfBubbleTextView(views, i, delta);
+    }
+    private static View getBubbleTextViewInDirection(CellLayout layout, ViewGroup parent, View v,
+            int delta) {
+        final ArrayList<View> views = getCellLayoutChildrenSortedSpatially(layout, parent);
+        return findIndexOfBubbleTextView(views, views.indexOf(v), delta);
+    }
+    /**
+     * Private helper method to find the next closest BubbleTextView in the delta direction on the
+     * next line.
+     * @param delta either -1 or 1 depending on the line and direction we want to search
+     */
+    private static View getClosestBubbleTextViewOnLine(CellLayout layout, ViewGroup parent, View v,
+            int lineDelta) {
+        final ArrayList<View> views = getCellLayoutChildrenSortedSpatially(layout, parent);
+        final CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams();
+        final int cellCountX = layout.getCountX();
+        final int cellCountY = layout.getCountY();
+        final int row = lp.cellY;
+        final int newRow = row + lineDelta;
+        if (0 <= newRow && newRow < cellCountY) {
+            float closestDistance = Float.MAX_VALUE;
+            int closestIndex = -1;
+            int index = views.indexOf(v);
+            int endIndex = (lineDelta < 0) ? -1 : views.size();
+            while (index != endIndex) {
+                View newV = views.get(index);
+                CellLayout.LayoutParams tmpLp = (CellLayout.LayoutParams) newV.getLayoutParams();
+                boolean satisfiesRow = (lineDelta < 0) ? (tmpLp.cellY < row) : (tmpLp.cellY > row);
+                if (satisfiesRow && newV instanceof BubbleTextView) {
+                    float tmpDistance = (float) Math.sqrt(Math.pow(tmpLp.cellX - lp.cellX, 2) +
+                            Math.pow(tmpLp.cellY - lp.cellY, 2));
+                    if (tmpDistance < closestDistance) {
+                        closestIndex = index;
+                        closestDistance = tmpDistance;
+                    }
+                }
+                if (index <= endIndex) {
+                    ++index;
+                } else {
+                    --index;
+                }
+            }
+            if (closestIndex > -1) {
+                return views.get(closestIndex);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Handles key events in a Workspace containing BubbleTextView.
+     */
+    static boolean handleBubbleTextViewKeyEvent(BubbleTextView v, int keyCode, KeyEvent e) {
+        if (!LauncherApplication.isScreenXLarge()) return false;
+
+        CellLayoutChildren parent = (CellLayoutChildren) v.getParent();
+        final CellLayout layout = (CellLayout) parent.getParent();
+        final Workspace workspace = (Workspace) layout.getParent();
+        final ViewGroup launcher = (ViewGroup) workspace.getParent();
+        final ViewGroup tabs = (ViewGroup) launcher.findViewById(R.id.all_apps_button_cluster);
+        int iconIndex = parent.indexOfChild(v);
+        int iconCount = parent.getChildCount();
+        int pageIndex = workspace.indexOfChild(layout);
+        int pageCount = workspace.getChildCount();
+
+        final int action = e.getAction();
+        final boolean handleKeyEvent = (action != KeyEvent.ACTION_UP);
+        boolean wasHandled = false;
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+                if (handleKeyEvent) {
+                    // Select the previous icon or the last icon on the previous page if possible
+                    View newIcon = getBubbleTextViewInDirection(layout, parent, v, -1);
+                    if (newIcon != null) {
+                        newIcon.requestFocus();
+                    } else {
+                        if (pageIndex > 0) {
+                            parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
+                            newIcon = getBubbleTextViewInDirection(layout, parent,
+                                    parent.getChildCount(), -1);
+                            if (newIcon != null) {
+                                newIcon.requestFocus();
+                            } else {
+                                // Snap to the previous page
+                                workspace.snapToPage(pageIndex - 1);
+                            }
+                        }
+                    }
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+                if (handleKeyEvent) {
+                    // Select the next icon or the first icon on the next page if possible
+                    View newIcon = getBubbleTextViewInDirection(layout, parent, v, 1);
+                    if (newIcon != null) {
+                        newIcon.requestFocus();
+                    } else {
+                        if (pageIndex < (pageCount - 1)) {
+                            parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
+                            newIcon = getBubbleTextViewInDirection(layout, parent, -1, 1);
+                            if (newIcon != null) {
+                                newIcon.requestFocus();
+                            } else {
+                                // Snap to the next page
+                                workspace.snapToPage(pageIndex + 1);
+                            }
+                        }
+                    }
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_DPAD_UP:
+                if (handleKeyEvent) {
+                    // Select the closest icon in the previous line, otherwise select the tab bar
+                    View newIcon = getClosestBubbleTextViewOnLine(layout, parent, v, -1);
+                    if (newIcon != null) {
+                        newIcon.requestFocus();
+                        wasHandled = true;
+                    } else {
+                        tabs.requestFocus();
+                    }
+                }
+                break;
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+                if (handleKeyEvent) {
+                    // Select the closest icon in the next line, otherwise select the tab bar
+                    View newIcon = getClosestBubbleTextViewOnLine(layout, parent, v, 1);
+                    if (newIcon != null) {
+                        newIcon.requestFocus();
+                        wasHandled = true;
+                    }
+                }
+                break;
+            case KeyEvent.KEYCODE_PAGE_UP:
+                if (handleKeyEvent) {
+                    // Select the first icon on the previous page or the first icon on this page
+                    // if there is no previous page
+                    if (pageIndex > 0) {
+                        parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
+                        View newIcon = getBubbleTextViewInDirection(layout, parent, -1, 1);
+                        if (newIcon != null) {
+                            newIcon.requestFocus();
+                        } else {
+                            // Snap to the previous page
+                            workspace.snapToPage(pageIndex - 1);
+                        }
+                    } else {
+                        View newIcon = getBubbleTextViewInDirection(layout, parent, -1, 1);
+                        if (newIcon != null) {
+                            newIcon.requestFocus();
+                        }
+                    }
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_PAGE_DOWN:
+                if (handleKeyEvent) {
+                    // Select the first icon on the next page or the last icon on this page
+                    // if there is no previous page
+                    if (pageIndex < (pageCount - 1)) {
+                        parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
+                        View newIcon = getBubbleTextViewInDirection(layout, parent, -1, 1);
+                        if (newIcon != null) {
+                            newIcon.requestFocus();
+                        } else {
+                            // Snap to the next page
+                            workspace.snapToPage(pageIndex + 1);
+                        }
+                    } else {
+                        View newIcon = getBubbleTextViewInDirection(layout, parent,
+                                parent.getChildCount(), -1);
+                        if (newIcon != null) {
+                            newIcon.requestFocus();
+                        }
+                    }
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_MOVE_HOME:
+                if (handleKeyEvent) {
+                    // Select the first icon on this page
+                    View newIcon = getBubbleTextViewInDirection(layout, parent, -1, 1);
+                    if (newIcon != null) {
+                        newIcon.requestFocus();
+                    }
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_MOVE_END:
+                if (handleKeyEvent) {
+                    // Select the last icon on this page
+                    View newIcon = getBubbleTextViewInDirection(layout, parent,
+                            parent.getChildCount(), -1);
+                    if (newIcon != null) {
+                        newIcon.requestFocus();
+                    }
+                }
+                wasHandled = true;
+                break;
+            default: break;
+        }
+        return wasHandled;
+    }
+}
diff --git a/src/com/android/launcher2/FocusOnlyTabWidget.java b/src/com/android/launcher2/FocusOnlyTabWidget.java
new file mode 100644
index 0000000..8e9f58c
--- /dev/null
+++ b/src/com/android/launcher2/FocusOnlyTabWidget.java
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+package com.android.launcher2;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.TabWidget;
+
+public class FocusOnlyTabWidget extends TabWidget {
+    public FocusOnlyTabWidget(Context context) {
+        super(context);
+    }
+
+    public FocusOnlyTabWidget(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public FocusOnlyTabWidget(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public View getSelectedTab() {
+        final int count = getTabCount();
+        for (int i = 0; i < count; ++i) {
+            View v = getChildTabViewAt(i);
+            if (v.isSelected()) {
+                return v;
+            }
+        }
+        return null;
+    }
+
+    public int getChildTabIndex(View v) {
+        final int tabCount = getTabCount();
+        for (int i = 0; i < tabCount; ++i) {
+            if (getChildTabViewAt(i) == v) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    public void setCurrentTabToFocusedTab() {
+        View tab = null;
+        int index = -1;
+        final int count = getTabCount();
+        for (int i = 0; i < count; ++i) {
+            View v = getChildTabViewAt(i);
+            if (v.hasFocus()) {
+                tab = v;
+                index = i;
+                break;
+            }
+        }
+        if (index > -1) {
+            super.setCurrentTab(index);
+            super.onFocusChange(tab, true);
+        }
+    }
+    public void superOnFocusChange(View v, boolean hasFocus) {
+        super.onFocusChange(v, hasFocus);
+    }
+
+    @Override
+    public void onFocusChange(android.view.View v, boolean hasFocus) {
+        if (v == this && hasFocus && getTabCount() > 0) {
+            getSelectedTab().requestFocus();
+            return;
+        }
+    }
+}
diff --git a/src/com/android/launcher2/Folder.java b/src/com/android/launcher2/Folder.java
index 059e73d..33a1cd3 100644
--- a/src/com/android/launcher2/Folder.java
+++ b/src/com/android/launcher2/Folder.java
@@ -37,7 +37,6 @@
 public class Folder extends LinearLayout implements DragSource, OnItemLongClickListener,
         OnItemClickListener, OnClickListener, View.OnLongClickListener {
 
-    protected AbsListView mContent;
     protected DragController mDragController;
     
     protected Launcher mLauncher;
@@ -66,10 +65,6 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
 
-        mContent = (AbsListView) findViewById(R.id.folder_content);
-        mContent.setOnItemClickListener(this);
-        mContent.setOnItemLongClickListener(this);
-        
         mCloseButton = (Button) findViewById(R.id.folder_close);
         mCloseButton.setOnClickListener(this);
         mCloseButton.setOnLongClickListener(this);
@@ -121,18 +116,7 @@
     public void onDragViewVisible() {
     }
 
-    /**
-     * Sets the adapter used to populate the content area. The adapter must only
-     * contains ShortcutInfo items.
-     *
-     * @param adapter The list of applications to display in the folder.
-     */
-    void setContentAdapter(BaseAdapter adapter) {
-        mContent.setAdapter(adapter);
-    }
-
     void notifyDataSetChanged() {
-        ((BaseAdapter) mContent.getAdapter()).notifyDataSetChanged();
     }
 
     void setLauncher(Launcher launcher) {
@@ -146,10 +130,7 @@
         return mInfo;
     }
 
-    // When the folder opens, we need to refresh the GridView's selection by
-    // forcing a layout
     void onOpen() {
-        mContent.requestLayout();
     }
 
     void onClose() {
diff --git a/src/com/android/launcher2/FolderIcon.java b/src/com/android/launcher2/FolderIcon.java
index dd83c78..1122d77 100644
--- a/src/com/android/launcher2/FolderIcon.java
+++ b/src/com/android/launcher2/FolderIcon.java
@@ -43,7 +43,10 @@
     }
 
     public boolean isDropEnabled() {
-        return !((Workspace)getParent().getParent()).isSmall();
+        final ViewGroup cellLayoutChildren = (ViewGroup) getParent();
+        final ViewGroup cellLayout = (ViewGroup) cellLayoutChildren.getParent();
+        final Workspace workspace = (Workspace) cellLayout.getParent();
+        return !workspace.isSmall();
     }
 
     static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group,
@@ -74,6 +77,11 @@
                 && item.container != mInfo.id;
     }
 
+    public void addItem(ShortcutInfo item) {
+        mInfo.add(item);
+        LauncherModel.addOrMoveItemInDatabase(mLauncher, item, mInfo.id, 0, 0, 0);
+    }
+
     public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
             DragView dragView, Object dragInfo) {
         ShortcutInfo item;
@@ -83,8 +91,9 @@
         } else {
             item = (ShortcutInfo)dragInfo;
         }
-        mInfo.add(item);
-        LauncherModel.addOrMoveItemInDatabase(mLauncher, item, mInfo.id, 0, 0, 0);
+        item.cellX = -1;
+        item.cellY = -1;
+        addItem(item);
     }
 
     public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset,
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index d7a360d..60cab8e 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -19,13 +19,11 @@
 
 import com.android.common.Search;
 import com.android.launcher.R;
-import com.android.launcher2.CustomizePagedView.CustomizationType;
 import com.android.launcher2.Workspace.ShrinkState;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.app.Activity;
@@ -98,11 +96,8 @@
 import android.widget.LinearLayout;
 import android.widget.PopupWindow;
 import android.widget.TabHost;
-import android.widget.TabWidget;
 import android.widget.TextView;
 import android.widget.Toast;
-import android.widget.TabHost.OnTabChangeListener;
-import android.widget.TabHost.TabContentFactory;
 
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
@@ -112,7 +107,6 @@
 import java.util.HashMap;
 import java.util.List;
 
-
 /**
  * Default launcher application.
  */
@@ -172,12 +166,6 @@
     // Type: long
     private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME_ID = "launcher.rename_folder_id";
 
-    // tags for the customization tabs
-    private static final String WIDGETS_TAG = "widgets";
-    private static final String APPLICATIONS_TAG = "applications";
-    private static final String SHORTCUTS_TAG = "shortcuts";
-    private static final String WALLPAPERS_TAG = "wallpapers";
-
     private static final String TOOLBAR_ICON_METADATA_NAME = "com.android.launcher.toolbar_icon";
 
     /** The different states that Launcher can be in. */
@@ -214,10 +202,10 @@
     private DeleteZone mDeleteZone;
     private HandleView mHandleView;
     private AllAppsView mAllAppsGrid;
-    private TabHost mHomeCustomizationDrawer;
+    private CustomizeTrayTabHost mHomeCustomizationDrawer;
     private boolean mAutoAdvanceRunning = false;
 
-    private View mButtonCluster;
+    private ViewGroup mButtonCluster;
     private View mAllAppsButton;
     private View mDivider;
     private View mConfigureButton;
@@ -293,19 +281,6 @@
         int cellY;
     }
 
-    private CustomizationType getCustomizeFilterForTabTag(String tag) {
-        if (tag.equals(WIDGETS_TAG)) {
-            return CustomizationType.WidgetCustomization;
-        } else if (tag.equals(APPLICATIONS_TAG)) {
-            return CustomizationType.ApplicationCustomization;
-        } else if (tag.equals(WALLPAPERS_TAG)) {
-            return CustomizePagedView.CustomizationType.WallpaperCustomization;
-        } else if (tag.equals(SHORTCUTS_TAG)) {
-            return CustomizePagedView.CustomizationType.ShortcutCustomization;
-        }
-        return CustomizationType.WidgetCustomization;
-    }
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -334,68 +309,11 @@
         loadHotseats();
         checkForLocaleChange();
         setContentView(R.layout.launcher);
-        mHomeCustomizationDrawer = (TabHost) findViewById(R.id.customization_drawer);
+        mHomeCustomizationDrawer = (CustomizeTrayTabHost) findViewById(R.id.customization_drawer);
         if (mHomeCustomizationDrawer != null) {
-            mHomeCustomizationDrawer.setup();
-
             // share the same customization workspace across all the tabs
-            mCustomizePagedView = (CustomizePagedView) mInflater.inflate(
-                    R.layout.customization_drawer_tab_contents, mHomeCustomizationDrawer, false);
-            TabContentFactory contentFactory = new TabContentFactory() {
-                public View createTabContent(String tag) {
-                    return mCustomizePagedView;
-                }
-            };
-
-
-            TextView tabView;
-            TabWidget tabWidget = (TabWidget)
-                    mHomeCustomizationDrawer.findViewById(com.android.internal.R.id.tabs);
-
-            tabView = (TextView) mInflater.inflate(R.layout.tab_widget_indicator, tabWidget, false);
-            tabView.setText(getString(R.string.widgets_tab_label));
-            mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec(WIDGETS_TAG)
-                    .setIndicator(tabView).setContent(contentFactory));
-            tabView = (TextView) mInflater.inflate(R.layout.tab_widget_indicator, tabWidget, false);
-            tabView.setText(getString(R.string.applications_tab_label));
-            mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec(APPLICATIONS_TAG)
-                    .setIndicator(tabView).setContent(contentFactory));
-            tabView = (TextView) mInflater.inflate(R.layout.tab_widget_indicator, tabWidget, false);
-            tabView.setText(getString(R.string.wallpapers_tab_label));
-            mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec(WALLPAPERS_TAG)
-                    .setIndicator(tabView).setContent(contentFactory));
-            tabView = (TextView) mInflater.inflate(R.layout.tab_widget_indicator, tabWidget, false);
-            tabView.setText(getString(R.string.shortcuts_tab_label));
-            mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec(SHORTCUTS_TAG)
-                    .setIndicator(tabView).setContent(contentFactory));
-            mHomeCustomizationDrawer.setOnTabChangedListener(new OnTabChangeListener() {
-                public void onTabChanged(String tabId) {
-                    final CustomizePagedView.CustomizationType newType =
-                        getCustomizeFilterForTabTag(tabId);
-                    if (newType != mCustomizePagedView.getCustomizationFilter()) {
-                        // animate the changing of the tab content by fading pages in and out
-                        final Resources res = getResources();
-                        final int duration = res.getInteger(R.integer.config_tabTransitionTime);
-                        final float alpha = mCustomizePagedView.getAlpha();
-                        ValueAnimator alphaAnim = ObjectAnimator.ofFloat(mCustomizePagedView,
-                                "alpha", alpha, 0.0f);
-                        alphaAnim.setDuration(duration);
-                        alphaAnim.addListener(new AnimatorListenerAdapter() {
-                            @Override
-                            public void onAnimationEnd(Animator animation) {
-                                mCustomizePagedView.setCustomizationFilter(newType);
-
-                                final float alpha = mCustomizePagedView.getAlpha();
-                                ValueAnimator alphaAnim = ObjectAnimator.ofFloat(
-                                        mCustomizePagedView, "alpha", alpha, 1.0f);
-                                alphaAnim.setDuration(duration);
-                                alphaAnim.start();
-                            }
-                        });
-                        alphaAnim.start();
-                    }
-                }
-            });
+            mCustomizePagedView = (CustomizePagedView) findViewById(
+                    R.id.customization_drawer_tab_contents);
         }
         setupViews();
 
@@ -851,8 +769,10 @@
 
     @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
-        boolean handled = super.onKeyDown(keyCode, event);
-        if (!handled && acceptFilter() && keyCode != KeyEvent.KEYCODE_ENTER) {
+        final int uniChar = event.getUnicodeChar();
+        final boolean handled = super.onKeyDown(keyCode, event);
+        final boolean isKeyNotWhitespace = uniChar > 0 && !Character.isWhitespace(uniChar);
+        if (!handled && acceptFilter() && isKeyNotWhitespace) {
             boolean gotKey = TextKeyListener.getInstance().onKeyDown(mWorkspace, mDefaultKeySsb,
                     keyCode, event);
             if (gotKey && mDefaultKeySsb != null && mDefaultKeySsb.length() > 0) {
@@ -957,7 +877,8 @@
             String curTab = savedState.getString("customize_currentTab");
             if (curTab != null) {
                 // We set this directly so that there is no delay before the tab is set
-                mCustomizePagedView.setCustomizationFilter(getCustomizeFilterForTabTag(curTab));
+                mCustomizePagedView.setCustomizationFilter(
+                        mHomeCustomizationDrawer.getCustomizeFilterForTabTag(curTab));
                 mHomeCustomizationDrawer.setCurrentTabByTag(curTab);
             }
 
@@ -1039,7 +960,7 @@
         deleteZone.setDragController(dragController);
 
         final View allAppsButton = findViewById(R.id.all_apps_button);
-        final View divider = findViewById(R.id.divider);
+        final View divider = findViewById(R.id.all_apps_divider);
         final View configureButton = findViewById(R.id.configure_button);
 
         if (LauncherApplication.isScreenXLarge()) {
@@ -1088,10 +1009,15 @@
         if (allAppsDeleteZone != null) {
             dragController.addDropTarget(allAppsDeleteZone);
         }
-        mButtonCluster = findViewById(R.id.all_apps_button_cluster);
+        mButtonCluster = (ViewGroup) findViewById(R.id.all_apps_button_cluster);
+        View.OnKeyListener listener = new ButtonBarKeyEventListener();
+        int buttonCount = mButtonCluster.getChildCount();
+        for (int i = 0; i < buttonCount; ++i) {
+            mButtonCluster.getChildAt(i).setOnKeyListener(listener);
+        }
 
         mAllAppsButton = findViewById(R.id.all_apps_button);
-        mDivider = findViewById(R.id.divider);
+        mDivider = findViewById(R.id.all_apps_divider);
         mConfigureButton = findViewById(R.id.configure_button);
 
         // We had previously set these click handlers in XML, but the first time we launched
@@ -1332,6 +1258,11 @@
             if (Intent.ACTION_SCREEN_OFF.equals(action)) {
                 mUserPresent = false;
                 updateRunning();
+
+                // Reset AllApps to it's initial state
+                if (mAllAppsGrid != null) {
+                    mAllAppsGrid.reset();
+                }
             } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
                 mUserPresent = true;
                 updateRunning();
@@ -1503,6 +1434,11 @@
                         INPUT_METHOD_SERVICE);
                 imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
             }
+
+            // Reset AllApps to it's initial state
+            if (mAllAppsGrid != null) {
+                mAllAppsGrid.reset();
+            }
         }
     }
 
@@ -1872,7 +1808,7 @@
         }
     }
 
-    void addFolder(int screen, int intersectCellX, int intersectCellY) {
+    FolderIcon addFolder(int screen, int intersectCellX, int intersectCellY) {
         UserFolderInfo folderInfo = new UserFolderInfo();
         folderInfo.title = getText(R.string.folder_name);
 
@@ -1880,7 +1816,7 @@
         final int[] cellXY = mTmpAddItemCellCoordinates;
         if (!layout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectCellX, intersectCellY)) {
             showOutOfSpaceMessage();
-            return;
+            return null;
         }
 
         // Update the model
@@ -1894,6 +1830,7 @@
                 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()),
                 folderInfo, mIconCache);
         mWorkspace.addInScreen(newFolder, screen, cellXY[0], cellXY[1], 1, 1, isWorkspaceLocked());
+        return newFolder;
     }
 
     void removeFolder(FolderInfo folder) {
@@ -2645,7 +2582,6 @@
     
     private void showAndEnableToolbarButton(View button) {
         button.setVisibility(View.VISIBLE);
-        button.setFocusable(true);
         button.setClickable(true);
     }
 
@@ -2656,7 +2592,6 @@
     }
 
     private void disableToolbarButton(View button) {
-        button.setFocusable(false);
         button.setClickable(false);
     }
 
@@ -2764,7 +2699,7 @@
      * of the screen.
      * @param toState The state to zoom out to. Must be ALL_APPS or CUSTOMIZE.
      */
-    private void cameraZoomOut(State toState, boolean animated) {
+    private void cameraZoomOut(State toState, boolean animated, boolean springLoaded) {
         final Resources res = getResources();
         final boolean toAllApps = (toState == State.ALL_APPS);
 
@@ -2784,7 +2719,15 @@
         setPivotsForZoom(toView, toState, scale);
 
         if (toAllApps) {
-            mWorkspace.shrink(ShrinkState.BOTTOM_HIDDEN, animated);
+            if (!springLoaded) {
+                mWorkspace.shrink(ShrinkState.BOTTOM_HIDDEN, animated);
+
+                // Everytime we launch into AllApps, we reset the successful drop flag which
+                // controls when it should hide/show the mini workspaces
+                mAllAppsPagedView.resetSuccessfulDropFlag();
+            } else {
+                mWorkspace.shrink(ShrinkState.BOTTOM_VISIBLE, animated);
+            }
         } else {
             mWorkspace.shrink(ShrinkState.TOP, animated);
         }
@@ -2792,10 +2735,8 @@
         if (animated) {
             final ValueAnimator scaleAnim = ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
             scaleAnim.setInterpolator(new Workspace.ZoomOutInterpolator());
-            scaleAnim.addUpdateListener(new AnimatorUpdateListener() {
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    final float b = (Float) animation.getAnimatedValue();
-                    final float a = 1f - b;
+            scaleAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
+                public void onAnimationUpdate(float a, float b) {
                     ((View) toView.getParent()).fastInvalidate();
                     toView.setFastScaleX(a * scale + b * 1f);
                     toView.setFastScaleY(a * scale + b * 1f);
@@ -2806,10 +2747,8 @@
                 toView.setFastAlpha(0f);
                 ValueAnimator alphaAnim = ValueAnimator.ofFloat(0f, 1f).setDuration(fadeDuration);
                 alphaAnim.setInterpolator(new DecelerateInterpolator(1.5f));
-                alphaAnim.addUpdateListener(new AnimatorUpdateListener() {
-                    public void onAnimationUpdate(ValueAnimator animation) {
-                        final float b = (Float) animation.getAnimatedValue();
-                        final float a = 1f - b;
+                alphaAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
+                    public void onAnimationUpdate(float a, float b) {
                         // don't need to invalidate because we do so above
                         toView.setFastAlpha(a * 0f + b * 1f);
                     }
@@ -2880,10 +2819,6 @@
      * @param fromState The current state (must be ALL_APPS or CUSTOMIZE).
      * @param animated If true, the transition will be animated.
      */
-    private void cameraZoomIn(State fromState, boolean animated) {
-        cameraZoomIn(fromState, animated, false);
-    }
-
     private void cameraZoomIn(State fromState, boolean animated, boolean springLoaded) {
         Resources res = getResources();
         final boolean fromAllApps = (fromState == State.ALL_APPS);
@@ -2906,7 +2841,6 @@
         if (!springLoaded) {
             mWorkspace.unshrink(animated);
         }
-
         if (animated) {
             if (mStateAnimation != null) mStateAnimation.cancel();
             mStateAnimation = new AnimatorSet();
@@ -2916,10 +2850,8 @@
 
             ValueAnimator scaleAnim = ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
             scaleAnim.setInterpolator(new Workspace.ZoomInInterpolator());
-            scaleAnim.addUpdateListener(new AnimatorUpdateListener() {
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    final float b = (Float) animation.getAnimatedValue();
-                    final float a = 1f - b;
+            scaleAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
+                public void onAnimationUpdate(float a, float b) {
                     ((View)fromView.getParent()).fastInvalidate();
                     fromView.setFastScaleX(a * oldScaleX + b * scaleFactor);
                     fromView.setFastScaleY(a * oldScaleY + b * scaleFactor);
@@ -2928,10 +2860,8 @@
             final ValueAnimator alphaAnim = ValueAnimator.ofFloat(0f, 1f);
             alphaAnim.setDuration(res.getInteger(R.integer.config_allAppsFadeOutTime));
             alphaAnim.setInterpolator(new DecelerateInterpolator(1.5f));
-            alphaAnim.addUpdateListener(new AnimatorUpdateListener() {
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    final float b = (Float) animation.getAnimatedValue();
-                    final float a = 1f - b;
+            alphaAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
+                public void onAnimationUpdate(float a, float b) {
                     // don't need to invalidate because we do so above
                     fromView.setFastAlpha(a * 1f + b * 0f);
                 }
@@ -2980,7 +2910,7 @@
         }
 
         if (LauncherApplication.isScreenXLarge()) {
-            cameraZoomOut(State.ALL_APPS, animated);
+            cameraZoomOut(State.ALL_APPS, animated, false);
         } else {
             mAllAppsGrid.zoom(1.0f, animated);
         }
@@ -3034,11 +2964,11 @@
     void enterSpringLoadedDragMode(CellLayout layout) {
         mWorkspace.enterSpringLoadedDragMode(layout);
         if (mState == State.ALL_APPS) {
-            cameraZoomIn(State.ALL_APPS, true, true);
             mState = State.ALL_APPS_SPRING_LOADED;
+            cameraZoomIn(State.ALL_APPS, true, true);
         } else if (mState == State.CUSTOMIZE) {
-            cameraZoomIn(State.CUSTOMIZE, true, true);
             mState = State.CUSTOMIZE_SPRING_LOADED;
+            cameraZoomIn(State.CUSTOMIZE, true, true);
         }/* else {
             // we're already in spring loaded mode; don't do anything
         }*/
@@ -3047,11 +2977,11 @@
     void exitSpringLoadedDragMode() {
         if (mState == State.ALL_APPS_SPRING_LOADED) {
             mWorkspace.exitSpringLoadedDragMode(Workspace.ShrinkState.BOTTOM_VISIBLE);
-            cameraZoomOut(State.ALL_APPS, true);
+            cameraZoomOut(State.ALL_APPS, true, true);
             mState = State.ALL_APPS;
         } else if (mState == State.CUSTOMIZE_SPRING_LOADED) {
             mWorkspace.exitSpringLoadedDragMode(Workspace.ShrinkState.TOP);
-            cameraZoomOut(State.CUSTOMIZE, true);
+            cameraZoomOut(State.CUSTOMIZE, true, true);
             mState = State.CUSTOMIZE;
         }/* else {
             // we're not in spring loaded mode; don't do anything
@@ -3101,12 +3031,12 @@
         if (mState == State.ALL_APPS || mState == State.ALL_APPS_SPRING_LOADED) {
             mWorkspace.setVisibility(View.VISIBLE);
             if (LauncherApplication.isScreenXLarge()) {
-                cameraZoomIn(State.ALL_APPS, animated);
+                cameraZoomIn(State.ALL_APPS, animated, false);
             } else {
                 mAllAppsGrid.zoom(0.0f, animated);
             }
             ((View)mAllAppsGrid).setFocusable(false);
-            mWorkspace.getChildAt(mWorkspace.getCurrentPage()).requestFocus();
+            ((CellLayout) mWorkspace.getChildAt(mWorkspace.getCurrentPage())).getChildrenLayout().requestFocus();
         }
     }
 
@@ -3124,7 +3054,7 @@
             return;
         }
 
-        cameraZoomOut(State.CUSTOMIZE, animated);
+        cameraZoomOut(State.CUSTOMIZE, animated, false);
 
         // Change the state *after* we've called all the transition code
         mState = State.CUSTOMIZE;
@@ -3137,7 +3067,7 @@
     // Hide the customization drawer (only exists in x-large configuration)
     void hideCustomizationDrawer(boolean animated) {
         if (mState == State.CUSTOMIZE || mState == State.CUSTOMIZE_SPRING_LOADED) {
-            cameraZoomIn(State.CUSTOMIZE, animated);
+            cameraZoomIn(State.CUSTOMIZE, animated, false);
         }
     }
 
@@ -3220,14 +3150,20 @@
 
     private void updateGlobalSearchIcon() {
         if (LauncherApplication.isScreenXLarge()) {
+            final View searchButton = findViewById(R.id.search_button);
+            final View searchDivider = findViewById(R.id.search_divider);
+
             final SearchManager searchManager =
                     (SearchManager) getSystemService(Context.SEARCH_SERVICE);
             ComponentName activityName = searchManager.getGlobalSearchActivity();
             if (activityName != null) {
                 sGlobalSearchIcon = updateButtonWithIconFromExternalActivity(
                         R.id.search_button, activityName, R.drawable.ic_generic_search);
+                searchButton.setVisibility(View.VISIBLE);
+                searchDivider.setVisibility(View.VISIBLE);
             } else {
-                findViewById(R.id.search_button).setVisibility(View.GONE);
+                searchButton.setVisibility(View.GONE);
+                searchDivider.setVisibility(View.GONE);
             }
         }
     }
@@ -3238,13 +3174,19 @@
 
     private void updateVoiceSearchIcon() {
         if (LauncherApplication.isScreenXLarge()) {
+            final View searchDivider = findViewById(R.id.search_divider);
+            final View voiceButton = findViewById(R.id.voice_button);
+
             Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
             ComponentName activityName = intent.resolveActivity(getPackageManager());
             if (activityName != null) {
                 sVoiceSearchIcon = updateButtonWithIconFromExternalActivity(
                         R.id.voice_button, activityName, R.drawable.ic_voice_search);
+                searchDivider.setVisibility(View.VISIBLE);
+                voiceButton.setVisibility(View.VISIBLE);
             } else {
-                findViewById(R.id.voice_button).setVisibility(View.GONE);
+                searchDivider.setVisibility(View.GONE);
+                voiceButton.setVisibility(View.GONE);
             }
         }
     }
@@ -3455,6 +3397,8 @@
      */
     public void startBinding() {
         final Workspace workspace = mWorkspace;
+
+        mWorkspace.clearDropTargets();
         int count = workspace.getChildCount();
         for (int i = 0; i < count; i++) {
             // Use removeAllViewsInLayout() to avoid an extra requestLayout() and invalidate().
diff --git a/src/com/android/launcher2/LauncherAnimatorUpdateListener.java b/src/com/android/launcher2/LauncherAnimatorUpdateListener.java
new file mode 100644
index 0000000..dd82113
--- /dev/null
+++ b/src/com/android/launcher2/LauncherAnimatorUpdateListener.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package com.android.launcher2;
+
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+
+abstract class LauncherAnimatorUpdateListener implements AnimatorUpdateListener {
+    public void onAnimationUpdate(ValueAnimator animation) {
+        final float b = (Float) animation.getAnimatedValue();
+        final float a = 1f - b;
+        onAnimationUpdate(a, b);
+    }
+
+    abstract void onAnimationUpdate(float a, float b);
+}
\ No newline at end of file
diff --git a/src/com/android/launcher2/LauncherAppWidgetHostView.java b/src/com/android/launcher2/LauncherAppWidgetHostView.java
index 7f60cac..0dd1d83 100644
--- a/src/com/android/launcher2/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher2/LauncherAppWidgetHostView.java
@@ -22,6 +22,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
+import android.view.ViewGroup;
 
 import com.android.launcher.R;
 
@@ -122,4 +123,9 @@
         }
         super.onVisibilityChanged(changedView, visibility);
     }
+
+    @Override
+    public int getDescendantFocusability() {
+        return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
+    }
 }
diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java
index c098749..e432733 100644
--- a/src/com/android/launcher2/LauncherModel.java
+++ b/src/com/android/launcher2/LauncherModel.java
@@ -91,9 +91,6 @@
 
     private AllAppsList mAllAppsList; // only access in worker thread
     private IconCache mIconCache;
-    final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();
-    final ArrayList<LauncherAppWidgetInfo> mAppWidgets = new ArrayList<LauncherAppWidgetInfo>();
-    final HashMap<Long, FolderInfo> mFolders = new HashMap<Long, FolderInfo>();
 
     private Bitmap mDefaultIcon;
 
@@ -522,9 +519,6 @@
                 mLoaderTask.stopLocked();
             }
         }
-        mItems.clear();
-        mAppWidgets.clear();
-        mFolders.clear();
     }
 
     /**
@@ -540,6 +534,10 @@
         private boolean mStopped;
         private boolean mLoadAndBindStepFinished;
 
+        final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();
+        final ArrayList<LauncherAppWidgetInfo> mAppWidgets = new ArrayList<LauncherAppWidgetInfo>();
+        final HashMap<Long, FolderInfo> mFolders = new HashMap<Long, FolderInfo>();
+
         LoaderTask(Context context, boolean isLaunching) {
             mContext = context;
             mIsLaunching = isLaunching;
@@ -1294,6 +1292,7 @@
             Log.d(TAG, "mLoaderTask.mIsLaunching=" + mIsLaunching);
             Log.d(TAG, "mLoaderTask.mStopped=" + mStopped);
             Log.d(TAG, "mLoaderTask.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished);
+            Log.d(TAG, "mItems size=" + mItems.size());
         }
     }
 
@@ -1797,7 +1796,6 @@
         ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.added", mAllAppsList.added);
         ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.removed", mAllAppsList.removed);
         ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.modified", mAllAppsList.modified);
-        Log.d(TAG, "mItems size=" + mItems.size());
         if (mLoaderTask != null) {
             mLoaderTask.dumpState();
         } else {
diff --git a/src/com/android/launcher2/LiveFolder.java b/src/com/android/launcher2/LiveFolder.java
index 07a295f..b692953 100644
--- a/src/com/android/launcher2/LiveFolder.java
+++ b/src/com/android/launcher2/LiveFolder.java
@@ -22,10 +22,12 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.AdapterView;
+import android.widget.BaseAdapter;
 import android.net.Uri;
 import android.provider.LiveFolders;
 import android.os.AsyncTask;
 import android.database.Cursor;
+import android.widget.GridView;
 
 import java.lang.ref.WeakReference;
 
@@ -33,6 +35,7 @@
 
 public class LiveFolder extends Folder {
     private AsyncTask<LiveFolderInfo,Void,Cursor> mLoadingTask;
+    protected GridView mContent;
 
     public LiveFolder(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -44,6 +47,15 @@
         return (LiveFolder) LayoutInflater.from(context).inflate(layout, null);
     }
 
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mContent = (GridView) findViewById(R.id.folder_content);
+        mContent.setOnItemClickListener(this);
+        mContent.setOnItemLongClickListener(this);
+    }
+
     private static boolean isDisplayModeList(FolderInfo folderInfo) {
         return ((LiveFolderInfo) folderInfo).displayMode ==
                 LiveFolders.DISPLAY_MODE_LIST;
@@ -68,6 +80,21 @@
         }
     }
 
+    /**
+     * Sets the adapter used to populate the content area. The adapter must only
+     * contains ShortcutInfo items.
+     *
+     * @param adapter The list of applications to display in the folder.
+     */
+    void setContentAdapter(BaseAdapter adapter) {
+        mContent.setAdapter(adapter);
+    }
+
+    @Override
+    void notifyDataSetChanged() {
+        ((BaseAdapter) mContent.getAdapter()).notifyDataSetChanged();
+    }
+
     @Override
     public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
         return false;
@@ -84,6 +111,9 @@
     @Override
     void onOpen() {
         super.onOpen();
+        // When the folder opens, we need to refresh the GridView's selection by
+        // forcing a layout
+        mContent.requestLayout();
         requestFocus();
     }
 
diff --git a/src/com/android/launcher2/PagedView.java b/src/com/android/launcher2/PagedView.java
index e7ecb99..9eae647 100644
--- a/src/com/android/launcher2/PagedView.java
+++ b/src/com/android/launcher2/PagedView.java
@@ -110,6 +110,7 @@
     protected int mPageLayoutPaddingRight;
     protected int mPageLayoutWidthGap;
     protected int mPageLayoutHeightGap;
+    protected int mPageLayoutMaxHeight;
     protected int mCellCountX;
     protected int mCellCountY;
     protected boolean mCenterPagesVertically;
@@ -189,6 +190,8 @@
                 R.styleable.PagedView_pageLayoutWidthGap, -1);
         mPageLayoutHeightGap = a.getDimensionPixelSize(
                 R.styleable.PagedView_pageLayoutHeightGap, -1);
+        mPageLayoutMaxHeight = a.getDimensionPixelSize(
+                R.styleable.PagedView_pageLayoutMaxHeight, -1);
         a.recycle();
 
         setHapticFeedbackEnabled(false);
@@ -289,6 +292,10 @@
         mIsPageMoving = false;
     }
 
+    protected boolean isPageMoving() {
+        return mIsPageMoving;
+    }
+
     // a method that subclasses can override to add behavior
     protected void onPageBeginMoving() {
     }
@@ -384,6 +391,10 @@
 
         final int verticalPadding = mPaddingTop + mPaddingBottom;
 
+        if (mPageLayoutMaxHeight != -1) {
+            heightSize = Math.min(mPageLayoutMaxHeight, heightSize);
+        }
+
         // The children are given the same width and height as the workspace
         // unless they were set to WRAP_CONTENT
         final int childCount = getChildCount();
@@ -1155,7 +1166,7 @@
     public void requestChildFocus(View child, View focused) {
         super.requestChildFocus(child, focused);
         int page = indexOfChild(child);
-        if (page >= 0 && !isInTouchMode()) {
+        if (page >= 0 && page != getCurrentPage() && !isInTouchMode()) {
             snapToPage(page);
         }
     }
@@ -1177,6 +1188,9 @@
     protected void setMinimumWidthOverride(int minimumWidth) {
         mMinimumWidth = minimumWidth;
     }
+    protected void resetMinimumWidthOverride() {
+        mMinimumWidth = 0;
+    }
 
     protected int getChildWidth(int index) {
         return Math.max(mMinimumWidth, getChildAt(index).getMeasuredWidth());
diff --git a/src/com/android/launcher2/PagedViewCellLayout.java b/src/com/android/launcher2/PagedViewCellLayout.java
index 28bb78b..28e092e 100644
--- a/src/com/android/launcher2/PagedViewCellLayout.java
+++ b/src/com/android/launcher2/PagedViewCellLayout.java
@@ -180,6 +180,14 @@
         return mChildren.indexOfChild(v);
     }
 
+    public int getCellCountX() {
+        return mCellCountX;
+    }
+
+    public int getCellCountY() {
+        return mCellCountY;
+    }
+
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         // TODO: currently ignoring padding
 
@@ -243,6 +251,18 @@
         setMeasuredDimension(newWidth, newHeight);
     }
 
+    int getContentWidth() {
+        // Return the distance from the left edge of the content of the leftmost icon to
+        // the right edge of the content of the rightmost icon
+
+        // icons are centered within cells, find out how much padding that accounts for
+        return getWidthBeforeFirstLayout() - (mCellWidth - Utilities.getIconContentSize());
+    }
+
+    int getWidthBeforeFirstLayout() {
+        return mCellCountX * mCellWidth + (mCellCountX - 1) * mWidthGap;
+    }
+
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         int count = getChildCount();
@@ -281,17 +301,6 @@
         mHolographicChildren.setGap(widthGap, heightGap);
     }
 
-    public void setCellDimensions(int width, int height) {
-        mCellWidth = width;
-        mCellHeight = height;
-        mChildren.setCellDimensions(width, height);
-        mHolographicChildren.setCellDimensions(width, height);
-    }
-
-    public int getDefaultCellDimensions() {
-        return sDefaultCellDimensions;
-    }
-
     public int[] getCellCountForDimensions(int width, int height) {
         // Always assume we're working with the smallest span to make sure we
         // reserve enough space in both orientations
diff --git a/src/com/android/launcher2/PagedViewExtendedLayout.java b/src/com/android/launcher2/PagedViewExtendedLayout.java
index e54d261..81f1498 100644
--- a/src/com/android/launcher2/PagedViewExtendedLayout.java
+++ b/src/com/android/launcher2/PagedViewExtendedLayout.java
@@ -26,7 +26,7 @@
  * The linear layout used strictly for the widget/wallpaper tab of the customization tray
  */
 public class PagedViewExtendedLayout extends LinearLayout implements Page {
-    static final String TAG = "PagedViewWidgetLayout";
+    static final String TAG = "PagedViewExtendedLayout";
 
     public PagedViewExtendedLayout(Context context) {
         this(context, null);
diff --git a/src/com/android/launcher2/PagedViewIcon.java b/src/com/android/launcher2/PagedViewIcon.java
index bde6559..f46b63c 100644
--- a/src/com/android/launcher2/PagedViewIcon.java
+++ b/src/com/android/launcher2/PagedViewIcon.java
@@ -31,6 +31,7 @@
 import android.os.HandlerThread;
 import android.os.Message;
 import android.util.AttributeSet;
+import android.view.KeyEvent;
 import android.widget.Checkable;
 import android.widget.TextView;
 
@@ -128,8 +129,6 @@
             mCheckedFadeOutDuration = r.getInteger(R.integer.icon_allAppsCustomizeFadeOutTime);
         }
 
-        setFocusable(true);
-        setBackgroundDrawable(null);
         mHolographicOutlineView = new HolographicPagedViewIcon(context, this);
     }
 
@@ -245,6 +244,18 @@
     }
 
     @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        return FocusHelper.handlePagedViewIconKeyEvent(this, keyCode, event)
+                || super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        return FocusHelper.handlePagedViewIconKeyEvent(this, keyCode, event)
+                || super.onKeyUp(keyCode, event);
+    }
+
+    @Override
     public boolean isChecked() {
         return mIsChecked;
     }
diff --git a/src/com/android/launcher2/PagedViewWidget.java b/src/com/android/launcher2/PagedViewWidget.java
index 72f928b..c2d609e 100644
--- a/src/com/android/launcher2/PagedViewWidget.java
+++ b/src/com/android/launcher2/PagedViewWidget.java
@@ -36,6 +36,7 @@
 import android.os.HandlerThread;
 import android.os.Message;
 import android.util.AttributeSet;
+import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 import android.widget.Checkable;
@@ -228,6 +229,18 @@
     }
 
     @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        return FocusHelper.handlePagedViewWidgetKeyEvent(this, keyCode, event)
+                || super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        return FocusHelper.handlePagedViewWidgetKeyEvent(this, keyCode, event)
+                || super.onKeyUp(keyCode, event);
+    }
+
+    @Override
     protected void onDraw(Canvas canvas) {
         if (mAlpha > 0) {
             super.onDraw(canvas);
diff --git a/src/com/android/launcher2/ShortcutsAdapter.java b/src/com/android/launcher2/ShortcutsAdapter.java
index 93c500a..de73c3e 100644
--- a/src/com/android/launcher2/ShortcutsAdapter.java
+++ b/src/com/android/launcher2/ShortcutsAdapter.java
@@ -30,7 +30,7 @@
 /**
  * GridView adapter to show the list of applications and shortcuts
  */
-public class ShortcutsAdapter  extends ArrayAdapter<ShortcutInfo> {
+public class ShortcutsAdapter extends ArrayAdapter<ShortcutInfo> {
     private final LayoutInflater mInflater;
     private final IconCache mIconCache;
 
diff --git a/src/com/android/launcher2/UserFolder.java b/src/com/android/launcher2/UserFolder.java
index 251b3f9..b800b07 100644
--- a/src/com/android/launcher2/UserFolder.java
+++ b/src/com/android/launcher2/UserFolder.java
@@ -1,11 +1,18 @@
 package com.android.launcher2;
 
 import android.content.Context;
+import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.GridView;
+import android.widget.TextView;
 
 import com.android.launcher.R;
+import java.util.ArrayList;
 
 /**
  * Folder which contains applications or shortcuts chosen by the user.
@@ -14,8 +21,21 @@
 public class UserFolder extends Folder implements DropTarget {
     private static final String TAG = "Launcher.UserFolder";
 
+    protected CellLayout mContent;
+    private final LayoutInflater mInflater;
+    private final IconCache mIconCache;
+
     public UserFolder(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mInflater = LayoutInflater.from(context);
+        mIconCache = ((LauncherApplication)context.getApplicationContext()).getIconCache();
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mContent = (CellLayout) findViewById(R.id.folder_content);
     }
     
     /**
@@ -29,6 +49,48 @@
         return (UserFolder) LayoutInflater.from(context).inflate(R.layout.user_folder, null);
     }
 
+    @Override
+    void notifyDataSetChanged() {
+        // recreate all the children if the data set changes under us. We may want to do this more
+        // intelligently (ie just removing the views that should no longer exist)
+        mContent.removeAllViewsInLayout();
+        bind(mInfo);
+    }
+
+    public void onClick(View v) {
+        Object tag = v.getTag();
+        if (tag instanceof ShortcutInfo) {
+            // refactor this code from Folder
+            ShortcutInfo item = (ShortcutInfo) tag;
+            int[] pos = new int[2];
+            v.getLocationOnScreen(pos);
+            item.intent.setSourceBounds(new Rect(pos[0], pos[1],
+                    pos[0] + v.getWidth(), pos[1] + v.getHeight()));
+            mLauncher.startActivitySafely(item.intent, item);
+        } else {
+            super.onClick(v);
+        }
+    }
+
+    public boolean onLongClick(View v) {
+        Object tag = v.getTag();
+        if (tag instanceof ShortcutInfo) {
+         // refactor this code from Folder
+            ShortcutInfo item = (ShortcutInfo) tag;
+            if (!v.isInTouchMode()) {
+                return false;
+            }
+
+            mDragController.startDrag(v, this, item, DragController.DRAG_ACTION_COPY);
+            mLauncher.closeFolder(this);
+            mDragItem = item;
+
+            return true;
+        } else {
+            return super.onLongClick(v);
+        }
+    }
+
     public boolean acceptDrop(DragSource source, int x, int y, int xOffset, int yOffset,
             DragView dragView, Object dragInfo) {
         final ItemInfo item = (ItemInfo) dragInfo;
@@ -44,11 +106,45 @@
         if (dragInfo instanceof ApplicationInfo) {
             // Came from all apps -- make a copy
             item = ((ApplicationInfo)dragInfo).makeShortcut();
+            item.spanX = 1;
+            item.spanY = 1;
         } else {
             item = (ShortcutInfo)dragInfo;
         }
-        ((ShortcutsAdapter)mContent.getAdapter()).add(item);
-        LauncherModel.addOrMoveItemInDatabase(mLauncher, item, mInfo.id, 0, 0, 0);
+        findAndSetEmptyCells(item);
+        ((UserFolderInfo)mInfo).add(item);
+        createAndAddShortcut(item);
+        LauncherModel.addOrMoveItemInDatabase(mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
+    }
+
+    protected boolean findAndSetEmptyCells(ShortcutInfo item) {
+        int[] emptyCell = new int[2];
+        if (mContent.findCellForSpan(emptyCell, item.spanX, item.spanY)) {
+            item.cellX = emptyCell[0];
+            item.cellY = emptyCell[1];
+            LauncherModel.addOrMoveItemInDatabase(
+                    mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    protected void createAndAddShortcut(ShortcutInfo item) {
+        final TextView textView =
+            (TextView) mInflater.inflate(R.layout.application_boxed, this, false);
+        textView.setCompoundDrawablesWithIntrinsicBounds(null,
+                new FastBitmapDrawable(item.getIcon(mIconCache)), null, null);
+        textView.setText(item.title);
+        textView.setTag(item);
+
+        textView.setOnClickListener(this);
+        textView.setOnLongClickListener(this);
+
+        CellLayout.LayoutParams lp =
+            new CellLayout.LayoutParams(item.cellX, item.cellY, item.spanX, item.spanY);
+        boolean insert = false;
+        mContent.addViewToCellLayout(textView, insert ? 0 : -1, (int)item.id, lp, true);
     }
 
     public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset,
@@ -66,8 +162,7 @@
     @Override
     public void onDropCompleted(View target, Object dragInfo, boolean success) {
         if (success) {
-            ShortcutsAdapter adapter = (ShortcutsAdapter)mContent.getAdapter();
-            adapter.remove(mDragItem);
+            ((UserFolderInfo)mInfo).remove(mDragItem);
         }
     }
 
@@ -77,14 +172,24 @@
 
     void bind(FolderInfo info) {
         super.bind(info);
-        setContentAdapter(new ShortcutsAdapter(mContext, ((UserFolderInfo) info).contents));
+        ArrayList<ShortcutInfo> children = ((UserFolderInfo)info).contents;
+        for (int i = 0; i < children.size(); i++) {
+            ShortcutInfo child = (ShortcutInfo) children.get(i);
+            if ((child.cellX == -1 && child.cellY == -1) ||
+                    mContent.isOccupied(child.cellX, child.cellY)) {
+                findAndSetEmptyCells(child);
+            }
+            createAndAddShortcut((ShortcutInfo) children.get(i));
+        }
     }
 
-    // When the folder opens, we need to refresh the GridView's selection by
-    // forcing a layout
     @Override
     void onOpen() {
         super.onOpen();
+        // When the folder opens, we need to refresh the GridView's selection by
+        // forcing a layout
+        // TODO: find out if this is still necessary
+        mContent.requestLayout();
         requestFocus();
     }
 
diff --git a/src/com/android/launcher2/Utilities.java b/src/com/android/launcher2/Utilities.java
index 60f71f5..cd98cab 100644
--- a/src/com/android/launcher2/Utilities.java
+++ b/src/com/android/launcher2/Utilities.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher2;
 
+import java.util.Random;
+
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -51,6 +53,7 @@
 
     private static int sIconWidth = -1;
     private static int sIconHeight = -1;
+    private static int sIconContentSize = -1;
     private static int sIconTextureWidth = -1;
     private static int sIconTextureHeight = -1;
 
@@ -90,6 +93,10 @@
     static int sColors[] = { 0xffff0000, 0xff00ff00, 0xff0000ff };
     static int sColorIndex = 0;
 
+    static int getIconContentSize() {
+        return sIconContentSize;
+    }
+
     /**
      * Returns a bitmap suitable for the all apps view.  The bitmap will be a power
      * of two sized ARGB_8888 bitmap that can be used as a gl texture.
@@ -236,6 +243,9 @@
         final float density = metrics.density;
 
         sIconWidth = sIconHeight = (int) resources.getDimension(R.dimen.app_icon_size);
+        if (LauncherApplication.isScreenXLarge()) {
+            sIconContentSize = (int) resources.getDimension(R.dimen.app_icon_content_size);
+        }
         sIconTextureWidth = sIconTextureHeight = sIconWidth + 2;
 
         sBlurPaint.setMaskFilter(new BlurMaskFilter(5 * density, BlurMaskFilter.Blur.NORMAL));
@@ -381,4 +391,8 @@
         }
         return n;
     }
+
+    static int generateRandomId() {
+        return new Random(System.currentTimeMillis()).nextInt(1 << 24);
+    }
 }
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index e7865d2..98cf62b 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -155,6 +155,7 @@
     // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
     private int[] mTempCell = new int[2];
     private int[] mTempEstimate = new int[2];
+    private float[] mDragViewVisualCenter = new float[2];
     private float[] mTempOriginXY = new float[2];
     private float[] mTempDragCoordinates = new float[2];
     private float[] mTempTouchCoordinates = new float[2];
@@ -213,6 +214,7 @@
     WallpaperOffsetInterpolator mWallpaperOffset;
     boolean mUpdateWallpaperOffsetImmediately = false;
     boolean mSyncWallpaperOffsetWithScroll = true;
+    private Runnable mDelayedResizeRunnable;
 
     // info about the last drag
     private DragView mLastDragView;
@@ -340,58 +342,41 @@
         }
     }
 
-    @Override
-    public void addView(View child, int index, LayoutParams params) {
+    private void onAddView(View child) {
         if (!(child instanceof CellLayout)) {
             throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
         }
         ((CellLayout) child).setOnInterceptTouchListener(this);
         child.setOnClickListener(this);
         child.setClickable(true);
+    }
+    @Override
+    public void addView(View child, int index, LayoutParams params) {
+        onAddView(child);
         super.addView(child, index, params);
     }
 
     @Override
     public void addView(View child) {
-        if (!(child instanceof CellLayout)) {
-            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
-        }
-        ((CellLayout) child).setOnInterceptTouchListener(this);
-        child.setOnClickListener(this);
-        child.setClickable(true);
+        onAddView(child);
         super.addView(child);
     }
 
     @Override
     public void addView(View child, int index) {
-        if (!(child instanceof CellLayout)) {
-            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
-        }
-        ((CellLayout) child).setOnInterceptTouchListener(this);
-        child.setOnClickListener(this);
-        child.setClickable(true);
+        onAddView(child);
         super.addView(child, index);
     }
 
     @Override
     public void addView(View child, int width, int height) {
-        if (!(child instanceof CellLayout)) {
-            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
-        }
-        ((CellLayout) child).setOnInterceptTouchListener(this);
-        child.setOnClickListener(this);
-        child.setClickable(true);
+        onAddView(child);
         super.addView(child, width, height);
     }
 
     @Override
     public void addView(View child, LayoutParams params) {
-        if (!(child instanceof CellLayout)) {
-            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
-        }
-        ((CellLayout) child).setOnInterceptTouchListener(this);
-        child.setOnClickListener(this);
-        child.setClickable(true);
+        onAddView(child);
         super.addView(child, params);
     }
 
@@ -645,6 +630,11 @@
         }
         mOverScrollMaxBackgroundAlpha = 0.0f;
         mOverScrollPageIndex = -1;
+
+        if (mDelayedResizeRunnable != null) {
+            mDelayedResizeRunnable.run();
+            mDelayedResizeRunnable = null;
+        }
     }
 
     @Override
@@ -1067,6 +1057,9 @@
 
     @Override
     protected void screenScrolled(int screenCenter) {
+        // If the screen is not xlarge, then don't rotate the CellLayouts
+        if (!LauncherApplication.isScreenXLarge()) return;
+
         final int halfScreenSize = getMeasuredWidth() / 2;
 
         for (int i = 0; i < getChildCount(); i++) {
@@ -1244,6 +1237,14 @@
     }
 
     @Override
+    public int getDescendantFocusability() {
+        if (mIsSmall) {
+            return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
+        }
+        return super.getDescendantFocusability();
+    }
+
+    @Override
     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
         if (!mLauncher.isAllAppsVisible()) {
             final Folder openFolder = getOpenFolder();
@@ -1392,14 +1393,6 @@
 
     // we use this to shrink the workspace for the all apps view and the customize view
     public void shrink(ShrinkState shrinkState, boolean animated) {
-        // In the launcher interaction model, we're never in the state where we're shrunken and
-        // visible in the bottom of the screen, and then want to fade to being invisible.
-        // After spring loaded mode ends, this method was getting called twice, the first time
-        // with BOTTOM_VISIBLE (what we want) and a second time with BOTTOM_INVISIBLE (not
-        // what we want). As a temporary solution, we just change the second call to BOTTOM_VISIBLE
-        if (mIsSmall && mShrinkState == ShrinkState.BOTTOM_VISIBLE) {
-            shrinkState = ShrinkState.BOTTOM_VISIBLE;
-        }
         if (mFirstLayout) {
             // (mFirstLayout == "first layout has not happened yet")
             // if we get a call to shrink() as part of our initialization (for example, if
@@ -1591,10 +1584,8 @@
             final float oldVerticalWallpaperOffset = getVerticalWallpaperOffset();
             final float newHorizontalWallpaperOffset = 0.5f;
             final float newVerticalWallpaperOffset = wallpaperOffset;
-            animWithInterpolator.addUpdateListener(new AnimatorUpdateListener() {
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    final float b = (Float) animation.getAnimatedValue();
-                    final float a = 1f - b;
+            animWithInterpolator.addUpdateListener(new LauncherAnimatorUpdateListener() {
+                public void onAnimationUpdate(float a, float b) {
                     if (b == 0f) {
                         // an optimization, and required for correct behavior.
                         return;
@@ -1708,26 +1699,32 @@
         for (int i = 0; i < screenCount; i++) {
             CellLayout cl = (CellLayout) getChildAt(i);
             cl.setIsDragOccuring(isDragHappening);
-            switch (state) {
-                case TOP:
-                    cl.setIsDefaultDropTarget(i == mCurrentPage);
-                case BOTTOM_HIDDEN:
-                case BOTTOM_VISIBLE:
-                case SPRING_LOADED:
-                    if (state != ShrinkState.TOP) {
-                        cl.setIsDefaultDropTarget(false);
-                    }
-                    if (!isDragHappening) {
-                        // even if a drag isn't happening, we don't want to show a screen as
-                        // accepting drops if it doesn't have at least one free cell
-                        spanX = 1;
-                        spanY = 1;
-                    }
-                    // the page accepts drops if we can find at least one empty spot
-                    cl.setAcceptsDrops(cl.findCellForSpan(null, spanX, spanY));
-                    break;
-                default:
-                     throw new RuntimeException("Unhandled ShrinkState " + state);
+            if (state == null) {
+                // If we are not in a shrunken state, mark all cell layouts as droppable (if they
+                // have the space)
+                cl.setAcceptsDrops(cl.findCellForSpan(null, spanX, spanY));
+            } else {
+                switch (state) {
+                    case TOP:
+                        cl.setIsDefaultDropTarget(i == mCurrentPage);
+                    case BOTTOM_HIDDEN:
+                    case BOTTOM_VISIBLE:
+                    case SPRING_LOADED:
+                        if (state != ShrinkState.TOP) {
+                            cl.setIsDefaultDropTarget(false);
+                        }
+                        if (!isDragHappening) {
+                            // even if a drag isn't happening, we don't want to show a screen as
+                            // accepting drops if it doesn't have at least one free cell
+                            spanX = 1;
+                            spanY = 1;
+                        }
+                        // the page accepts drops if we can find at least one empty spot
+                        cl.setAcceptsDrops(cl.findCellForSpan(null, spanX, spanY));
+                        break;
+                    default:
+                         throw new RuntimeException("Unhandled ShrinkState " + state);
+                }
             }
         }
     }
@@ -1800,8 +1797,8 @@
     }
 
     public void exitWidgetResizeMode() {
-        final CellLayout currentLayout = (CellLayout) getChildAt(getCurrentPage());
-        currentLayout.getChildrenLayout().clearAllResizeFrames();
+        DragLayer dragLayer = (DragLayer) mLauncher.findViewById(R.id.drag_layer);
+        dragLayer.clearAllResizeFrames();
     }
 
     void unshrink(boolean animated) {
@@ -1930,10 +1927,8 @@
                 final float oldVerticalWallpaperOffset = getVerticalWallpaperOffset();
                 final float newHorizontalWallpaperOffset = wallpaperOffsetForCurrentScroll();
                 final float newVerticalWallpaperOffset = 0.5f;
-                animWithInterpolator.addUpdateListener(new AnimatorUpdateListener() {
-                    public void onAnimationUpdate(ValueAnimator animation) {
-                        final float b = (Float) animation.getAnimatedValue();
-                        final float a = 1f - b;
+                animWithInterpolator.addUpdateListener(new LauncherAnimatorUpdateListener() {
+                    public void onAnimationUpdate(float a, float b) {
                         if (b == 0f) {
                             // an optimization, but not required
                             return;
@@ -1964,11 +1959,9 @@
                 ValueAnimator rotationAnim =
                     ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
                 rotationAnim.setInterpolator(new DecelerateInterpolator(2.0f));
-                rotationAnim.addUpdateListener(new AnimatorUpdateListener() {
-                    public void onAnimationUpdate(ValueAnimator animation) {
+                rotationAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
+                    public void onAnimationUpdate(float a, float b) {
                         // don't invalidate workspace because we did it above
-                        final float b = (Float) animation.getAnimatedValue();
-                        final float a = 1f - b;
                         if (b == 0f) {
                             // an optimization, but not required
                             return;
@@ -2140,12 +2133,23 @@
 
         final int bmpWidth = b.getWidth();
         final int bmpHeight = b.getHeight();
+
         child.getLocationOnScreen(mTempXY);
         final int screenX = (int) mTempXY[0] + (child.getWidth() - bmpWidth) / 2;
         final int screenY = (int) mTempXY[1] + (child.getHeight() - bmpHeight) / 2;
-        mLauncher.lockScreenOrientation();
-        mDragController.startDrag(
-                b, screenX, screenY, this, child.getTag(), DragController.DRAG_ACTION_MOVE);
+
+        Rect dragRect = null;
+        if (child instanceof BubbleTextView) {
+            int iconSize = getResources().getDimensionPixelSize(R.dimen.app_icon_size);
+            int top = child.getPaddingTop();
+            int left = (bmpWidth - iconSize) / 2;
+            int right = left + iconSize;
+            int bottom = top + iconSize;
+            dragRect = new Rect(left, top, right, bottom);
+        }
+
+        mDragController.startDrag(b, screenX, screenY, this, child.getTag(),
+                DragController.DRAG_ACTION_MOVE, dragRect);
         b.recycle();
     }
 
@@ -2167,10 +2171,8 @@
         final CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
 
         // Based on the position of the drag view, find the top left of the original view
-        int viewX = dragViewX + (dragView.getWidth() - child.getWidth()) / 2;
-        int viewY = dragViewY + (dragView.getHeight() - child.getHeight()) / 2;
-        viewX += getResources().getInteger(R.integer.config_dragViewOffsetX);
-        viewY += getResources().getInteger(R.integer.config_dragViewOffsetY);
+        int viewX = dragViewX + (dragView.getWidth() - child.getMeasuredWidth()) / 2;
+        int viewY = dragViewY + (dragView.getHeight() - child.getMeasuredHeight()) / 2;
 
         // Set its old pos (in the new parent's coordinates); it will be animated
         // in animateViewIntoPosition after the next layout pass
@@ -2271,24 +2273,56 @@
         return true;
     }
 
+    boolean createUserFolderIfNecessary(View newView, CellLayout target, int originX,
+            int originY, boolean external) {
+        int spanX = mDragInfo != null ? mDragInfo.spanX : 1;
+        int spanY = mDragInfo != null ? mDragInfo.spanY : 1;
+
+        // First we find the cell nearest to point at which the item is dropped, without
+        // any consideration to whether there is an item there.
+        mTargetCell = findNearestArea(originX, originY,
+                spanX, spanY, target,
+                mTargetCell);
+
+        View v = target.getChildAt(mTargetCell[0], mTargetCell[1]);
+        boolean hasntMoved = mDragInfo != null && (mDragInfo.cellX == mTargetCell[0] &&
+                mDragInfo.cellY == mTargetCell[1]);
+
+        if (v == null || hasntMoved) return false;
+
+        final int screen = (mTargetCell == null) ?
+                mDragInfo.screen : indexOfChild(target);
+
+        boolean aboveShortcut = (v.getTag() instanceof ShortcutInfo);
+        boolean willBecomeShortcut = (newView.getTag() instanceof ShortcutInfo);
+
+        if (aboveShortcut && willBecomeShortcut) {
+            ShortcutInfo sourceInfo = (ShortcutInfo) newView.getTag();
+            ShortcutInfo destInfo = (ShortcutInfo) v.getTag();
+            // if the drag started here, we need to remove it from the workspace
+            if (!external) {
+                int fromScreen = mDragInfo.screen;
+                CellLayout sourceLayout = (CellLayout) getChildAt(fromScreen);
+                sourceLayout.removeView(newView);
+            }
+
+            target.removeView(v);
+            FolderIcon fi = mLauncher.addFolder(screen, mTargetCell[0], mTargetCell[1]);
+            fi.addItem(destInfo);
+            fi.addItem(sourceInfo);
+            return true;
+        }
+        return false;
+    }
+
     public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
             DragView dragView, Object dragInfo) {
-        boolean largeOrSpringLoaded = !mIsSmall || mWasSpringLoadedOnDragExit;
-        int originX = largeOrSpringLoaded ? x - xOffset : x - xOffset + dragView.getWidth() / 2;
-        int originY = largeOrSpringLoaded ? y - yOffset : y - yOffset + dragView.getHeight() / 2;
 
-        if (mIsSmall || mIsInUnshrinkAnimation) {
-            // get originX and originY in the local coordinate system of the screen
-            mTempOriginXY[0] = originX;
-            mTempOriginXY[1] = originY;
-            mapPointFromSelfToChild(mDragTargetLayout, mTempOriginXY);
-            originX = (int)mTempOriginXY[0];
-            originY = (int)mTempOriginXY[1];
-            if (!largeOrSpringLoaded) {
-               originX -= mDragTargetLayout.getCellWidth() / 2;
-               originY -= mDragTargetLayout.getCellHeight() / 2;
-            }
-        }
+        mDragViewVisualCenter = getDragViewVisualCenter(x, y, xOffset, yOffset, dragView,
+                mDragViewVisualCenter);
+
+        // We want the point to be mapped to the dragTarget.
+        mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
 
         // When you are in customization mode and drag to a particular screen, make that the
         // new current/default screen, so any subsequent taps add items to that screen
@@ -2300,14 +2334,15 @@
         }
 
         if (source != this) {
-            final int[] touchXY = new int[] { originX, originY };
+            final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0],
+                    (int) mDragViewVisualCenter[1] };
             if ((mIsSmall || mIsInUnshrinkAnimation) && !mLauncher.isAllAppsVisible()) {
                 // When the workspace is shrunk and the drop comes from customize, don't actually
                 // add the item to the screen -- customize will do this itself
                 ((ItemInfo) dragInfo).dropPos = touchXY;
                 return;
             }
-            onDropExternal(touchXY, dragInfo, mDragTargetLayout, false);
+            onDropExternal(touchXY, dragInfo, mDragTargetLayout, false, dragView);
         } else if (mDragInfo != null) {
             final View cell = mDragInfo.cell;
             CellLayout dropTargetLayout = mDragTargetLayout;
@@ -2324,13 +2359,22 @@
 
             if (dropTargetLayout != null) {
                 // Move internally
-                mTargetCell = findNearestVacantArea(originX, originY,
-                        mDragInfo.spanX, mDragInfo.spanY, cell, dropTargetLayout,
-                        mTargetCell);
-
                 final int screen = (mTargetCell == null) ?
                         mDragInfo.screen : indexOfChild(dropTargetLayout);
 
+                // If the item being dropped is a shortcut and the nearest drop cell also contains
+                // a shortcut, then create a folder with the two shortcuts.
+                if (createUserFolderIfNecessary(cell, dropTargetLayout,
+                        (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], false)) {
+                    return;
+                }
+
+                // Aside from the special case where we're dropping a shortcut onto a shortcut,
+                // we need to find the nearest cell location that is vacant
+                mTargetCell = findNearestVacantArea((int) mDragViewVisualCenter[0],
+                        (int) mDragViewVisualCenter[1], mDragInfo.spanX, mDragInfo.spanY, cell,
+                        dropTargetLayout, mTargetCell);
+
                 if (screen != mCurrentPage) {
                     snapToPage(screen);
                 }
@@ -2353,7 +2397,6 @@
                             mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY));
 
                     if (cell instanceof LauncherAppWidgetHostView) {
-                        final CellLayoutChildren children = dropTargetLayout.getChildrenLayout();
                         final CellLayout cellLayout = dropTargetLayout;
                         // We post this call so that the widget has a chance to be placed
                         // in its final location
@@ -2361,10 +2404,21 @@
                         final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell;
                         AppWidgetProviderInfo pinfo = hostView.getAppWidgetInfo();
                         if (pinfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE) {
+                            final Runnable resizeRunnable = new Runnable() {
+                                public void run() {
+                                    DragLayer dragLayer = (DragLayer)
+                                            mLauncher.findViewById(R.id.drag_layer);
+                                    dragLayer.addResizeFrame(info, hostView,
+                                            cellLayout);
+                                }
+                            };
                             post(new Runnable() {
                                 public void run() {
-                                    children.addResizeFrame(info, hostView, 
-                                            cellLayout);
+                                    if (!isPageMoving()) {
+                                        resizeRunnable.run();
+                                    } else {
+                                        mDelayedResizeRunnable = resizeRunnable;
+                                    }
                                 }
                             });
                         }
@@ -2378,14 +2432,30 @@
 
             final CellLayout parent = (CellLayout) cell.getParent().getParent();
 
+            int loc[] = new int[2];
+            getViewLocationRelativeToSelf(dragView, loc);
+
             // Prepare it to be animated into its new position
             // This must be called after the view has been re-parented
-            setPositionForDropAnimation(dragView, originX, originY, parent, cell);
+            setPositionForDropAnimation(dragView, loc[0], loc[1], parent, cell);
             boolean animateDrop = !mWasSpringLoadedOnDragExit;
             parent.onDropChild(cell, animateDrop);
         }
     }
 
+    private void getViewLocationRelativeToSelf(View v, int[] location) {
+        getLocationOnScreen(location);
+        int x = location[0];
+        int y = location[1];
+
+        v.getLocationOnScreen(location);
+        int vX = location[0];
+        int vY = location[1];
+
+        location[0] = vX - x;
+        location[1] = vY - y;
+    }
+
     public void onDragEnter(DragSource source, int x, int y, int xOffset,
             int yOffset, DragView dragView, Object dragInfo) {
         mDragTargetLayout = null; // Reset the drag state
@@ -2614,11 +2684,11 @@
        xy[1] -= (mScrollY - v.getTop());
    }
 
-    static private float squaredDistance(float[] point1, float[] point2) {
+   static private float squaredDistance(float[] point1, float[] point2) {
         float distanceX = point1[0] - point2[0];
         float distanceY = point2[1] - point2[1];
         return distanceX * distanceX + distanceY * distanceY;
-    }
+   }
 
     /*
      *
@@ -2716,21 +2786,57 @@
         return bestMatchingScreen;
     }
 
+    // This is used to compute the visual center of the dragView. This point is then
+    // used to visualize drop locations and determine where to drop an item. The idea is that
+    // the visual center represents the user's interpretation of where the item is, and hence
+    // is the appropriate point to use when determining drop location.
+    private float[] getDragViewVisualCenter(int x, int y, int xOffset, int yOffset,
+            DragView dragView, float[] recycle) {
+        float res[];
+        if (recycle == null) {
+            res = new float[2];
+        } else {
+            res = recycle;
+        }
+
+        // First off, the drag view has been shifted in a way that is not represented in the
+        // x and y values or the x/yOffsets. Here we account for that shift.
+        x += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetX);
+        y += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY);
+
+        // These represent the visual top and left of drag view if a dragRect was provided.
+        // If a dragRect was not provided, then they correspond to the actual view left and
+        // top, as the dragRect is in that case taken to be the entire dragView.
+        // R.dimen.dragViewOffsetY.
+        int left = x - xOffset;
+        int top = y - yOffset;
+
+        // In order to find the visual center, we shift by half the dragRect
+        res[0] = left + dragView.getDragRegion().width() / 2;
+        res[1] = top + dragView.getDragRegion().height() / 2;
+
+        return res;
+    }
+
     public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
             DragView dragView, Object dragInfo) {
         // When touch is inside the scroll area, skip dragOver actions for the current screen
         if (!mInScrollArea) {
             CellLayout layout;
-            int originX = x - xOffset;
-            int originY = y - yOffset;
+            int left = x - xOffset;
+            int top = y - yOffset;
+
+            mDragViewVisualCenter = getDragViewVisualCenter(x, y, xOffset, yOffset, dragView,
+                    mDragViewVisualCenter);
+
             boolean shrunken = mIsSmall || mIsInUnshrinkAnimation;
             if (shrunken) {
                 mLastDragView = dragView;
-                mLastDragOriginX = originX;
-                mLastDragOriginY = originY;
+                mLastDragOriginX = left;
+                mLastDragOriginY = top;
                 mLastDragXOffset = xOffset;
                 mLastDragYOffset = yOffset;
-                layout = findMatchingPageForDragOver(dragView, originX, originY, xOffset, yOffset);
+                layout = findMatchingPageForDragOver(dragView, left, top, xOffset, yOffset);
 
                 if (layout != mDragTargetLayout) {
                     if (mDragTargetLayout != null) {
@@ -2776,35 +2882,14 @@
                     }
                 }
 
-                if (source instanceof AllAppsPagedView) {
-                    // This is a hack to fix the point used to determine which cell an icon from
-                    // the all apps screen is over
-                    if (item != null && item.spanX == 1 && layout != null) {
-                        int dragRegionLeft = (dragView.getWidth() - layout.getCellWidth()) / 2;
-
-                        originX += dragRegionLeft - dragView.getDragRegionLeft();
-                        if (dragView.getDragRegionWidth() != layout.getCellWidth()) {
-                            dragView.setDragRegion(dragView.getDragRegionLeft(),
-                                    dragView.getDragRegionTop(),
-                                    layout.getCellWidth(),
-                                    dragView.getDragRegionHeight());
-                        }
-                    }
-                } else if (source == this) {
-                    // When dragging from the workspace, the drag view is slightly bigger than
-                    // the original view, and offset vertically. Adjust to account for this.
-                    final View origView = mDragInfo.cell;
-                    originX += (dragView.getMeasuredWidth() - origView.getWidth()) / 2;
-                    originY += (dragView.getMeasuredHeight() - origView.getHeight()) / 2
-                            + dragView.getOffsetY();
-                }
-
                 if (mDragTargetLayout != null) {
                     final View child = (mDragInfo == null) ? null : mDragInfo.cell;
-                    float[] localOrigin = { originX, originY };
-                    mapPointFromSelfToChild(mDragTargetLayout, localOrigin, null);
+                    // We want the point to be mapped to the dragTarget.
+                    mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
                     mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
-                            (int) localOrigin[0], (int) localOrigin[1], item.spanX, item.spanY);
+                            (int) mDragViewVisualCenter[0],
+                            (int) mDragViewVisualCenter[1],
+                            item.spanX, item.spanY);
                 }
             }
         }
@@ -2850,6 +2935,11 @@
         return false;
     }
 
+    private void onDropExternal(int[] touchXY, Object dragInfo,
+            CellLayout cellLayout, boolean insertAtFirst) {
+        onDropExternal(touchXY, dragInfo, cellLayout, insertAtFirst, null);
+    }
+
     /**
      * Drop an item that didn't originate on one of the workspace screens.
      * It may have come from Launcher (e.g. from all apps or customize), or it may have
@@ -2859,7 +2949,7 @@
      * to add an item to one of the workspace screens.
      */
     private void onDropExternal(int[] touchXY, Object dragInfo,
-            CellLayout cellLayout, boolean insertAtFirst) {
+            CellLayout cellLayout, boolean insertAtFirst, DragView dragView) {
         int screen = indexOfChild(cellLayout);
         if (dragInfo instanceof PendingAddItemInfo) {
             PendingAddItemInfo info = (PendingAddItemInfo) dragInfo;
@@ -2902,10 +2992,18 @@
                 throw new IllegalStateException("Unknown item type: " + info.itemType);
             }
 
+            // If the item being dropped is a shortcut and the nearest drop cell also contains
+            // a shortcut, then create a folder with the two shortcuts.
+            if (touchXY != null && createUserFolderIfNecessary(view, cellLayout, touchXY[0],
+                  touchXY[1], true)) {
+                return;
+            }
+
             mTargetCell = new int[2];
             if (touchXY != null) {
                 // when dragging and dropping, just find the closest free spot
-                cellLayout.findNearestVacantArea(touchXY[0], touchXY[1], 1, 1, mTargetCell);
+                mTargetCell = findNearestVacantArea(touchXY[0], touchXY[1], 1, 1, null, cellLayout,
+                        mTargetCell);
             } else {
                 cellLayout.findCellForSpan(mTargetCell, 1, 1);
             }
@@ -2915,6 +3013,15 @@
             cellLayout.onDropChild(view, animateDrop);
             cellLayout.animateDrop();
             CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
+            cellLayout.getChildrenLayout().measureChild(view);
+
+            if (dragView != null) {
+                // we have the visual center of the drag view, we need to find the actual
+                // left and top of the dragView.
+                int loc[] = new int[2];
+                getViewLocationRelativeToSelf(dragView, loc);
+                setPositionForDropAnimation(dragView, loc[0], loc[1], cellLayout, view);
+            }
 
             LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
                     LauncherSettings.Favorites.CONTAINER_DESKTOP, screen,
@@ -2942,16 +3049,24 @@
 
     /**
      * Calculate the nearest cell where the given object would be dropped.
+     *
+     * pixelX and pixelY should be in the coordinate system of layout
      */
     private int[] findNearestVacantArea(int pixelX, int pixelY,
             int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) {
-
-        int localPixelX = pixelX - (layout.getLeft() - mScrollX);
-        int localPixelY = pixelY - (layout.getTop() - mScrollY);
-
-        // Find the best target drop location
         return layout.findNearestVacantArea(
-                localPixelX, localPixelY, spanX, spanY, ignoreView, recycle);
+                pixelX, pixelY, spanX, spanY, ignoreView, recycle);
+    }
+
+    /**
+     * Calculate the nearest cell where the given object would be dropped.
+     *
+     * pixelX and pixelY should be in the coordinate system of layout
+     */
+    private int[] findNearestArea(int pixelX, int pixelY,
+            int spanX, int spanY, CellLayout layout, int[] recycle) {
+        return layout.findNearestArea(
+                pixelX, pixelY, spanX, spanY, recycle);
     }
 
     void setLauncher(Launcher launcher) {
@@ -3104,6 +3219,21 @@
         return null;
     }
 
+    void clearDropTargets() {
+        final int screenCount = getChildCount();
+
+        for (int i = 0; i < screenCount; i++) {
+            final CellLayout layoutParent = (CellLayout) getChildAt(i);
+            final ViewGroup layout = layoutParent.getChildrenLayout();
+            int childCount = layout.getChildCount();
+            for (int j = 0; j < childCount; j++) {
+                View v = layout.getChildAt(j);
+                if (v instanceof DropTarget) {
+                    mDragController.removeDropTarget((DropTarget) v);
+                }
+            }
+        }
+    }
 
     void removeItems(final ArrayList<ApplicationInfo> apps) {
         final int screenCount = getChildCount();