Merge "Enabling user folder creation by dropping shortcut onto shortcut"
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-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/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/all_apps_tabbed.xml b/res/layout-xlarge/all_apps_tabbed.xml
index 9937338..b00b3c3 100644
--- a/res/layout-xlarge/all_apps_tabbed.xml
+++ b/res/layout-xlarge/all_apps_tabbed.xml
@@ -32,7 +32,7 @@
             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"
@@ -50,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"
@@ -64,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"
@@ -84,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"
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 98fc9d9..d8db066 100644
--- a/res/layout-xlarge/customization_drawer.xml
+++ b/res/layout-xlarge/customization_drawer.xml
@@ -22,7 +22,7 @@
         android:layout_height="match_parent">
         <!-- The layout_width of this RelativeLayout gets overwritten in
              CustomizeTrayTabHost.onFinishInflate -->
-        <TabWidget
+        <com.android.launcher2.FocusOnlyTabWidget
             android:id="@android:id/tabs"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
@@ -32,7 +32,7 @@
         <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"
@@ -47,7 +47,8 @@
                 launcher:pageLayoutPaddingTop="40dp"
                 launcher:pageLayoutPaddingBottom="25dp"
                 launcher:pageLayoutPaddingLeft="20dp"
-                launcher:pageLayoutPaddingRight="20dp" />
+                launcher:pageLayoutPaddingRight="20dp"
+                launcher:pageLayoutMaxHeight="@dimen/customization_drawer_content_height" />
          </FrameLayout>
       </LinearLayout>
 </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-de/strings.xml b/res/values-de/strings.xml
index f69ee43..697c760 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>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 8a73b47..ccdab7e 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -31,7 +31,7 @@
     <string name="folders_tab_label" msgid="1145293785541489736">"Mapper"</string>
     <string name="shortcuts_tab_label" msgid="8640731503933155644">"Mer"</string>
     <string name="wallpapers_tab_label" msgid="1617804870364119879">"Bakgrunner"</string>
-    <string name="applications_tab_label" msgid="2991364240020736760">"Programsnarveier"</string>
+    <string name="applications_tab_label" msgid="2991364240020736760">"App-snarveier"</string>
     <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Dette er nå bakgrunnsfanen"</string>
     <string name="all_apps_tab_all" msgid="2942727589595027258">"Alle"</string>
     <string name="all_apps_tab_apps" msgid="5468972551904071712">"Programmer"</string>
diff --git a/res/values-xlarge-land/dimens.xml b/res/values-xlarge-land/dimens.xml
index b44a94a..8d25555 100644
--- a/res/values-xlarge-land/dimens.xml
+++ b/res/values-xlarge-land/dimens.xml
@@ -27,8 +27,6 @@
 
     <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>
diff --git a/res/values-xlarge-port/dimens.xml b/res/values-xlarge-port/dimens.xml
index bb410b1..e696e34 100644
--- a/res/values-xlarge-port/dimens.xml
+++ b/res/values-xlarge-port/dimens.xml
@@ -27,8 +27,6 @@
 
     <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>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index fec78ab..2274274 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -39,7 +39,7 @@
     <string name="all_apps_tab_downloaded" msgid="1488049110598641387">"我的應用程式"</string>
     <string name="all_apps_no_games" msgid="5293893733372793696">"找不到遊戲。"</string>
     <string name="all_apps_no_downloads" msgid="2284720393234453761">"您沒有下載任何應用程式。"</string>
-    <string name="market" msgid="2652226429823445833">"購物"</string>
+    <string name="market" msgid="2652226429823445833">"商店"</string>
     <string name="external_drop_widget_error" msgid="4976816434597126575">"無法將項目拖放至主螢幕上"</string>
     <string name="external_drop_widget_pick_title" msgid="4481311720134376218">"選取要建立的小工具"</string>
     <string name="rename_folder_label" msgid="5646236631298452787">"資料夾名稱"</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/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/AllAppsPagedView.java b/src/com/android/launcher2/AllAppsPagedView.java
index 3924c57..c05fe75 100644
--- a/src/com/android/launcher2/AllAppsPagedView.java
+++ b/src/com/android/launcher2/AllAppsPagedView.java
@@ -66,6 +66,7 @@
     private boolean mAllowHardwareLayerCreation;
 
     private int mPageContentWidth;
+    private boolean mHasMadeSuccessfulDrop;
 
     public AllAppsPagedView(Context context) {
         this(context, null);
@@ -138,6 +139,10 @@
         }
     }
 
+    void resetSuccessfulDropFlag() {
+        mHasMadeSuccessfulDrop = false;
+    }
+
     @Override
     public void zoom(float zoom, boolean animate) {
         mZoom = zoom;
@@ -322,6 +327,12 @@
         tearDownDragMode();
         mLauncher.getWorkspace().onDragStopped(success);
         mLauncher.unlockScreenOrientation();
+
+        if (!success && !mHasMadeSuccessfulDrop) {
+            mLauncher.getWorkspace().shrink(Workspace.ShrinkState.BOTTOM_HIDDEN);
+        } else {
+            mHasMadeSuccessfulDrop |= success;
+        }
     }
 
     int getPageContentWidth() {
diff --git a/src/com/android/launcher2/AllAppsTabbed.java b/src/com/android/launcher2/AllAppsTabbed.java
index 059d0ae..2deec8e 100644
--- a/src/com/android/launcher2/AllAppsTabbed.java
+++ b/src/com/android/launcher2/AllAppsTabbed.java
@@ -29,11 +29,13 @@
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.TabHost;
 import android.widget.TabWidget;
 import android.widget.TextView;
 
 import java.util.ArrayList;
+import java.util.Random;
 
 /**
  * Implements a tabbed version of AllApps2D.
@@ -80,6 +82,7 @@
         };
 
         // 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);
@@ -90,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
@@ -259,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/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 ac9d54d..dd0c58a 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -1639,7 +1639,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/CustomizePagedView.java b/src/com/android/launcher2/CustomizePagedView.java
index 3dfa7c2..ca60361 100644
--- a/src/com/android/launcher2/CustomizePagedView.java
+++ b/src/com/android/launcher2/CustomizePagedView.java
@@ -172,12 +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);
@@ -488,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;
 
diff --git a/src/com/android/launcher2/CustomizeTrayTabHost.java b/src/com/android/launcher2/CustomizeTrayTabHost.java
index 3e04025..5c683c4 100644
--- a/src/com/android/launcher2/CustomizeTrayTabHost.java
+++ b/src/com/android/launcher2/CustomizeTrayTabHost.java
@@ -16,8 +16,7 @@
 
 package com.android.launcher2;
 
-import com.android.launcher.R;
-import com.android.launcher2.CustomizePagedView.CustomizationType;
+import java.util.Random;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -28,10 +27,14 @@
 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";
@@ -69,7 +72,8 @@
 
         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));
+        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)
@@ -82,6 +86,7 @@
         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 =
@@ -149,6 +154,14 @@
         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;
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/Launcher.java b/src/com/android/launcher2/Launcher.java
index bb58081..60cab8e 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -205,7 +205,7 @@
     private CustomizeTrayTabHost mHomeCustomizationDrawer;
     private boolean mAutoAdvanceRunning = false;
 
-    private View mButtonCluster;
+    private ViewGroup mButtonCluster;
     private View mAllAppsButton;
     private View mDivider;
     private View mConfigureButton;
@@ -769,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) {
@@ -958,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()) {
@@ -1007,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
@@ -2575,7 +2582,6 @@
     
     private void showAndEnableToolbarButton(View button) {
         button.setVisibility(View.VISIBLE);
-        button.setFocusable(true);
         button.setClickable(true);
     }
 
@@ -2586,7 +2592,6 @@
     }
 
     private void disableToolbarButton(View button) {
-        button.setFocusable(false);
         button.setClickable(false);
     }
 
@@ -2694,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);
 
@@ -2714,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);
         }
@@ -2806,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);
@@ -2901,7 +2910,7 @@
         }
 
         if (LauncherApplication.isScreenXLarge()) {
-            cameraZoomOut(State.ALL_APPS, animated);
+            cameraZoomOut(State.ALL_APPS, animated, false);
         } else {
             mAllAppsGrid.zoom(1.0f, animated);
         }
@@ -2955,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
         }*/
@@ -2968,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
@@ -3022,7 +3031,7 @@
         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);
             }
@@ -3045,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;
@@ -3058,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);
         }
     }
 
@@ -3141,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);
             }
         }
     }
@@ -3159,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);
             }
         }
     }
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/PagedView.java b/src/com/android/launcher2/PagedView.java
index 9a0b79c..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);
@@ -388,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();
@@ -1159,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);
         }
     }
diff --git a/src/com/android/launcher2/PagedViewCellLayout.java b/src/com/android/launcher2/PagedViewCellLayout.java
index 53657e5..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
 
@@ -247,9 +255,12 @@
         // 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 offset that accounts for
-        int iconHorizontalOffset = (mCellWidth - Utilities.getIconContentSize());
-        return mCellCountX * mCellWidth + (mCellCountX - 1) * mWidthGap - iconHorizontalOffset;
+        // 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
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/Utilities.java b/src/com/android/launcher2/Utilities.java
index 8ab22eb..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;
@@ -389,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 a156d0d..4e5169a 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -341,58 +341,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);
     }
 
@@ -1253,6 +1236,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();
@@ -1401,14 +1392,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