resolved conflicts for merge of 319226a8 to master

Change-Id: Ibeefaa496cc3970e35af007fb64344c2a0f1f82f
diff --git a/Android.mk b/Android.mk
index dc72ec6..dfe6788 100644
--- a/Android.mk
+++ b/Android.mk
@@ -14,6 +14,8 @@
 # limitations under the License.
 #
 
+ifneq ($(TARGET_SIMULATOR),true)
+
 LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
@@ -21,7 +23,7 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := android-common
 
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_SRC_FILES := $(call all-subdir-java-files) $(call all-renderscript-files-under, src)
 
 LOCAL_PACKAGE_NAME := Launcher2
 LOCAL_CERTIFICATE := shared
@@ -31,3 +33,5 @@
 LOCAL_PROGUARD_FLAGS := -include $(LOCAL_PATH)/proguard.flags
 
 include $(BUILD_PACKAGE)
+
+endif
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 84ee599..b10ab92 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -74,7 +74,6 @@
             android:clearTaskOnLaunch="true"
             android:stateNotNeeded="true"
             android:theme="@style/Theme"
-            android:screenOrientation="nosensor"
             android:windowSoftInputMode="stateUnspecified|adjustPan">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/proguard.flags b/proguard.flags
index 1187fd8a..aa75f4a 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -2,6 +2,15 @@
   public void previousScreen(android.view.View);
   public void nextScreen(android.view.View);
   public void launchHotSeat(android.view.View);
+  public void onClickSearchButton(android.view.View);
+  public void onClickConfigureButton(android.view.View);
+  public void onClickAllAppsButton(android.view.View);
+  public void onClickAppMarketButton(android.view.View);
+}
+
+-keep class com.android.launcher2.CellLayout {
+  public float getDimmedBitmapAlpha();
+  public void setDimmedBitmapAlpha(float);
 }
 
 -keep class com.android.launcher2.AllApps3D$Defines {
diff --git a/res/anim/all_apps_zoom_in.xml b/res/anim/all_apps_zoom_in.xml
new file mode 100644
index 0000000..644d1cf
--- /dev/null
+++ b/res/anim/all_apps_zoom_in.xml
@@ -0,0 +1,33 @@
+<?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.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    android:interpolator="@android:anim/decelerate_interpolator"
+    android:shareInterpolator="true">
+    <translate xmlns:android="http://schemas.android.com/apk/res/android"
+        android:fromXDelta="0%p"
+        android:toXDelta="0%p"
+        android:fromYDelta="-20%p"
+        android:toYDelta="0%p"
+        android:duration="500" />
+    <scale
+        android:fromXScale="5.0"
+        android:toXScale="1.0"
+        android:fromYScale="5.0"
+        android:toYScale="1.0"
+        android:pivotX="50%"
+        android:pivotY="100%"
+        android:duration="500" />
+</set>
diff --git a/res/anim/all_apps_zoom_out.xml b/res/anim/all_apps_zoom_out.xml
new file mode 100644
index 0000000..23a8712
--- /dev/null
+++ b/res/anim/all_apps_zoom_out.xml
@@ -0,0 +1,33 @@
+<?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.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    android:interpolator="@android:anim/accelerate_interpolator"
+    android:shareInterpolator="true">
+    <translate xmlns:android="http://schemas.android.com/apk/res/android"
+        android:fromXDelta="0%p"
+        android:toXDelta="0%p"
+        android:fromYDelta="0%p"
+        android:toYDelta="-20%p"
+        android:duration="500" />
+    <scale
+        android:fromXScale="1.0"
+        android:toXScale="5.0"
+        android:fromYScale="1.0"
+        android:toYScale="5.0"
+        android:pivotX="50%"
+        android:pivotY="100%"
+        android:duration="500" />
+</set>
diff --git a/res/anim/home_customization_drawer_slide_down.xml b/res/anim/home_customization_drawer_slide_down.xml
new file mode 100644
index 0000000..b3041c7
--- /dev/null
+++ b/res/anim/home_customization_drawer_slide_down.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+    android:fromXDelta="0%p"
+    android:toXDelta="0%p"
+    android:fromYDelta="0%p"
+    android:toYDelta="100%p"
+
+    android:duration="500" />
diff --git a/res/anim/home_customization_drawer_slide_up.xml b/res/anim/home_customization_drawer_slide_up.xml
new file mode 100644
index 0000000..3df2320
--- /dev/null
+++ b/res/anim/home_customization_drawer_slide_up.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+    android:fromXDelta="0%p"
+    android:toXDelta="0%p"
+    android:fromYDelta="100%p"
+    android:toYDelta="0%p"
+
+    android:duration="500" />
diff --git a/res/anim/paged_view_click_feedback.xml b/res/anim/paged_view_click_feedback.xml
new file mode 100644
index 0000000..786d974
--- /dev/null
+++ b/res/anim/paged_view_click_feedback.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+    android:fromAlpha="1.0"
+    android:toAlpha="0.65"
+    android:duration="100"
+    android:fillAfter="true"
+    android:repeatCount="1"
+    android:repeatMode="reverse" />
diff --git a/res/drawable-hdpi/add_button.png b/res/drawable-hdpi/add_button.png
new file mode 100644
index 0000000..7e60d81
--- /dev/null
+++ b/res/drawable-hdpi/add_button.png
Binary files differ
diff --git a/res/drawable-hdpi/default_widget_preview.9.png b/res/drawable-hdpi/default_widget_preview.9.png
new file mode 100644
index 0000000..6596f07
--- /dev/null
+++ b/res/drawable-hdpi/default_widget_preview.9.png
Binary files differ
diff --git a/res/drawable-hdpi/pressed_application_background.9.png b/res/drawable-hdpi/pressed_application_background.9.png
new file mode 100644
index 0000000..a6cbe94
--- /dev/null
+++ b/res/drawable-hdpi/pressed_application_background.9.png
Binary files differ
diff --git a/res/drawable-hdpi/rounded_rect_green.9.png b/res/drawable-hdpi/rounded_rect_green.9.png
new file mode 100644
index 0000000..a846cbd
--- /dev/null
+++ b/res/drawable-hdpi/rounded_rect_green.9.png
Binary files differ
diff --git a/res/drawable-hdpi/rounded_rect_red.9.png b/res/drawable-hdpi/rounded_rect_red.9.png
new file mode 100644
index 0000000..21fd62c
--- /dev/null
+++ b/res/drawable-hdpi/rounded_rect_red.9.png
Binary files differ
diff --git a/res/drawable-xlarge/all_apps_button.xml b/res/drawable-xlarge/all_apps_button.xml
new file mode 100644
index 0000000..46bc632
--- /dev/null
+++ b/res/drawable-xlarge/all_apps_button.xml
@@ -0,0 +1,20 @@
+<?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_pressed="true" android:drawable="@drawable/all_apps_button_pressed" />
+    <item android:drawable="@drawable/all_apps_button_normal" />
+</selector>
diff --git a/res/drawable-xlarge/all_apps_button_normal.png b/res/drawable-xlarge/all_apps_button_normal.png
new file mode 100644
index 0000000..095ce63
--- /dev/null
+++ b/res/drawable-xlarge/all_apps_button_normal.png
Binary files differ
diff --git a/res/drawable-xlarge/all_apps_button_pressed.png b/res/drawable-xlarge/all_apps_button_pressed.png
new file mode 100644
index 0000000..50e4735
--- /dev/null
+++ b/res/drawable-xlarge/all_apps_button_pressed.png
Binary files differ
diff --git a/res/drawable-xlarge/app_market_generic.png b/res/drawable-xlarge/app_market_generic.png
new file mode 100644
index 0000000..0ceaeef
--- /dev/null
+++ b/res/drawable-xlarge/app_market_generic.png
Binary files differ
diff --git a/res/drawable-xlarge/configure_button.xml b/res/drawable-xlarge/configure_button.xml
new file mode 100644
index 0000000..ac87290
--- /dev/null
+++ b/res/drawable-xlarge/configure_button.xml
@@ -0,0 +1,20 @@
+<?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_pressed="true" android:drawable="@drawable/configure_button_pressed" />
+    <item android:drawable="@drawable/configure_button_normal" />
+</selector>
diff --git a/res/drawable-xlarge/configure_button_normal.png b/res/drawable-xlarge/configure_button_normal.png
new file mode 100644
index 0000000..066f492
--- /dev/null
+++ b/res/drawable-xlarge/configure_button_normal.png
Binary files differ
diff --git a/res/drawable-xlarge/configure_button_pressed.png b/res/drawable-xlarge/configure_button_pressed.png
new file mode 100644
index 0000000..536ed7c
--- /dev/null
+++ b/res/drawable-xlarge/configure_button_pressed.png
Binary files differ
diff --git a/res/drawable-xlarge/info_button.png b/res/drawable-xlarge/info_button.png
new file mode 100644
index 0000000..e3b27f4
--- /dev/null
+++ b/res/drawable-xlarge/info_button.png
Binary files differ
diff --git a/res/drawable-xlarge/mini_home_screen_bg.9.png b/res/drawable-xlarge/mini_home_screen_bg.9.png
new file mode 100644
index 0000000..fd989c1
--- /dev/null
+++ b/res/drawable-xlarge/mini_home_screen_bg.9.png
Binary files differ
diff --git a/res/drawable-xlarge/mini_home_screen_bg_hover.9.png b/res/drawable-xlarge/mini_home_screen_bg_hover.9.png
new file mode 100644
index 0000000..331ad54
--- /dev/null
+++ b/res/drawable-xlarge/mini_home_screen_bg_hover.9.png
Binary files differ
diff --git a/res/drawable-xlarge/rotate_button.xml b/res/drawable-xlarge/rotate_button.xml
new file mode 100644
index 0000000..c29efa4
--- /dev/null
+++ b/res/drawable-xlarge/rotate_button.xml
@@ -0,0 +1,20 @@
+<?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_pressed="true" android:drawable="@drawable/rotate_button_pressed" />
+    <item android:drawable="@drawable/rotate_button_normal" />
+</selector>
diff --git a/res/drawable-xlarge/rotate_button_normal.png b/res/drawable-xlarge/rotate_button_normal.png
new file mode 100644
index 0000000..fd014aa
--- /dev/null
+++ b/res/drawable-xlarge/rotate_button_normal.png
Binary files differ
diff --git a/res/drawable-xlarge/rotate_button_pressed.png b/res/drawable-xlarge/rotate_button_pressed.png
new file mode 100644
index 0000000..29fb8d9
--- /dev/null
+++ b/res/drawable-xlarge/rotate_button_pressed.png
Binary files differ
diff --git a/res/drawable-xlarge/search_button.xml b/res/drawable-xlarge/search_button.xml
new file mode 100644
index 0000000..8f18e67
--- /dev/null
+++ b/res/drawable-xlarge/search_button.xml
@@ -0,0 +1,20 @@
+<?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_pressed="true" android:drawable="@drawable/search_button_pressed" />
+    <item android:drawable="@drawable/search_button_normal" />
+</selector>
diff --git a/res/drawable-xlarge/search_button_normal.png b/res/drawable-xlarge/search_button_normal.png
new file mode 100644
index 0000000..3a3045a
--- /dev/null
+++ b/res/drawable-xlarge/search_button_normal.png
Binary files differ
diff --git a/res/drawable-xlarge/search_button_pressed.png b/res/drawable-xlarge/search_button_pressed.png
new file mode 100644
index 0000000..8ae6b16
--- /dev/null
+++ b/res/drawable-xlarge/search_button_pressed.png
Binary files differ
diff --git a/res/drawable-xlarge/trashcan.png b/res/drawable-xlarge/trashcan.png
new file mode 100644
index 0000000..284543c
--- /dev/null
+++ b/res/drawable-xlarge/trashcan.png
Binary files differ
diff --git a/res/drawable-xlarge/trashcan_hover.png b/res/drawable-xlarge/trashcan_hover.png
new file mode 100644
index 0000000..38695cd
--- /dev/null
+++ b/res/drawable-xlarge/trashcan_hover.png
Binary files differ
diff --git a/res/drawable-xlarge/wallpaper_leaf.jpg b/res/drawable-xlarge/wallpaper_leaf.jpg
new file mode 100644
index 0000000..afb0acc
--- /dev/null
+++ b/res/drawable-xlarge/wallpaper_leaf.jpg
Binary files differ
diff --git a/res/drawable-xlarge/wallpaper_leaf_small.jpg b/res/drawable-xlarge/wallpaper_leaf_small.jpg
new file mode 100644
index 0000000..48e9f3e
--- /dev/null
+++ b/res/drawable-xlarge/wallpaper_leaf_small.jpg
Binary files differ
diff --git a/res/drawable/default_widget_preview.9.png b/res/drawable/default_widget_preview.9.png
new file mode 100644
index 0000000..b3ddada
--- /dev/null
+++ b/res/drawable/default_widget_preview.9.png
Binary files differ
diff --git a/res/drawable/rounded_rect_green.9.png b/res/drawable/rounded_rect_green.9.png
new file mode 100644
index 0000000..5787c3d
--- /dev/null
+++ b/res/drawable/rounded_rect_green.9.png
Binary files differ
diff --git a/res/drawable/rounded_rect_red.9.png b/res/drawable/rounded_rect_red.9.png
new file mode 100644
index 0000000..7a5fdee
--- /dev/null
+++ b/res/drawable/rounded_rect_red.9.png
Binary files differ
diff --git a/res/layout-land/all_apps_2d.xml b/res/layout-land/all_apps_2d.xml
index a253b93..b7fcd45 100644
--- a/res/layout-land/all_apps_2d.xml
+++ b/res/layout-land/all_apps_2d.xml
@@ -22,7 +22,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:padding="2dip"
-    >
+    android:background="#FF000000">
 
     <GridView android:id="@+id/all_apps_2d_grid"
         android:tag="all_apps_2d_grid"
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index 1f13f1f..8d38a3d6 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -24,14 +24,16 @@
 
     <include layout="@layout/all_apps" />
 
-    <!-- The workspace contains 3 screens of cells -->
+    <!-- The workspace contains 5 screens of cells -->
     <com.android.launcher2.Workspace
         android:id="@+id/workspace"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:scrollbars="horizontal"
         android:fadeScrollbars="true"
-        launcher:defaultScreen="2">
+        launcher:defaultScreen="2"
+        launcher:cellCountX="4"
+        launcher:cellCountY="4">
 
         <include android:id="@+id/cell1" layout="@layout/workspace_screen" />
         <include android:id="@+id/cell2" layout="@layout/workspace_screen" />
diff --git a/res/layout-land/workspace_screen.xml b/res/layout-land/workspace_screen.xml
index 315e68b..9323f58 100644
--- a/res/layout-land/workspace_screen.xml
+++ b/res/layout-land/workspace_screen.xml
@@ -24,9 +24,7 @@
 
     launcher:cellWidth="@dimen/workspace_cell_width"
     launcher:cellHeight="@dimen/workspace_cell_height"
-    launcher:longAxisStartPadding="65dip"
-    launcher:longAxisEndPadding="65dip"
-    launcher:shortAxisStartPadding="0dip"
-    launcher:shortAxisEndPadding="0dip"
-    launcher:shortAxisCells="4"
-    launcher:longAxisCells="4" />
+    launcher:xAxisStartPadding="65dip"
+    launcher:xAxisEndPadding="65dip"
+    launcher:yAxisStartPadding="0dip"
+    launcher:yAxisEndPadding="0dip"/>
diff --git a/res/layout-port/all_apps_2d.xml b/res/layout-port/all_apps_2d.xml
index 0607d62..081cba2 100644
--- a/res/layout-port/all_apps_2d.xml
+++ b/res/layout-port/all_apps_2d.xml
@@ -22,7 +22,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:padding="2dip"
-    >
+    android:background="#FF000000">
 
     <GridView android:id="@+id/all_apps_2d_grid"
         android:tag="all_apps_2d_grid"
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index 8dc5092..c50dbca 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -24,12 +24,14 @@
 
     <include layout="@layout/all_apps" />
 
-    <!-- The workspace contains 3 screens of cells -->
+    <!-- The workspace contains 5 screens of cells -->
     <com.android.launcher2.Workspace
         android:id="@+id/workspace"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        launcher:defaultScreen="2">
+        launcher:defaultScreen="2"
+        launcher:cellCountX="4"
+        launcher:cellCountY="4">
 
         <include android:id="@+id/cell1" layout="@layout/workspace_screen" />
         <include android:id="@+id/cell2" layout="@layout/workspace_screen" />
diff --git a/res/layout-port/workspace_screen.xml b/res/layout-port/workspace_screen.xml
index 96df91b..f400c40 100644
--- a/res/layout-port/workspace_screen.xml
+++ b/res/layout-port/workspace_screen.xml
@@ -24,9 +24,7 @@
 
     launcher:cellWidth="@dimen/workspace_cell_width"
     launcher:cellHeight="@dimen/workspace_cell_height"
-    launcher:longAxisStartPadding="8dip"
-    launcher:longAxisEndPadding="@dimen/button_bar_height"
-    launcher:shortAxisStartPadding="0dip"
-    launcher:shortAxisEndPadding="0dip"
-    launcher:shortAxisCells="4"
-    launcher:longAxisCells="4" />
+    launcher:yAxisStartPadding="8dip"
+    launcher:yAxisEndPadding="@dimen/button_bar_height"
+    launcher:xAxisStartPadding="0dip"
+    launcher:xAxisEndPadding="0dip" />
diff --git a/res/layout-xlarge-land/workspace_screen.xml b/res/layout-xlarge-land/workspace_screen.xml
new file mode 100644
index 0000000..15fc3b1
--- /dev/null
+++ b/res/layout-xlarge-land/workspace_screen.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<com.android.launcher2.CellLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
+
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:hapticFeedbackEnabled="false"
+
+    launcher:cellWidth="@dimen/workspace_cell_width"
+    launcher:cellHeight="@dimen/workspace_cell_height"
+    launcher:yAxisStartPadding="40dip"
+    launcher:yAxisEndPadding="40dip"
+    launcher:xAxisStartPadding="256dip"
+    launcher:xAxisEndPadding="256dip" />
diff --git a/res/layout-xlarge-port/workspace_screen.xml b/res/layout-xlarge-port/workspace_screen.xml
new file mode 100644
index 0000000..eb7620c
--- /dev/null
+++ b/res/layout-xlarge-port/workspace_screen.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<com.android.launcher2.CellLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
+
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:hapticFeedbackEnabled="false"
+
+    launcher:cellWidth="@dimen/workspace_cell_width"
+    launcher:cellHeight="@dimen/workspace_cell_height"
+    launcher:yAxisStartPadding="172dip"
+    launcher:yAxisEndPadding="172dip"
+    launcher:xAxisStartPadding="40dip"
+    launcher:xAxisEndPadding="40dip"/>
diff --git a/res/layout-xlarge/all_apps_paged_view_application.xml b/res/layout-xlarge/all_apps_paged_view_application.xml
new file mode 100644
index 0000000..55779e5
--- /dev/null
+++ b/res/layout-xlarge/all_apps_paged_view_application.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<com.android.launcher2.PagedViewIcon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/name"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center_horizontal"
+    android:paddingTop="2dip"
+
+    android:textColor="#FFFFFFFF"
+    android:textSize="14sp"
+    android:shadowColor="#FF000000"
+    android:shadowDx="0.0"
+    android:shadowDy="1.0"
+    android:shadowRadius="1.0"
+
+    android:maxLines="2"
+    android:fadingEdge="horizontal" />
diff --git a/res/layout-xlarge/all_apps_tabbed.xml b/res/layout-xlarge/all_apps_tabbed.xml
new file mode 100644
index 0000000..dbe192c
--- /dev/null
+++ b/res/layout-xlarge/all_apps_tabbed.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.launcher2.AllAppsTabbed
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:background="#30000000">
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+        <TabWidget
+            android:id="@android:id/tabs"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:tabStripEnabled="false"
+            android:paddingBottom="10dp" />
+        <FrameLayout
+            android:id="@android:id/tabcontent"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+            <com.android.launcher2.AllAppsPagedView
+                android:id="@+id/all_apps_paged_view"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                cellCountX="8"
+                cellCountY="4">
+            </com.android.launcher2.AllAppsPagedView>
+        </FrameLayout>
+    </LinearLayout>
+</com.android.launcher2.AllAppsTabbed>
diff --git a/res/layout-xlarge/customize_paged_view_item.xml b/res/layout-xlarge/customize_paged_view_item.xml
new file mode 100644
index 0000000..55779e5
--- /dev/null
+++ b/res/layout-xlarge/customize_paged_view_item.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<com.android.launcher2.PagedViewIcon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/name"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center_horizontal"
+    android:paddingTop="2dip"
+
+    android:textColor="#FFFFFFFF"
+    android:textSize="14sp"
+    android:shadowColor="#FF000000"
+    android:shadowDx="0.0"
+    android:shadowDy="1.0"
+    android:shadowRadius="1.0"
+
+    android:maxLines="2"
+    android:fadingEdge="horizontal" />
diff --git a/res/layout-xlarge/customize_paged_view_wallpaper_placeholder.xml b/res/layout-xlarge/customize_paged_view_wallpaper_placeholder.xml
new file mode 100644
index 0000000..51f624a
--- /dev/null
+++ b/res/layout-xlarge/customize_paged_view_wallpaper_placeholder.xml
@@ -0,0 +1,34 @@
+<?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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/name"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center_vertical"
+    android:paddingTop="2dip"
+
+    android:textColor="#FFFFFFFF"
+    android:textSize="14sp"
+    android:shadowColor="#FF000000"
+    android:shadowDx="0.0"
+    android:shadowDy="1.0"
+    android:shadowRadius="1.0"
+    android:drawableLeft="@drawable/ic_launcher_wallpaper"
+    android:drawablePadding="10dip"
+
+    android:maxLines="2"
+    android:fadingEdge="horizontal" />
diff --git a/res/layout-xlarge/customize_paged_view_widget.xml b/res/layout-xlarge/customize_paged_view_widget.xml
new file mode 100644
index 0000000..da5342b
--- /dev/null
+++ b/res/layout-xlarge/customize_paged_view_widget.xml
@@ -0,0 +1,32 @@
+<?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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/name"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center_horizontal"
+    android:paddingTop="2dip"
+
+    android:textColor="#FFFFFFFF"
+    android:textSize="14sp"
+    android:shadowColor="#FF000000"
+    android:shadowDx="0.0"
+    android:shadowDy="1.0"
+    android:shadowRadius="1.0"
+
+    android:maxLines="2"
+    android:fadingEdge="horizontal" />
diff --git a/res/layout-xlarge/launcher.xml b/res/layout-xlarge/launcher.xml
new file mode 100644
index 0000000..3b39fd1
--- /dev/null
+++ b/res/layout-xlarge/launcher.xml
@@ -0,0 +1,150 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<com.android.launcher2.DragLayer
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
+
+    android:id="@+id/drag_layer"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <include
+        layout="@layout/all_apps_tabbed"
+        android:id="@+id/all_apps_view"
+        android:layout_width="match_parent"
+        android:layout_height="550dip"
+        android:layout_gravity="top"/>
+
+    <!-- The workspace contains 5 screens of cells -->
+    <com.android.launcher2.Workspace
+        android:id="@+id/workspace"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        launcher:defaultScreen="2"
+        launcher:cellCountX="8"
+        launcher:cellCountY="7">
+
+        <include android:id="@+id/cell1" layout="@layout/workspace_screen" />
+        <include android:id="@+id/cell2" layout="@layout/workspace_screen" />
+        <include android:id="@+id/cell3" layout="@layout/workspace_screen" />
+        <include android:id="@+id/cell4" layout="@layout/workspace_screen" />
+        <include android:id="@+id/cell5" layout="@layout/workspace_screen" />
+    </com.android.launcher2.Workspace>
+
+    <RelativeLayout
+        android:id="@+id/all_apps_button_cluster"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="top"
+        android:padding="@dimen/toolbar_padding">
+
+        <ImageView
+            android:id="@+id/search_button"
+            android:src="@drawable/search_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="left"
+
+            android:onClick="onClickSearchButton"
+            android:focusable="true"
+            android:clickable="true"/>
+
+        <ImageView
+            android:id="@+id/all_apps_button"
+            android:src="@drawable/all_apps_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentRight="true"
+            android:layout_marginLeft="@dimen/toolbar_button_spacing"
+
+            android:onClick="onClickAllAppsButton"
+            android:focusable="true"
+            android:clickable="true" />
+
+        <!-- The button to bring up the installed app market.
+             The icon for this button will be dynamically set. -->
+        <ImageView
+            android:id="@+id/market_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignLeft="@id/all_apps_button"
+
+            android:onClick="onClickAppMarketButton"
+            android:focusable="false"
+            android:clickable="false"
+            android:visibility="gone"/>
+
+        <ImageView
+            android:id="@+id/configure_button"
+            android:src="@drawable/configure_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_toLeftOf="@id/all_apps_button"
+            android:layout_marginLeft="@dimen/toolbar_button_spacing"
+
+            android:onClick="onClickConfigureButton"
+            android:focusable="true"
+            android:clickable="true" />
+
+        <com.android.launcher2.DeleteZone
+            android:id="@+id/delete_zone"
+            android:src="@drawable/delete_zone_selector"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignLeft="@id/all_apps_button"
+
+            android:visibility="gone"
+            launcher:direction="horizontal" />
+
+        <com.android.launcher2.ApplicationInfoDropTarget
+            android:id="@+id/info_button"
+            android:src="@drawable/info_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignLeft="@id/configure_button"
+
+            android:visibility="gone"
+            android:focusable="true"
+            android:clickable="true" />
+
+    </RelativeLayout>
+
+    <TabHost
+        android:id="@android:id/tabhost"
+        android:layout_width="match_parent"
+        android:layout_height="550dip"
+        android:layout_gravity="bottom">
+        <LinearLayout
+            android:orientation="vertical"
+            android:background="#40000000"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+            <TabWidget
+                android:id="@android:id/tabs"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_horizontal"
+                android:tabStripEnabled="false"
+                android:paddingBottom="10dp" />
+            <FrameLayout
+                android:id="@android:id/tabcontent"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent">
+             </FrameLayout>
+          </LinearLayout>
+    </TabHost>
+</com.android.launcher2.DragLayer>
diff --git a/res/layout/home_customization_drawer_item.xml b/res/layout/home_customization_drawer_item.xml
new file mode 100644
index 0000000..4d61dbf
--- /dev/null
+++ b/res/layout/home_customization_drawer_item.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="200dip"
+    android:layout_height="200dip"
+    android:padding="20dip"
+    android:orientation="vertical"
+    android:gravity="center_horizontal|center_vertical|clip_vertical"
+    android:textAppearance="?android:attr/textAppearanceMedium"
+    android:textColor="#ffffffff" />
\ No newline at end of file
diff --git a/res/layout/home_customization_drawer_widget.xml b/res/layout/home_customization_drawer_widget.xml
new file mode 100644
index 0000000..1581308
--- /dev/null
+++ b/res/layout/home_customization_drawer_widget.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="600dip"
+    android:layout_height="match_parent"
+    android:padding="20dip"
+    android:orientation="vertical"
+    android:gravity="center_horizontal|center_vertical|clip_vertical"
+    android:textAppearance="?android:attr/textAppearanceMedium"
+    android:textColor="#ffffffff" />
\ No newline at end of file
diff --git a/res/raw/allapps.rs b/res/raw/allapps.rs
deleted file mode 100644
index af5abd3..0000000
--- a/res/raw/allapps.rs
+++ /dev/null
@@ -1,427 +0,0 @@
-#pragma version(1)
-#pragma stateVertex(PV)
-#pragma stateFragment(PFTexNearest)
-#pragma stateStore(PSIcons)
-
-#define PI 3.14159f
-
-int g_SpecialHWWar;
-
-// Attraction to center values from page edge to page center.
-float g_AttractionTable[9];
-float g_FrictionTable[9];
-float g_PhysicsTableSize;
-
-float g_PosPage;
-float g_PosVelocity;
-float g_LastPositionX;
-int g_LastTouchDown;
-float g_DT;
-int g_LastTime;
-int g_PosMax;
-float g_Zoom;
-float g_Animation;
-float g_OldPosPage;
-float g_OldPosVelocity;
-float g_OldZoom;
-float g_MoveToTotalTime;
-float g_MoveToTime;
-float g_MoveToOldPos;
-
-int g_Cols;
-int g_Rows;
-
-// Drawing constants, should be parameters ======
-#define VIEW_ANGLE 1.28700222f
-
-int g_DrawLastFrame;
-int lastFrame(int draw) {
-    // We draw one extra frame to work around the last frame post bug.
-    // We also need to track if we drew the last frame to deal with large DT
-    // in the physics.
-    int ret = g_DrawLastFrame | draw;
-    g_DrawLastFrame = draw;
-    return ret;  // should return draw instead.
-}
-
-void updateReadback() {
-    if ((g_OldPosPage != g_PosPage) ||
-        (g_OldPosVelocity != g_PosVelocity) ||
-        (g_OldZoom != g_Zoom)) {
-
-        g_OldPosPage = g_PosPage;
-        g_OldPosVelocity = g_PosVelocity;
-        g_OldZoom = g_Zoom;
-
-        int i[3];
-        i[0] = g_PosPage * (1 << 16);
-        i[1] = g_PosVelocity * (1 << 16);
-        i[2] = g_OldZoom * (1 << 16);
-        sendToClient(&i[0], 1, 12, 1);
-    }
-}
-
-void setColor(float r, float g, float b, float a) {
-    if (g_SpecialHWWar) {
-        color(0, 0, 0, 0.001f);
-    } else {
-        color(r, g, b, a);
-    }
-}
-
-void init() {
-    g_AttractionTable[0] = 20.0f;
-    g_AttractionTable[1] = 20.0f;
-    g_AttractionTable[2] = 20.0f;
-    g_AttractionTable[3] = 10.0f;
-    g_AttractionTable[4] = -10.0f;
-    g_AttractionTable[5] = -20.0f;
-    g_AttractionTable[6] = -20.0f;
-    g_AttractionTable[7] = -20.0f;
-    g_AttractionTable[8] = -20.0f;  // dup 7 to avoid a clamp later
-    g_FrictionTable[0] = 10.0f;
-    g_FrictionTable[1] = 10.0f;
-    g_FrictionTable[2] = 11.0f;
-    g_FrictionTable[3] = 15.0f;
-    g_FrictionTable[4] = 15.0f;
-    g_FrictionTable[5] = 11.0f;
-    g_FrictionTable[6] = 10.0f;
-    g_FrictionTable[7] = 10.0f;
-    g_FrictionTable[8] = 10.0f;  // dup 7 to avoid a clamp later
-    g_PhysicsTableSize = 7;
-
-    g_PosVelocity = 0;
-    g_PosPage = 0;
-    g_LastTouchDown = 0;
-    g_LastPositionX = 0;
-    g_Zoom = 0;
-    g_Animation = 1.f;
-    g_SpecialHWWar = 1;
-    g_MoveToTime = 0;
-    g_MoveToOldPos = 0;
-    g_MoveToTotalTime = 0.2f; // Duration of scrolling 1 line
-}
-
-void resetHWWar() {
-    g_SpecialHWWar = 1;
-}
-
-void move() {
-    if (g_LastTouchDown) {
-        float dx = -(state->newPositionX - g_LastPositionX);
-        g_PosVelocity = 0;
-        g_PosPage += dx * 5.2f;
-
-        float pmin = -0.49f;
-        float pmax = g_PosMax + 0.49f;
-        g_PosPage = clampf(g_PosPage, pmin, pmax);
-    }
-    g_LastTouchDown = state->newTouchDown;
-    g_LastPositionX = state->newPositionX;
-    g_MoveToTime = 0;
-    //debugF("Move P", g_PosPage);
-}
-
-void moveTo() {
-    g_MoveToTime = g_MoveToTotalTime;
-    g_PosVelocity = 0;
-    g_MoveToOldPos = g_PosPage;
-
-	// debugF("======= moveTo", state->targetPos);
-}
-
-void setZoom() {
-    g_Zoom = state->zoomTarget;
-    g_DrawLastFrame = 1;
-    updateReadback();
-}
-
-void fling() {
-    g_LastTouchDown = 0;
-    g_PosVelocity = -state->flingVelocity * 4;
-    float av = fabsf(g_PosVelocity);
-    float minVel = 3.5f;
-
-    minVel *= 1.f - (fabsf(fracf(g_PosPage + 0.5f) - 0.5f) * 0.45f);
-
-    if (av < minVel && av > 0.2f) {
-        if (g_PosVelocity > 0) {
-            g_PosVelocity = minVel;
-        } else {
-            g_PosVelocity = -minVel;
-        }
-    }
-
-    if (g_PosPage <= 0) {
-        g_PosVelocity = maxf(0, g_PosVelocity);
-    }
-    if (g_PosPage > g_PosMax) {
-        g_PosVelocity = minf(0, g_PosVelocity);
-    }
-}
-
-float
-modf(float x, float y)
-{
-    return x-(y*floorf(x/y));
-}
-
-
-/*
- * Interpolates values in the range 0..1 to a curve that eases in
- * and out.
- */
-float
-getInterpolation(float input) {
-    return (cosf((input + 1) * PI) / 2.0f) + 0.5f;
-}
-
-
-void updatePos() {
-    if (g_LastTouchDown) {
-        return;
-    }
-
-    float tablePosNorm = fracf(g_PosPage + 0.5f);
-    float tablePosF = tablePosNorm * g_PhysicsTableSize;
-    int tablePosI = tablePosF;
-    float tablePosFrac = tablePosF - tablePosI;
-    float accel = lerpf(g_AttractionTable[tablePosI],
-                        g_AttractionTable[tablePosI + 1],
-                        tablePosFrac) * g_DT;
-    float friction = lerpf(g_FrictionTable[tablePosI],
-                        g_FrictionTable[tablePosI + 1],
-                        tablePosFrac) * g_DT;
-
-    if (g_MoveToTime) {
-        // New position is old posiition + (total distance) * (interpolated time)
-        g_PosPage = g_MoveToOldPos + (state->targetPos - g_MoveToOldPos) * getInterpolation((g_MoveToTotalTime - g_MoveToTime) / g_MoveToTotalTime);
-        g_MoveToTime -= g_DT;
-        if (g_MoveToTime <= 0) {
-            g_MoveToTime = 0;
-            g_PosPage = state->targetPos;
-        }
-        return;
-    }
-
-    // If our velocity is low OR acceleration is opposing it, apply it.
-    if (fabsf(g_PosVelocity) < 4.0f || (g_PosVelocity * accel) < 0) {
-        g_PosVelocity += accel;
-    }
-    //debugF("g_PosPage", g_PosPage);
-    //debugF("  g_PosVelocity", g_PosVelocity);
-    //debugF("  friction", friction);
-    //debugF("  accel", accel);
-
-    // Normal physics
-    if (g_PosVelocity > 0) {
-        g_PosVelocity -= friction;
-        g_PosVelocity = maxf(g_PosVelocity, 0);
-    } else {
-        g_PosVelocity += friction;
-        g_PosVelocity = minf(g_PosVelocity, 0);
-    }
-
-    if ((friction > fabsf(g_PosVelocity)) && (friction > fabsf(accel))) {
-        // Special get back to center and overcome friction physics.
-        float t = tablePosNorm - 0.5f;
-        if (fabsf(t) < (friction * g_DT)) {
-            // really close, just snap
-            g_PosPage = roundf(g_PosPage);
-            g_PosVelocity = 0;
-        } else {
-            if (t > 0) {
-                g_PosVelocity = -friction;
-            } else {
-                g_PosVelocity = friction;
-            }
-        }
-    }
-
-    // Check for out of boundry conditions.
-    if (g_PosPage < 0 && g_PosVelocity < 0) {
-        float damp = 1.0 + (g_PosPage * 4);
-        damp = clampf(damp, 0.f, 0.9f);
-        g_PosVelocity *= damp;
-    }
-    if (g_PosPage > g_PosMax && g_PosVelocity > 0) {
-        float damp = 1.0 - ((g_PosPage - g_PosMax) * 4);
-        damp = clampf(damp, 0.f, 0.9f);
-        g_PosVelocity *= damp;
-    }
-
-    g_PosPage += g_PosVelocity * g_DT;
-    g_PosPage = clampf(g_PosPage, -0.49, g_PosMax + 0.49);
-}
-
-
-void
-draw_home_button()
-{
-    setColor(1.0f, 1.0f, 1.0f, 1.0f);
-    bindTexture(NAMED_PFTexNearest, 0, state->homeButtonId);
-
-    float w = getWidth();
-    float h = getHeight();
-
-    float x;
-    float y;
-    if (getWidth() > getHeight()) {
-        x = w - (params->homeButtonTextureWidth * (1 - g_Animation)) + 20;
-        y = (h - params->homeButtonTextureHeight) * 0.5f;
-    } else {
-        x = (w - params->homeButtonTextureWidth) / 2;
-        y = -g_Animation * params->homeButtonTextureHeight;
-        y -= 30; // move the house to the edge of the screen as it doesn't fill the texture.
-    }
-
-    drawSpriteScreenspace(x, y, 0, params->homeButtonTextureWidth, params->homeButtonTextureHeight);
-}
-
-void drawFrontGrid(float rowOffset, float p)
-{
-    float h = getHeight();
-    float w = getWidth();
-
-    int intRowOffset = rowOffset;
-    float rowFrac = rowOffset - intRowOffset;
-    float colWidth = 120.f;//getWidth() / 4;
-    float rowHeight = colWidth + 25.f;
-    float yoff = 0.5f * h + 1.5f * rowHeight;
-
-    int row, col;
-    int colCount = 4;
-    if (w > h) {
-        colCount = 6;
-        rowHeight -= 12.f;
-        yoff = 0.47f * h + 1.0f * rowHeight;
-    }
-
-    int iconNum = (intRowOffset - 5) * colCount;
-
-
-    bindProgramVertex(NAMED_PVCurve);
-
-    vpConstants->Position.z = p;
-
-    setColor(1.0f, 1.0f, 1.0f, 1.0f);
-    for (row = -5; row < 15; row++) {
-        float y = yoff - ((-rowFrac + row) * rowHeight);
-
-        for (col=0; col < colCount; col++) {
-            if (iconNum >= state->iconCount) {
-                return;
-            }
-
-            if (iconNum >= 0) {
-                float x = colWidth * col + (colWidth / 2);
-                vpConstants->Position.x = x + 0.2f;
-
-                if (state->selectedIconIndex == iconNum && !p) {
-                    bindProgramFragment(NAMED_PFTexNearest);
-                    bindTexture(NAMED_PFTexNearest, 0, state->selectedIconTexture);
-                    vpConstants->ImgSize.x = SELECTION_TEXTURE_WIDTH_PX;
-                    vpConstants->ImgSize.y = SELECTION_TEXTURE_HEIGHT_PX;
-                    vpConstants->Position.y = y - (SELECTION_TEXTURE_HEIGHT_PX - ICON_TEXTURE_HEIGHT_PX) * 0.5f;
-                    drawSimpleMesh(NAMED_SMCell);
-                }
-
-                bindProgramFragment(NAMED_PFTexMip);
-                vpConstants->ImgSize.x = ICON_TEXTURE_WIDTH_PX;
-                vpConstants->ImgSize.y = ICON_TEXTURE_HEIGHT_PX;
-                vpConstants->Position.y = y - 0.2f;
-                bindTexture(NAMED_PFTexMip, 0, loadI32(ALLOC_ICON_IDS, iconNum));
-                drawSimpleMesh(NAMED_SMCell);
-
-                bindProgramFragment(NAMED_PFTexMipAlpha);
-                vpConstants->ImgSize.x = 120.f;
-                vpConstants->ImgSize.y = 64.f;
-                vpConstants->Position.y = y - 64.f - 0.2f;
-                bindTexture(NAMED_PFTexMipAlpha, 0, loadI32(ALLOC_LABEL_IDS, iconNum));
-                drawSimpleMesh(NAMED_SMCell);
-            }
-            iconNum++;
-        }
-    }
-}
-
-
-int
-main(int launchID)
-{
-    // Compute dt in seconds.
-    int newTime = uptimeMillis();
-    g_DT = (newTime - g_LastTime) / 1000.f;
-    g_LastTime = newTime;
-
-    if (!g_DrawLastFrame) {
-        // If we stopped rendering we cannot use DT.
-        // assume 30fps in this case.
-        g_DT = 0.033f;
-    }
-    // physics may break if DT is large.
-    g_DT = minf(g_DT, 0.2f);
-
-    if (g_Zoom != state->zoomTarget) {
-        float dz = g_DT * 1.7f;
-        if (state->zoomTarget < 0.5f) {
-            dz = -dz;
-        }
-        if (fabsf(g_Zoom - state->zoomTarget) < fabsf(dz)) {
-            g_Zoom = state->zoomTarget;
-        } else {
-            g_Zoom += dz;
-        }
-        updateReadback();
-    }
-    g_Animation = powf(1-g_Zoom, 3);
-
-    // Set clear value to dim the background based on the zoom position.
-    if ((g_Zoom < 0.001f) && (state->zoomTarget < 0.001f) && !g_SpecialHWWar) {
-        pfClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-        // When we're zoomed out and not tracking motion events, reset the pos to 0.
-        if (!g_LastTouchDown) {
-            g_PosPage = 0;
-        }
-        return lastFrame(0);
-    } else {
-        pfClearColor(0.0f, 0.0f, 0.0f, g_Zoom);
-    }
-
-    // icons & labels
-    int iconCount = state->iconCount;
-    if (getWidth() > getHeight()) {
-        g_Cols = 6;
-        g_Rows = 3;
-    } else {
-        g_Cols = 4;
-        g_Rows = 4;
-    }
-    g_PosMax = ((iconCount + (g_Cols-1)) / g_Cols) - g_Rows;
-    if (g_PosMax < 0) g_PosMax = 0;
-
-    updatePos();
-    updateReadback();
-
-    //debugF("    draw g_PosPage", g_PosPage);
-
-    // Draw the icons ========================================
-    drawFrontGrid(g_PosPage, g_Animation);
-
-    bindProgramFragment(NAMED_PFTexNearest);
-    draw_home_button();
-
-    // This is a WAR to do a rendering pass without drawing during init to
-    // force the driver to preload and compile its shaders.
-    // Without this the first animation does not appear due to the time it
-    // takes to init the driver state.
-    if (g_SpecialHWWar) {
-        g_SpecialHWWar = 0;
-        return 1;
-    }
-
-    // Bug workaround where the last frame is not always displayed
-    // So we keep rendering until the bug is fixed.
-    return lastFrame((g_PosVelocity != 0) || fracf(g_PosPage) || g_Zoom != state->zoomTarget || (g_MoveToTime != 0));
-}
-
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index bad2730..011ea1e 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -27,6 +27,19 @@
     <string name="pick_wallpaper" msgid="5630222540525626723">"Tapety"</string>
     <string name="activity_not_found" msgid="3571057450431950427">"Aplikace není v telefonu nainstalována."</string>
     <string name="configure_wallpaper" msgid="2820186271419674623">"Konfigurace..."</string>
+    <string name="widgets_tab_label" msgid="9145860100000983599">"Widgety"</string>
+    <string name="folders_tab_label" msgid="1145293785541489736">"Složky"</string>
+    <string name="shortcuts_tab_label" msgid="3312788893569416806">"Zástupci"</string>
+    <string name="wallpapers_tab_label" msgid="1617804870364119879">"Tapety"</string>
+    <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Toto bude karta Tapety"</string>
+    <!-- no translation found for all_apps_tab_all (2942727589595027258) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_apps (5468972551904071712) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_games (1855736784923494918) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_downloaded (2300935549064726963) -->
+    <skip />
     <string name="rename_folder_label" msgid="5646236631298452787">"Název složky"</string>
     <string name="rename_folder_title" msgid="4544573104191526550">"Přejmenovat složku"</string>
     <string name="rename_action" msgid="6016003384693240896">"OK"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 6ec1516..08ebc63 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -27,6 +27,19 @@
     <string name="pick_wallpaper" msgid="5630222540525626723">"Tapeter"</string>
     <string name="activity_not_found" msgid="3571057450431950427">"Programmet er ikke installeret på din telefon."</string>
     <string name="configure_wallpaper" msgid="2820186271419674623">"Konfigurer ..."</string>
+    <string name="widgets_tab_label" msgid="9145860100000983599">"Widgets"</string>
+    <string name="folders_tab_label" msgid="1145293785541489736">"Mapper"</string>
+    <string name="shortcuts_tab_label" msgid="3312788893569416806">"Genveje"</string>
+    <string name="wallpapers_tab_label" msgid="1617804870364119879">"Tapeter"</string>
+    <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Dette er fanen for tapeter"</string>
+    <!-- no translation found for all_apps_tab_all (2942727589595027258) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_apps (5468972551904071712) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_games (1855736784923494918) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_downloaded (2300935549064726963) -->
+    <skip />
     <string name="rename_folder_label" msgid="5646236631298452787">"Mappenavn"</string>
     <string name="rename_folder_title" msgid="4544573104191526550">"Omdøb mappe"</string>
     <string name="rename_action" msgid="6016003384693240896">"OK"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index e4f705e..e512dc5 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -27,6 +27,19 @@
     <string name="pick_wallpaper" msgid="5630222540525626723">"Hintergrundbilder"</string>
     <string name="activity_not_found" msgid="3571057450431950427">"Anwendung ist nicht auf dem Telefon 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>
+    <string name="shortcuts_tab_label" msgid="3312788893569416806">"Verknüpfungen"</string>
+    <string name="wallpapers_tab_label" msgid="1617804870364119879">"Hintergründe"</string>
+    <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Dies ist der Tab \"Hintergründe\""</string>
+    <!-- no translation found for all_apps_tab_all (2942727589595027258) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_apps (5468972551904071712) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_games (1855736784923494918) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_downloaded (2300935549064726963) -->
+    <skip />
     <string name="rename_folder_label" msgid="5646236631298452787">"Ordnername"</string>
     <string name="rename_folder_title" msgid="4544573104191526550">"Ordner umbenennen"</string>
     <string name="rename_action" msgid="6016003384693240896">"OK"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 6514964..eb8097f 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -27,6 +27,19 @@
     <string name="pick_wallpaper" msgid="5630222540525626723">"Ταπετσαρίες"</string>
     <string name="activity_not_found" msgid="3571057450431950427">"Η εφαρμογή δεν έχει εγκατασταθεί στο τηλέφωνό σας."</string>
     <string name="configure_wallpaper" msgid="2820186271419674623">"Διαμόρφωση..."</string>
+    <string name="widgets_tab_label" msgid="9145860100000983599">"Γραφικά στοιχεία"</string>
+    <string name="folders_tab_label" msgid="1145293785541489736">"Φάκελοι"</string>
+    <string name="shortcuts_tab_label" msgid="3312788893569416806">"Συντομεύσεις"</string>
+    <string name="wallpapers_tab_label" msgid="1617804870364119879">"Ταπετσαρίες"</string>
+    <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Αυτή θα είναι η καρτέλα ταπετσαριών"</string>
+    <!-- no translation found for all_apps_tab_all (2942727589595027258) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_apps (5468972551904071712) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_games (1855736784923494918) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_downloaded (2300935549064726963) -->
+    <skip />
     <string name="rename_folder_label" msgid="5646236631298452787">"Όνομα φακέλου"</string>
     <string name="rename_folder_title" msgid="4544573104191526550">"Μετονομασία φακέλου"</string>
     <string name="rename_action" msgid="6016003384693240896">"OK"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 5f6b4e5..5a40e3a 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -27,6 +27,19 @@
     <string name="pick_wallpaper" msgid="5630222540525626723">"Papeles tapiz"</string>
     <string name="activity_not_found" msgid="3571057450431950427">"La aplicación no está instalada en tu computadora."</string>
     <string name="configure_wallpaper" msgid="2820186271419674623">"Configurar..."</string>
+    <string name="widgets_tab_label" msgid="9145860100000983599">"Widgets"</string>
+    <string name="folders_tab_label" msgid="1145293785541489736">"Carpetas"</string>
+    <string name="shortcuts_tab_label" msgid="3312788893569416806">"Accesos directos"</string>
+    <string name="wallpapers_tab_label" msgid="1617804870364119879">"Papeles tapiz"</string>
+    <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Ésta será la pestaña para los papeles tapiz"</string>
+    <!-- no translation found for all_apps_tab_all (2942727589595027258) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_apps (5468972551904071712) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_games (1855736784923494918) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_downloaded (2300935549064726963) -->
+    <skip />
     <string name="rename_folder_label" msgid="5646236631298452787">"Nombre de carpeta"</string>
     <string name="rename_folder_title" msgid="4544573104191526550">"Cambiar nombre de carpeta"</string>
     <string name="rename_action" msgid="6016003384693240896">"Aceptar"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 5b46c59..9772526 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -27,6 +27,19 @@
     <string name="pick_wallpaper" msgid="5630222540525626723">"Fondos de pantalla"</string>
     <string name="activity_not_found" msgid="3571057450431950427">"La aplicación no está instalada en el teléfono."</string>
     <string name="configure_wallpaper" msgid="2820186271419674623">"Configurar..."</string>
+    <string name="widgets_tab_label" msgid="9145860100000983599">"Widgets"</string>
+    <string name="folders_tab_label" msgid="1145293785541489736">"Carpetas"</string>
+    <string name="shortcuts_tab_label" msgid="3312788893569416806">"Accesos directos"</string>
+    <string name="wallpapers_tab_label" msgid="1617804870364119879">"Fondos de pantalla"</string>
+    <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Será la carpeta de fondos de pantalla."</string>
+    <!-- no translation found for all_apps_tab_all (2942727589595027258) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_apps (5468972551904071712) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_games (1855736784923494918) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_downloaded (2300935549064726963) -->
+    <skip />
     <string name="rename_folder_label" msgid="5646236631298452787">"Nombre de carpeta"</string>
     <string name="rename_folder_title" msgid="4544573104191526550">"Cambiar nombre de carpeta"</string>
     <string name="rename_action" msgid="6016003384693240896">"Aceptar"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index f1fb440..6d2d638 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -27,6 +27,19 @@
     <string name="pick_wallpaper" msgid="5630222540525626723">"Fonds d\'écran"</string>
     <string name="activity_not_found" msgid="3571057450431950427">"L\'application n\'est pas installée sur votre téléphone."</string>
     <string name="configure_wallpaper" msgid="2820186271419674623">"Configurer..."</string>
+    <string name="widgets_tab_label" msgid="9145860100000983599">"Widgets"</string>
+    <string name="folders_tab_label" msgid="1145293785541489736">"Dossiers"</string>
+    <string name="shortcuts_tab_label" msgid="3312788893569416806">"Raccourcis"</string>
+    <string name="wallpapers_tab_label" msgid="1617804870364119879">"Fonds d\'écran"</string>
+    <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Onglet des fonds d\'écran"</string>
+    <!-- no translation found for all_apps_tab_all (2942727589595027258) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_apps (5468972551904071712) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_games (1855736784923494918) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_downloaded (2300935549064726963) -->
+    <skip />
     <string name="rename_folder_label" msgid="5646236631298452787">"Nom du dossier"</string>
     <string name="rename_folder_title" msgid="4544573104191526550">"Renommer le dossier"</string>
     <string name="rename_action" msgid="6016003384693240896">"OK"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 950e15f..f3fffbe3 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -27,6 +27,19 @@
     <string name="pick_wallpaper" msgid="5630222540525626723">"Sfondi"</string>
     <string name="activity_not_found" msgid="3571057450431950427">"Applicazione non installata sul telefono."</string>
     <string name="configure_wallpaper" msgid="2820186271419674623">"Configura..."</string>
+    <string name="widgets_tab_label" msgid="9145860100000983599">"Widget"</string>
+    <string name="folders_tab_label" msgid="1145293785541489736">"Cartelle"</string>
+    <string name="shortcuts_tab_label" msgid="3312788893569416806">"Scorciatorie"</string>
+    <string name="wallpapers_tab_label" msgid="1617804870364119879">"Sfondi"</string>
+    <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Questa sarà la scheda degli sfondi"</string>
+    <!-- no translation found for all_apps_tab_all (2942727589595027258) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_apps (5468972551904071712) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_games (1855736784923494918) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_downloaded (2300935549064726963) -->
+    <skip />
     <string name="rename_folder_label" msgid="5646236631298452787">"Nome cartella"</string>
     <string name="rename_folder_title" msgid="4544573104191526550">"Rinomina cartella"</string>
     <string name="rename_action" msgid="6016003384693240896">"OK"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 021b895..70e7852 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -27,6 +27,19 @@
     <string name="pick_wallpaper" msgid="5630222540525626723">"壁紙"</string>
     <string name="activity_not_found" msgid="3571057450431950427">"アプリケーションがインストールされていません。"</string>
     <string name="configure_wallpaper" msgid="2820186271419674623">"設定..."</string>
+    <string name="widgets_tab_label" msgid="9145860100000983599">"ウィジェット"</string>
+    <string name="folders_tab_label" msgid="1145293785541489736">"フォルダ"</string>
+    <string name="shortcuts_tab_label" msgid="3312788893569416806">"ショートカット"</string>
+    <string name="wallpapers_tab_label" msgid="1617804870364119879">"壁紙"</string>
+    <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"ここが壁紙タブになります"</string>
+    <!-- no translation found for all_apps_tab_all (2942727589595027258) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_apps (5468972551904071712) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_games (1855736784923494918) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_downloaded (2300935549064726963) -->
+    <skip />
     <string name="rename_folder_label" msgid="5646236631298452787">"フォルダ名"</string>
     <string name="rename_folder_title" msgid="4544573104191526550">"フォルダ名を変更"</string>
     <string name="rename_action" msgid="6016003384693240896">"OK"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 341c7ee..5c99a17 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -27,6 +27,19 @@
     <string name="pick_wallpaper" msgid="5630222540525626723">"배경화면"</string>
     <string name="activity_not_found" msgid="3571057450431950427">"휴대전화에 설치되어 있지 않은 애플리케이션입니다."</string>
     <string name="configure_wallpaper" msgid="2820186271419674623">"구성..."</string>
+    <string name="widgets_tab_label" msgid="9145860100000983599">"위젯"</string>
+    <string name="folders_tab_label" msgid="1145293785541489736">"폴더"</string>
+    <string name="shortcuts_tab_label" msgid="3312788893569416806">"바로가기"</string>
+    <string name="wallpapers_tab_label" msgid="1617804870364119879">"배경화면"</string>
+    <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"배경화면 탭이 됩니다."</string>
+    <!-- no translation found for all_apps_tab_all (2942727589595027258) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_apps (5468972551904071712) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_games (1855736784923494918) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_downloaded (2300935549064726963) -->
+    <skip />
     <string name="rename_folder_label" msgid="5646236631298452787">"폴더 이름"</string>
     <string name="rename_folder_title" msgid="4544573104191526550">"폴더 이름 바꾸기"</string>
     <string name="rename_action" msgid="6016003384693240896">"확인"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 54418e1..aa24d7a 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -27,6 +27,19 @@
     <string name="pick_wallpaper" msgid="5630222540525626723">"Bakgrunner"</string>
     <string name="activity_not_found" msgid="3571057450431950427">"Applikasjonen er ikke installert."</string>
     <string name="configure_wallpaper" msgid="2820186271419674623">"Konfigurer"</string>
+    <string name="widgets_tab_label" msgid="9145860100000983599">"Moduler"</string>
+    <string name="folders_tab_label" msgid="1145293785541489736">"Mapper"</string>
+    <string name="shortcuts_tab_label" msgid="3312788893569416806">"Snarveier"</string>
+    <string name="wallpapers_tab_label" msgid="1617804870364119879">"Bakgrunner"</string>
+    <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Dette er nå bakgrunnsfanen"</string>
+    <!-- no translation found for all_apps_tab_all (2942727589595027258) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_apps (5468972551904071712) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_games (1855736784923494918) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_downloaded (2300935549064726963) -->
+    <skip />
     <string name="rename_folder_label" msgid="5646236631298452787">"Mappenavn"</string>
     <string name="rename_folder_title" msgid="4544573104191526550">"Gi nytt navn til mappe"</string>
     <string name="rename_action" msgid="6016003384693240896">"OK"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index f5ac90e..0777dea 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -27,6 +27,19 @@
     <string name="pick_wallpaper" msgid="5630222540525626723">"Achtergronden"</string>
     <string name="activity_not_found" msgid="3571057450431950427">"Deze toepassing is niet geïnstalleerd op uw telefoon."</string>
     <string name="configure_wallpaper" msgid="2820186271419674623">"Configureren..."</string>
+    <string name="widgets_tab_label" msgid="9145860100000983599">"Widgets"</string>
+    <string name="folders_tab_label" msgid="1145293785541489736">"Mappen"</string>
+    <string name="shortcuts_tab_label" msgid="3312788893569416806">"Sneltoetsen"</string>
+    <string name="wallpapers_tab_label" msgid="1617804870364119879">"Achtergronden"</string>
+    <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Dit wordt het tabblad \'Achtergronden\'"</string>
+    <!-- no translation found for all_apps_tab_all (2942727589595027258) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_apps (5468972551904071712) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_games (1855736784923494918) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_downloaded (2300935549064726963) -->
+    <skip />
     <string name="rename_folder_label" msgid="5646236631298452787">"Mapnaam"</string>
     <string name="rename_folder_title" msgid="4544573104191526550">"Naam van map wijzigen"</string>
     <string name="rename_action" msgid="6016003384693240896">"OK"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index d50a59a..33a9da7 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -27,6 +27,19 @@
     <string name="pick_wallpaper" msgid="5630222540525626723">"Tapety"</string>
     <string name="activity_not_found" msgid="3571057450431950427">"Aplikacja nie jest zainstalowana w telefonie."</string>
     <string name="configure_wallpaper" msgid="2820186271419674623">"Konfiguruj..."</string>
+    <string name="widgets_tab_label" msgid="9145860100000983599">"Widżety"</string>
+    <string name="folders_tab_label" msgid="1145293785541489736">"Foldery"</string>
+    <string name="shortcuts_tab_label" msgid="3312788893569416806">"Skróty"</string>
+    <string name="wallpapers_tab_label" msgid="1617804870364119879">"Tapety"</string>
+    <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"To będzie karta tapet"</string>
+    <!-- no translation found for all_apps_tab_all (2942727589595027258) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_apps (5468972551904071712) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_games (1855736784923494918) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_downloaded (2300935549064726963) -->
+    <skip />
     <string name="rename_folder_label" msgid="5646236631298452787">"Nazwa folderu"</string>
     <string name="rename_folder_title" msgid="4544573104191526550">"Zmień nazwę folderu"</string>
     <string name="rename_action" msgid="6016003384693240896">"OK"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index df63f30..bdee633 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -27,6 +27,19 @@
     <string name="pick_wallpaper" msgid="5630222540525626723">"Imagens de fundo"</string>
     <string name="activity_not_found" msgid="3571057450431950427">"A aplicação não está instalada no telefone."</string>
     <string name="configure_wallpaper" msgid="2820186271419674623">"Configurar..."</string>
+    <string name="widgets_tab_label" msgid="9145860100000983599">"Widgets"</string>
+    <string name="folders_tab_label" msgid="1145293785541489736">"Pastas"</string>
+    <string name="shortcuts_tab_label" msgid="3312788893569416806">"Atalhos"</string>
+    <string name="wallpapers_tab_label" msgid="1617804870364119879">"Imagens de fundo"</string>
+    <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Isto será o separador de imagens de fundo"</string>
+    <!-- no translation found for all_apps_tab_all (2942727589595027258) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_apps (5468972551904071712) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_games (1855736784923494918) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_downloaded (2300935549064726963) -->
+    <skip />
     <string name="rename_folder_label" msgid="5646236631298452787">"Nome da pasta"</string>
     <string name="rename_folder_title" msgid="4544573104191526550">"Mudar o nome da pasta"</string>
     <string name="rename_action" msgid="6016003384693240896">"OK"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index b72f7e0..1386d35 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -27,6 +27,19 @@
     <string name="pick_wallpaper" msgid="5630222540525626723">"Papéis de parede"</string>
     <string name="activity_not_found" msgid="3571057450431950427">"O aplicativo não está instalado no seu telefone."</string>
     <string name="configure_wallpaper" msgid="2820186271419674623">"Configurar..."</string>
+    <string name="widgets_tab_label" msgid="9145860100000983599">"Widgets"</string>
+    <string name="folders_tab_label" msgid="1145293785541489736">"Pastas"</string>
+    <string name="shortcuts_tab_label" msgid="3312788893569416806">"Atalhos"</string>
+    <string name="wallpapers_tab_label" msgid="1617804870364119879">"Papéis de parede"</string>
+    <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Esta será a guia de papéis de parede"</string>
+    <!-- no translation found for all_apps_tab_all (2942727589595027258) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_apps (5468972551904071712) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_games (1855736784923494918) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_downloaded (2300935549064726963) -->
+    <skip />
     <string name="rename_folder_label" msgid="5646236631298452787">"Nome da pasta"</string>
     <string name="rename_folder_title" msgid="4544573104191526550">"Renomear pasta"</string>
     <string name="rename_action" msgid="6016003384693240896">"OK"</string>
diff --git a/res/values-rm/strings.xml b/res/values-rm/strings.xml
new file mode 100644
index 0000000..505f7ca
--- /dev/null
+++ b/res/values-rm/strings.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="application_name" msgid="8424725141379931883">"Lantschader"</string>
+    <string name="uid_name" msgid="3371120195364560632">"Applicaziuns da basa dad Android"</string>
+    <string name="folder_name" msgid="4588446541914685904">"Ordinatur"</string>
+    <string name="chooser_wallpaper" msgid="5988031014201479733">"Tscherner in fund davos"</string>
+    <string name="wallpaper_instructions" msgid="4215640646180727542">"Definir in fund davos"</string>
+    <string name="pick_wallpaper" msgid="5630222540525626723">"Maletgs da fund davos"</string>
+    <string name="activity_not_found" msgid="3571057450431950427">"L\'applicaziun n\'è betg installada sin quest telefonin."</string>
+    <string name="configure_wallpaper" msgid="2820186271419674623">"Configurar…"</string>
+    <!-- no translation found for widgets_tab_label (9145860100000983599) -->
+    <skip />
+    <!-- no translation found for folders_tab_label (1145293785541489736) -->
+    <skip />
+    <!-- no translation found for shortcuts_tab_label (3312788893569416806) -->
+    <skip />
+    <!-- no translation found for wallpapers_tab_label (1617804870364119879) -->
+    <skip />
+    <!-- no translation found for wallpapers_temp_tab_text (1660218201190495279) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_all (2942727589595027258) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_apps (5468972551904071712) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_games (1855736784923494918) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_downloaded (2300935549064726963) -->
+    <skip />
+    <string name="rename_folder_label" msgid="5646236631298452787">"Num da l\'ordinatur"</string>
+    <string name="rename_folder_title" msgid="4544573104191526550">"Renumnar l\'ordinatur"</string>
+    <string name="rename_action" msgid="6016003384693240896">"OK"</string>
+    <string name="cancel_action" msgid="3811860427489435048">"Interrumper"</string>
+    <string name="menu_item_add_item" msgid="6233177331075781114">"Agiuntar al visur da partenza"</string>
+    <string name="group_applications" msgid="4118484163419674240">"Applicaziuns"</string>
+    <string name="group_shortcuts" msgid="9133529424900391877">"Scursanidas"</string>
+    <string name="group_folder" msgid="5143593791798929193">"Nov ordinatur"</string>
+    <string name="group_live_folders" msgid="2664945399140647217">"Ordinatur"</string>
+    <string name="group_widgets" msgid="6704978494073105844">"Widgets"</string>
+    <string name="group_wallpapers" msgid="1568191644272224858">"Funds davos"</string>
+    <string name="add_folder" msgid="3521088587367839879">"Ordinatur"</string>
+    <string name="add_clock" msgid="2337943840175865746">"Ura"</string>
+    <string name="add_photo_frame" msgid="3154058437359487954">"Rom da maletgs"</string>
+    <string name="out_of_space" msgid="8365249326091984698">"Nagin spazi liber sin il visur da partenza."</string>
+    <string name="shortcut_installed" msgid="7071557296331322355">"Creà ina scursanida \"<xliff:g id="NAME">%s</xliff:g>\"."</string>
+    <string name="shortcut_uninstalled" msgid="2129499669449749995">"La scursanida \"<xliff:g id="NAME">%s</xliff:g>\" è vegnida stizzada."</string>
+    <string name="shortcut_duplicate" msgid="4757756326465060694">"La scursanida \"<xliff:g id="NAME">%s</xliff:g>\" exista gia."</string>
+    <string name="title_select_shortcut" msgid="2858897527672831763">"Tscherner ina cumbinaziun da tastas"</string>
+    <string name="title_select_live_folder" msgid="3753447798805166749">"Tscherner l\'ordinatur"</string>
+    <string name="all_apps_button_label" msgid="3953036962111614813">"Tut las applicaziuns"</string>
+    <string name="all_apps_home_button_label" msgid="1022222300329398558">"Pagina da partenza"</string>
+    <string name="menu_add" msgid="3065046628354640854">"Agiuntar"</string>
+    <string name="menu_wallpaper" msgid="5837429080911269832">"Fund davos"</string>
+    <string name="menu_search" msgid="4826514464423239041">"Tschertgar"</string>
+    <string name="menu_notifications" msgid="6424587053194766192">"Avis"</string>
+    <string name="menu_gestures" msgid="514678675575912237">"Moviments"</string>
+    <string name="menu_settings" msgid="6233960148378443661">"Parameters"</string>
+    <string name="permlab_install_shortcut" msgid="1201690825493376489">"Installar scursanidas"</string>
+    <string name="permdesc_install_shortcut" msgid="7429365847558984148">"Pussibilitescha ch\'ina applicaziun agiunta scursanidas senza l\'intervenziun da l\'utilisader."</string>
+    <string name="permlab_uninstall_shortcut" msgid="7696645932555926449">"deinstallar scursanidas"</string>
+    <string name="permdesc_uninstall_shortcut" msgid="959972195916090900">"Pussibilitescha ch\'ina applicaziun possia stizzar scursanidas senza ina intervenziun da l\'utilisader."</string>
+    <string name="permlab_read_settings" msgid="3452408290738106747">"Leger ils parameters e las scursanidas da la pagina da partenza"</string>
+    <string name="permdesc_read_settings" msgid="8377434937176025492">"Pussibilitescha ch\'ina applicaziun possia leger ils parameters e las scursanidas da la pagina da partenza."</string>
+    <string name="permlab_write_settings" msgid="1360567537236705628">"Definir ils parameters e las scursanidas per la pagina da partenza"</string>
+    <string name="permdesc_write_settings" msgid="1098648778383349818">"Pussibilitescha ch\'ina applicaziun possia midar ils parameters e las scursanidas sin la pagina da partenza."</string>
+    <string name="gadget_error_text" msgid="8359351016167075858">"Problems cun chargiar il widget"</string>
+</resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index b151618..60e93de 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -27,6 +27,19 @@
     <string name="pick_wallpaper" msgid="5630222540525626723">"Обои"</string>
     <string name="activity_not_found" msgid="3571057450431950427">"Приложение не установлено на телефоне."</string>
     <string name="configure_wallpaper" msgid="2820186271419674623">"Настроить..."</string>
+    <string name="widgets_tab_label" msgid="9145860100000983599">"Виджеты"</string>
+    <string name="folders_tab_label" msgid="1145293785541489736">"Папки"</string>
+    <string name="shortcuts_tab_label" msgid="3312788893569416806">"Ярлыки"</string>
+    <string name="wallpapers_tab_label" msgid="1617804870364119879">"Обои"</string>
+    <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Это будет вкладка обоев"</string>
+    <!-- no translation found for all_apps_tab_all (2942727589595027258) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_apps (5468972551904071712) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_games (1855736784923494918) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_downloaded (2300935549064726963) -->
+    <skip />
     <string name="rename_folder_label" msgid="5646236631298452787">"Название папки"</string>
     <string name="rename_folder_title" msgid="4544573104191526550">"Переименовать папку"</string>
     <string name="rename_action" msgid="6016003384693240896">"ОК"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 9c29ef1..f27c401 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -27,6 +27,19 @@
     <string name="pick_wallpaper" msgid="5630222540525626723">"Bakgrundsbilder"</string>
     <string name="activity_not_found" msgid="3571057450431950427">"Programmet är inte installerat på din telefon."</string>
     <string name="configure_wallpaper" msgid="2820186271419674623">"Konfigurera..."</string>
+    <string name="widgets_tab_label" msgid="9145860100000983599">"Widgetar"</string>
+    <string name="folders_tab_label" msgid="1145293785541489736">"Mappar"</string>
+    <string name="shortcuts_tab_label" msgid="3312788893569416806">"Genvägar"</string>
+    <string name="wallpapers_tab_label" msgid="1617804870364119879">"Bakgrundsbilder"</string>
+    <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Det här kommer att vara fliken för bakgrundsbilder"</string>
+    <!-- no translation found for all_apps_tab_all (2942727589595027258) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_apps (5468972551904071712) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_games (1855736784923494918) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_downloaded (2300935549064726963) -->
+    <skip />
     <string name="rename_folder_label" msgid="5646236631298452787">"Mappnamn"</string>
     <string name="rename_folder_title" msgid="4544573104191526550">"Byt namn på mapp"</string>
     <string name="rename_action" msgid="6016003384693240896">"OK"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 43f7520..689c186 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -27,6 +27,19 @@
     <string name="pick_wallpaper" msgid="5630222540525626723">"Duvar Kağıtları"</string>
     <string name="activity_not_found" msgid="3571057450431950427">"Uygulama telefonunuza yüklenmemiş."</string>
     <string name="configure_wallpaper" msgid="2820186271419674623">"Yapılandır..."</string>
+    <string name="widgets_tab_label" msgid="9145860100000983599">"Widget\'lar"</string>
+    <string name="folders_tab_label" msgid="1145293785541489736">"Klasörler"</string>
+    <string name="shortcuts_tab_label" msgid="3312788893569416806">"Kısayollar"</string>
+    <string name="wallpapers_tab_label" msgid="1617804870364119879">"Duvar Kağıtları"</string>
+    <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Bu duvar kağıdı sekmesi olacaktır"</string>
+    <!-- no translation found for all_apps_tab_all (2942727589595027258) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_apps (5468972551904071712) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_games (1855736784923494918) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_downloaded (2300935549064726963) -->
+    <skip />
     <string name="rename_folder_label" msgid="5646236631298452787">"Klasör adı"</string>
     <string name="rename_folder_title" msgid="4544573104191526550">"Klasörü yeniden adlandır"</string>
     <string name="rename_action" msgid="6016003384693240896">"Tamam"</string>
diff --git a/res/values-xlarge/colors.xml b/res/values-xlarge/colors.xml
new file mode 100644
index 0000000..a6cdd06
--- /dev/null
+++ b/res/values-xlarge/colors.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+<resources>
+    <color name="app_info_filter">#A50000FE</color>
+</resources>
\ No newline at end of file
diff --git a/res/values-xlarge/config.xml b/res/values-xlarge/config.xml
new file mode 100644
index 0000000..9b1bc0d
--- /dev/null
+++ b/res/values-xlarge/config.xml
@@ -0,0 +1,29 @@
+<resources>
+    <!--  NOTE: Many of the all apps values here are also used for the customization drawer -->
+
+    <!-- Duration in milliseconds of the all apps zoom-in animation. -->
+    <!-- NB: This should be less than the workspaceShrinkTime as they happen together. -->
+    <integer name="config_allAppsZoomInTime">350</integer>
+
+    <!-- Duration in milliseconds of the all apps zoom-out animation -->
+    <!-- NB: This should be less than the workspaceUnshrinkTime as they happen together. -->
+    <integer name="config_allAppsZoomOutTime">350</integer>
+
+    <!-- Scaling factor used in the all apps zooming animations -->
+    <integer name="config_allAppsZoomScaleFactor">5</integer>
+
+    <!-- The extra distance off-screen that all apps appears from -->
+    <integer name="config_allAppsVerticalOffset">200</integer>
+
+    <!-- Duration in milliseconds of the animations between all apps, customize, & home.
+         NOTE: If these are changed, the toolbar animation times below should also be. -->
+    <integer name="config_allAppsCameraPanTime">700</integer>
+    <integer name="config_workspaceShrinkTime">700</integer>
+    <integer name="config_workspaceUnshrinkTime">700</integer>
+
+    <!-- Duration in milliseconds toolbar fade in and fade out animations.
+         NOTE: Fade in and fade out time should together be less the transition
+         animations between all apps, customize, & the workspace. -->
+    <integer name="config_toolbarButtonFadeInTime">350</integer>
+    <integer name="config_toolbarButtonFadeOutTime">350</integer>
+</resources>
diff --git a/res/values-xlarge/dimens.xml b/res/values-xlarge/dimens.xml
new file mode 100644
index 0000000..1010390
--- /dev/null
+++ b/res/values-xlarge/dimens.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <dimen name="workspace_cell_width">76dip</dimen>
+    <dimen name="workspace_cell_height">76dip</dimen>
+
+    <!-- horizontal spacing between mini screen thumbnails ie. in all
+         apps and in customization mode -->
+    <dimen name="smallScreenSpacing">10dip</dimen>
+
+    <!-- vertical spacing between edge of screen and mini screen thumbnails -->
+    <dimen name="smallScreenVerticalMargin">20dip</dimen>
+
+    <dimen name="toolbar_padding">10dip</dimen>
+
+    <dimen name="toolbar_button_spacing">20dip</dimen>
+</resources>
diff --git a/res/values-xlarge/styles.xml b/res/values-xlarge/styles.xml
new file mode 100644
index 0000000..e4c047f
--- /dev/null
+++ b/res/values-xlarge/styles.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+
+<resources>
+    <style name="WorkspaceIcon.Portrait">
+        <item name="android:drawablePadding">4dip</item>
+        <item name="android:paddingTop">1dip</item>
+    </style>
+
+    <style name="WorkspaceIcon.Landscape">
+        <item name="android:drawablePadding">4dip</item>
+        <item name="android:paddingTop">1dip</item>
+    </style>
+</resources>
diff --git a/res/values-xlarge/wallpapers.xml b/res/values-xlarge/wallpapers.xml
new file mode 100644
index 0000000..90a6af7
--- /dev/null
+++ b/res/values-xlarge/wallpapers.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<resources>
+    <string-array name="wallpapers" translatable="false">
+        <item>wallpaper_leaf</item>
+    </string-array>
+</resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 0705787..15543bc 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -27,6 +27,19 @@
     <string name="pick_wallpaper" msgid="5630222540525626723">"壁纸"</string>
     <string name="activity_not_found" msgid="3571057450431950427">"您的手机上未安装应用程序。"</string>
     <string name="configure_wallpaper" msgid="2820186271419674623">"配置..."</string>
+    <string name="widgets_tab_label" msgid="9145860100000983599">"窗口小部件"</string>
+    <string name="folders_tab_label" msgid="1145293785541489736">"文件夹"</string>
+    <string name="shortcuts_tab_label" msgid="3312788893569416806">"快捷方式"</string>
+    <string name="wallpapers_tab_label" msgid="1617804870364119879">"壁纸"</string>
+    <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"这将会成为壁纸标签"</string>
+    <!-- no translation found for all_apps_tab_all (2942727589595027258) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_apps (5468972551904071712) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_games (1855736784923494918) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_downloaded (2300935549064726963) -->
+    <skip />
     <string name="rename_folder_label" msgid="5646236631298452787">"文件夹名称"</string>
     <string name="rename_folder_title" msgid="4544573104191526550">"重命名文件夹"</string>
     <string name="rename_action" msgid="6016003384693240896">"确定"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index de37e19..0467f3b 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -27,6 +27,19 @@
     <string name="pick_wallpaper" msgid="5630222540525626723">"桌布"</string>
     <string name="activity_not_found" msgid="3571057450431950427">"應用程式未安裝到手機。"</string>
     <string name="configure_wallpaper" msgid="2820186271419674623">"設定..."</string>
+    <string name="widgets_tab_label" msgid="9145860100000983599">"小工具"</string>
+    <string name="folders_tab_label" msgid="1145293785541489736">"資料夾"</string>
+    <string name="shortcuts_tab_label" msgid="3312788893569416806">"快速鍵"</string>
+    <string name="wallpapers_tab_label" msgid="1617804870364119879">"桌布"</string>
+    <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"桌布標籤保留位"</string>
+    <!-- no translation found for all_apps_tab_all (2942727589595027258) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_apps (5468972551904071712) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_games (1855736784923494918) -->
+    <skip />
+    <!-- no translation found for all_apps_tab_downloaded (2300935549064726963) -->
+    <skip />
     <string name="rename_folder_label" msgid="5646236631298452787">"資料夾名稱"</string>
     <string name="rename_folder_title" msgid="4544573104191526550">"重新命名資料夾"</string>
     <string name="rename_action" msgid="6016003384693240896">"確定"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 7839120..09fb0da 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -32,6 +32,10 @@
     <declare-styleable name="Workspace">
         <!-- The first screen the workspace should display. -->
         <attr name="defaultScreen" format="integer"  />
+        <!-- The number of horizontal cells in the CellLayout -->
+        <attr name="cellCountX" format="integer"  />
+        <!-- The number of vertical cells in the CellLayout -->
+        <attr name="cellCountY" format="integer"  />
     </declare-styleable>
     
     <!-- CellLayout specific attributes. These attributes are used to customize
@@ -42,17 +46,22 @@
         <!-- The height of a single cell -->
         <attr name="cellHeight" format="dimension"  />
         <!-- Padding to apply at the start of the long axis -->
-        <attr name="longAxisStartPadding" format="dimension"  />
+        <attr name="xAxisStartPadding" format="dimension"  />
         <!-- Padding to apply at the end of the long axis -->
-        <attr name="longAxisEndPadding" format="dimension"  />
+        <attr name="xAxisEndPadding" format="dimension"  />
         <!-- Padding to apply at the start of the short axis -->
-        <attr name="shortAxisStartPadding" format="dimension"  />
+        <attr name="yAxisStartPadding" format="dimension"  />
         <!-- Padding to apply at the end of the short axis -->
-        <attr name="shortAxisEndPadding" format="dimension"  />
-        <!-- Number of cells on the short axis of the CellLayout -->
-        <attr name="shortAxisCells" format="integer" />
-        <!-- Number of cells on the long axis of the CellLayout -->
-        <attr name="longAxisCells" format="integer" />
+        <attr name="yAxisEndPadding" format="dimension"  />
+    </declare-styleable>
+
+    <!-- PagedView specific attributes. These attributes are used to customize
+         a PagedView view in XML files. -->
+    <declare-styleable name="PagedView">
+        <!-- The number of horizontal cells in a page -->
+        <attr name="cellCountX" />
+        <!-- The number of vertical cells in a page -->
+        <attr name="cellCountY" />
     </declare-styleable>
 
     <!-- DeleteZone specific attributes. These attributes are used to customize
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 86c1b3c..0a704f8 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -43,6 +43,27 @@
     <string name="activity_not_found">Application is not installed on your phone.</string>
     <!-- List item for configuring the current wallpaper -->
     <string name="configure_wallpaper">Configure...</string>
+    <!--  Labels for the tabs in the customize drawer -->
+    <string name="widgets_tab_label">Widgets</string>
+    <!--  Title of tab containing all possible folders (ie standard Folders and Live Folders) that 
+          can be added to the home screen -->
+    <string name="folders_tab_label">Folders</string>
+    <!--  Title of tab containing all possible shortcuts (eg Contacts, Bookmarks) that can be
+          added to the home screen -->
+    <string name="shortcuts_tab_label">Shortcuts</string>
+    <!--  Title of tab for configuring wallpapers -->
+    <string name="wallpapers_tab_label">Wallpapers</string>
+    <!--  Placeholder text, will be removed -->
+    <string name="wallpapers_temp_tab_text">This will be the wallpapers tab</string>
+    <!--  Labels for the tabs in All Apps -->
+    <!--  Title of the tab for all applications (includes games and non-games) -->
+    <string name="all_apps_tab_all">All</string>
+    <!--  Title of the tab for all applications *except* games -->
+    <string name="all_apps_tab_apps">Apps</string>
+    <!--  Title of the tab for applications labeled as games -->
+    <string name="all_apps_tab_games">Games</string>
+    <!--  Tile of the tab for applications that were downloaded from market -->
+    <string name="all_apps_tab_downloaded">Downloaded</string>
 
     <!-- Folders -->
     <skip />
diff --git a/src/com/android/launcher2/AllApps2D.java b/src/com/android/launcher2/AllApps2D.java
index 7ad5e49..9764f23 100644
--- a/src/com/android/launcher2/AllApps2D.java
+++ b/src/com/android/launcher2/AllApps2D.java
@@ -16,32 +16,30 @@
 
 package com.android.launcher2;
 
+import com.android.launcher.R;
+
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Resources;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.Bitmap;
-import android.graphics.Color;
+import android.graphics.drawable.BitmapDrawable;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.KeyEvent;
-import android.view.ViewGroup;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.animation.AnimationUtils;
-import android.view.ViewConfiguration;
 import android.widget.AdapterView;
-import android.widget.ImageButton;
-import android.widget.TextView;
 import android.widget.ArrayAdapter;
 import android.widget.GridView;
+import android.widget.ImageButton;
 import android.widget.RelativeLayout;
+import android.widget.TextView;
 
 import java.util.ArrayList;
 import java.util.Collections;
 
-import com.android.launcher.R;
-
 public class AllApps2D
         extends RelativeLayout
         implements AllAppsView,
@@ -58,8 +56,16 @@
 
     private GridView mGrid;
 
+    /** All applications in the system (we might only be showing a subset) */
     private ArrayList<ApplicationInfo> mAllAppsList = new ArrayList<ApplicationInfo>();
 
+    /** Currently visible applications in the grid */
+    private ArrayList<ApplicationInfo> mVisibleAppsList = new ArrayList<ApplicationInfo>();
+
+    public enum AppType { APP, GAME, DOWNLOADED, ALL };
+
+    private AppType mCurrentFilter = AppType.ALL;
+
     // preserve compatibility with 3D all apps:
     //    0.0 -> hidden
     //    1.0 -> shown and opaque
@@ -120,30 +126,27 @@
         setVisibility(View.GONE);
         setSoundEffectsEnabled(false);
 
-        mAppsAdapter = new AppsAdapter(getContext(), mAllAppsList);
-        mAppsAdapter.setNotifyOnChange(false);
+        mAppsAdapter = new AppsAdapter(getContext(), mVisibleAppsList);
     }
 
     @Override
     protected void onFinishInflate() {
-        setBackgroundColor(Color.BLACK);
-
         try {
             mGrid = (GridView)findViewWithTag("all_apps_2d_grid");
             if (mGrid == null) throw new Resources.NotFoundException();
             mGrid.setOnItemClickListener(this);
             mGrid.setOnItemLongClickListener(this);
-            mGrid.setBackgroundColor(Color.BLACK);
-            mGrid.setCacheColorHint(Color.BLACK);
             
+            // The home button is optional; some layouts might not use it
             ImageButton homeButton = (ImageButton) findViewWithTag("all_apps_2d_home");
-            if (homeButton == null) throw new Resources.NotFoundException();
-            homeButton.setOnClickListener(
-                new View.OnClickListener() {
-                    public void onClick(View v) {
-                        mLauncher.closeAllApps(true);
-                    }
-                });
+            if (homeButton != null) {
+                homeButton.setOnClickListener(
+                    new View.OnClickListener() {
+                        public void onClick(View v) {
+                            mLauncher.closeAllApps(true);
+                        }
+                    });
+            }
         } catch (Resources.NotFoundException e) {
             Log.e(TAG, "Can't find necessary layout elements for AllApps2D");
         }
@@ -250,19 +253,17 @@
         return mZoom > 0.001f;
     }
 
-    @Override
-    public boolean isOpaque() {
-        return mZoom > 0.999f;
+    public boolean isAnimating() {
+        return (getAnimation() != null);
     }
 
     public void setApps(ArrayList<ApplicationInfo> list) {
         mAllAppsList.clear();
         addApps(list);
+        filterApps(mCurrentFilter);
     }
 
     public void addApps(ArrayList<ApplicationInfo> list) {
-//        Log.d(TAG, "addApps: " + list.size() + " apps: " + list.toString());
-
         final int N = list.size();
 
         for (int i=0; i<N; i++) {
@@ -274,11 +275,12 @@
             }
             mAllAppsList.add(index, item);
         }
-        mAppsAdapter.notifyDataSetChanged();
+        filterApps(mCurrentFilter);
     }
 
     public void removeApps(ArrayList<ApplicationInfo> list) {
         final int N = list.size();
+
         for (int i=0; i<N; i++) {
             final ApplicationInfo item = list.get(i);
             int index = findAppByComponent(mAllAppsList, item);
@@ -289,7 +291,7 @@
                 // Try to recover.  This should keep us from crashing for now.
             }
         }
-        mAppsAdapter.notifyDataSetChanged();
+        filterApps(mCurrentFilter);
     }
 
     public void updateApps(ArrayList<ApplicationInfo> list) {
@@ -298,6 +300,33 @@
         addApps(list);
     }
 
+    public void filterApps(AppType appType) {
+        mCurrentFilter = appType;
+
+        mAppsAdapter.setNotifyOnChange(false);
+        mVisibleAppsList.clear();
+        if (appType == AppType.ALL) {
+            mVisibleAppsList.addAll(mAllAppsList);
+        } else {
+            int searchFlags = 0;
+
+            if (appType == AppType.APP) {
+                searchFlags = ApplicationInfo.APP_FLAG;
+            } else if (appType == AppType.GAME) {
+                searchFlags = ApplicationInfo.GAME_FLAG;
+            } else if (appType == AppType.DOWNLOADED) {
+                searchFlags = ApplicationInfo.DOWNLOADED_FLAG;
+            }
+
+            for (ApplicationInfo info : mAllAppsList) {
+                if ((info.flags & searchFlags) != 0) {
+                    mVisibleAppsList.add(info);
+                }
+            }
+        }
+        mAppsAdapter.notifyDataSetChanged();
+    }
+
     private static int findAppByComponent(ArrayList<ApplicationInfo> list, ApplicationInfo item) {
         ComponentName component = item.intent.getComponent();
         final int N = list.size();
diff --git a/src/com/android/launcher2/AllApps3D.java b/src/com/android/launcher2/AllApps3D.java
index 308ad28..d06624d 100644
--- a/src/com/android/launcher2/AllApps3D.java
+++ b/src/com/android/launcher2/AllApps3D.java
@@ -16,6 +16,10 @@
 
 package com.android.launcher2;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Resources;
@@ -29,12 +33,10 @@
 import android.renderscript.ProgramStore;
 import android.renderscript.ProgramVertex;
 import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScriptGL;
 import android.renderscript.RenderScript;
+import android.renderscript.RenderScriptGL;
 import android.renderscript.Sampler;
-import android.renderscript.Script;
-import android.renderscript.ScriptC;
-import android.renderscript.SimpleMesh;
+import android.renderscript.Mesh;
 import android.renderscript.Type;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
@@ -48,10 +50,6 @@
 import android.view.ViewConfiguration;
 import android.view.accessibility.AccessibilityEvent;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-
 import com.android.launcher.R;
 
 public class AllApps3D extends RSSurfaceView
@@ -131,27 +129,16 @@
     private boolean mSurrendered;
 
     private int mRestoreFocusIndex = -1;
-    
+
     @SuppressWarnings({"UnusedDeclaration"})
     static class Defines {
-        public static final int ALLOC_PARAMS = 0;
-        public static final int ALLOC_STATE = 1;
-        public static final int ALLOC_ICON_IDS = 3;
-        public static final int ALLOC_LABEL_IDS = 4;
-        public static final int ALLOC_VP_CONSTANTS = 5;
-
         public static final int COLUMNS_PER_PAGE_PORTRAIT = 4;
         public static final int ROWS_PER_PAGE_PORTRAIT = 4;
 
         public static final int COLUMNS_PER_PAGE_LANDSCAPE = 6;
         public static final int ROWS_PER_PAGE_LANDSCAPE = 3;
 
-        public static final int ICON_WIDTH_PX = 64;
-        public static final int ICON_TEXTURE_WIDTH_PX = 74;
         public static final int SELECTION_TEXTURE_WIDTH_PX = 74 + 20;
-
-        public static final int ICON_HEIGHT_PX = 64;
-        public static final int ICON_TEXTURE_HEIGHT_PX = 74;
         public static final int SELECTION_TEXTURE_HEIGHT_PX = 74 + 20;
     }
 
@@ -159,7 +146,6 @@
         super(context, attrs);
         setFocusable(true);
         setSoundEffectsEnabled(false);
-        getHolder().setFormat(PixelFormat.TRANSLUCENT);
         final ViewConfiguration config = ViewConfiguration.get(context);
         mSlop = config.getScaledTouchSlop();
         mMaxFlingVelocity = config.getScaledMaximumFlingVelocity();
@@ -274,24 +260,25 @@
         sRollo.dirtyCheck();
         sRollo.resize(w, h);
 
+        Log.d(TAG, "sc " + sRS);
         if (sRS != null) {
             sRS.mMessageCallback = mMessageProc = new AAMessage();
         }
 
         if (sRollo.mUniformAlloc != null) {
-            float tf[] = new float[] {72.f, 72.f,
-                                      120.f, 120.f, 0.f, 0.f,
-                                      120.f, 680.f,
-                                      (2.f / 480.f), 0, -((float)w / 2) - 0.25f, -380.25f};
+            ScriptField_VpConsts.Item i = new ScriptField_VpConsts.Item();
+            i.ScaleOffset.x = (2.f / 480.f);
+            i.ScaleOffset.y = 0;
+            i.ScaleOffset.z = -((float)w / 2) - 0.25f;
+            i.ScaleOffset.w = -380.25f;
+            i.BendPos.x = 120.f;
+            i.BendPos.y = 680.f;
             if (w > h) {
-                tf[6] = 40.f;
-                tf[7] = h - 40.f;
-                tf[9] = 1.f;
-                tf[10] = -((float)w / 2) - 0.25f;
-                tf[11] = -((float)h / 2) - 0.25f;
+                i.ScaleOffset.z = 40.f;
+                i.ScaleOffset.w = h - 40.f;
+                i.BendPos.y = 1.f;
             }
-
-            sRollo.mUniformAlloc.data(tf);
+            sRollo.mUniformAlloc.set(i, 0, true);
         }
 
         //long endTime = SystemClock.uptimeMillis();
@@ -307,18 +294,17 @@
         if (mArrowNavigation) {
             if (!hasWindowFocus) {
                 // Clear selection when we lose window focus
-                mLastSelectedIcon = sRollo.mState.selectedIconIndex;
+                mLastSelectedIcon = sRollo.mScript.get_gSelectedIconIndex();
                 sRollo.setHomeSelected(SELECTED_NONE);
                 sRollo.clearSelectedIcon();
-                sRollo.mState.save();
             } else {
-                if (sRollo.mState.iconCount > 0) {
+                if (sRollo.mScript.get_gIconCount() > 0) {
                     if (mLastSelection == SELECTION_ICONS) {
                         int selection = mLastSelectedIcon;
                         final int firstIcon = Math.round(sRollo.mScrollPos) * mColumnsPerPage;
                         if (selection < 0 || // No selection
                                 selection < firstIcon || // off the top of the screen
-                                selection >= sRollo.mState.iconCount || // past last icon
+                                selection >= sRollo.mScript.get_gIconCount() || // past last icon
                                 selection >= firstIcon + // past last icon on screen
                                     (mColumnsPerPage * mRowsPerPage)) {
                             selection = firstIcon;
@@ -326,10 +312,8 @@
 
                         // Select the first icon when we gain window focus
                         sRollo.selectIcon(selection, SELECTED_FOCUSED);
-                        sRollo.mState.save();
                     } else if (mLastSelection == SELECTION_HOME) {
                         sRollo.setHomeSelected(SELECTED_FOCUSED);
-                        sRollo.mState.save();
                     }
                 }
             }
@@ -356,7 +340,6 @@
                     // Clear selection when we lose focus
                     sRollo.clearSelectedIcon();
                     sRollo.setHomeSelected(SELECTED_NONE);
-                    sRollo.mState.save();
                     mArrowNavigation = false;
                 }
             } else {
@@ -366,11 +349,10 @@
     }
 
     private void gainFocus() {
-        if (!mArrowNavigation && sRollo.mState.iconCount > 0) {
+        if (!mArrowNavigation && sRollo.mScript.get_gIconCount() > 0) {
             // Select the first icon when we gain keyboard focus
             mArrowNavigation = true;
             sRollo.selectIcon(Math.round(sRollo.mScrollPos) * mColumnsPerPage, SELECTED_FOCUSED);
-            sRollo.mState.save();
         }
     }
 
@@ -382,7 +364,7 @@
         if (!isVisible()) {
             return false;
         }
-        final int iconCount = sRollo.mState.iconCount;
+        final int iconCount = sRollo.mScript.get_gIconCount();
 
         if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER) {
             if (mArrowNavigation) {
@@ -390,7 +372,7 @@
                     reallyPlaySoundEffect(SoundEffectConstants.CLICK);
                     mLauncher.closeAllApps(true);
                 } else {
-                    int whichApp = sRollo.mState.selectedIconIndex;
+                    int whichApp = sRollo.mScript.get_gSelectedIconIndex();
                     if (whichApp >= 0) {
                         ApplicationInfo app = mAllAppsList.get(whichApp);
                         mLauncher.startActivitySafely(app.intent, app);
@@ -402,10 +384,10 @@
 
         if (iconCount > 0) {
             final boolean isPortrait = getWidth() < getHeight();
-            
+
             mArrowNavigation = true;
 
-            int currentSelection = sRollo.mState.selectedIconIndex;
+            int currentSelection = sRollo.mScript.get_gSelectedIconIndex();
             int currentTopRow = Math.round(sRollo.mScrollPos);
 
             // The column of the current selection, in the range 0..COLUMNS_PER_PAGE_PORTRAIT-1
@@ -511,7 +493,6 @@
             }
             if (newSelection != currentSelection) {
                 sRollo.selectIcon(newSelection, SELECTED_FOCUSED);
-                sRollo.mState.save();
             }
         }
         return handled;
@@ -611,7 +592,6 @@
                     (!isPortrait && x > mTouchXBorders[mTouchXBorders.length-1])) {
                 mTouchTracking = TRACKING_HOME;
                 sRollo.setHomeSelected(SELECTED_PRESSED);
-                sRollo.mState.save();
                 mCurrentIconIndex = -1;
             } else {
                 mTouchTracking = TRACKING_FLING;
@@ -619,9 +599,6 @@
                 mMotionDownRawX = (int)ev.getRawX();
                 mMotionDownRawY = (int)ev.getRawY();
 
-                sRollo.mState.newPositionX = ev.getRawY() / getHeight();
-                sRollo.mState.newTouchDown = 1;
-
                 if (!sRollo.checkClickOK()) {
                     sRollo.clearSelectedIcon();
                 } else {
@@ -632,8 +609,7 @@
                         cancelLongPress();
                     }
                 }
-                sRollo.mState.save();
-                sRollo.move();
+                sRollo.move(ev.getRawY() / getHeight());
                 mVelocityTracker = VelocityTracker.obtain();
                 mVelocityTracker.addMovement(ev);
                 mStartedScrolling = false;
@@ -646,7 +622,6 @@
                         y > mTouchYBorders[mTouchYBorders.length-1]) || (!isPortrait
                         && x > mTouchXBorders[mTouchXBorders.length-1])
                         ? SELECTED_PRESSED : SELECTED_NONE);
-                sRollo.mState.save();
             } else if (mTouchTracking == TRACKING_FLING) {
                 int rawY = (int)ev.getRawY();
                 int slop;
@@ -667,14 +642,11 @@
                         cancelLongPress();
                         mCurrentIconIndex = -1;
                     }
-                    sRollo.mState.newPositionX = ev.getRawY() / getHeight();
-                    sRollo.mState.newTouchDown = 1;
-                    sRollo.move();
+                    sRollo.move(ev.getRawY() / getHeight());
 
                     mStartedScrolling = true;
                     sRollo.clearSelectedIcon();
                     mVelocityTracker.addMovement(ev);
-                    sRollo.mState.save();
                 }
             }
             break;
@@ -688,18 +660,13 @@
                         mLauncher.closeAllApps(true);
                     }
                     sRollo.setHomeSelected(SELECTED_NONE);
-                    sRollo.mState.save();
                 }
                 mCurrentIconIndex = -1;
             } else if (mTouchTracking == TRACKING_FLING) {
-                sRollo.mState.newTouchDown = 0;
-                sRollo.mState.newPositionX = ev.getRawY() / getHeight();
-
                 mVelocityTracker.computeCurrentVelocity(1000 /* px/sec */, mMaxFlingVelocity);
-                sRollo.mState.flingVelocity = mVelocityTracker.getYVelocity() / getHeight();
                 sRollo.clearSelectedIcon();
-                sRollo.mState.save();
-                sRollo.fling();
+                sRollo.fling(ev.getRawY() / getHeight(),
+                             mVelocityTracker.getYVelocity() / getHeight());
 
                 if (mVelocityTracker != null) {
                     mVelocityTracker.recycle();
@@ -766,7 +733,7 @@
             int pos = -1;
             switch (mLastSelection) {
             case SELECTION_ICONS:
-                index = sRollo.mState.selectedIconIndex;
+                index = sRollo.mScript.get_gSelectedIconIndex();
                 if (index >= 0) {
                     ApplicationInfo info = mAllAppsList.get(index);
                     if (info.title != null) {
@@ -825,8 +792,8 @@
         return sRollo != null && mZoom > 0.001f;
     }
 
-    public boolean isOpaque() {
-        return mZoom > 0.999f;
+    public boolean isAnimating() {
+        return isVisible() && mZoom <= 0.999f;
     }
 
     public void setApps(ArrayList<ApplicationInfo> list) {
@@ -858,13 +825,12 @@
         if (sRollo != null && reload) {
             sRollo.setApps(list);
         }
-        
+
         if (hasFocus() && mRestoreFocusIndex != -1) {
             sRollo.selectIcon(mRestoreFocusIndex, SELECTED_FOCUSED);
-            sRollo.mState.save();
             mRestoreFocusIndex = -1;
         }
-        
+
         mLocks &= ~LOCK_ICONS_PENDING;
     }
 
@@ -881,7 +847,7 @@
         final int N = list.size();
         if (sRollo != null) {
             sRollo.pause();
-            sRollo.reallocAppsList(sRollo.mState.iconCount + N);
+            sRollo.reallocAppsList(sRollo.mScript.get_gIconCount() + N);
         }
 
         for (int i=0; i<N; i++) {
@@ -951,17 +917,6 @@
         return -1;
     }
 
-    /*
-    private static int countPages(int iconCount) {
-        int iconsPerPage = getColumnsCount() * Defines.ROWS_PER_PAGE_PORTRAIT;
-        int pages = iconCount / iconsPerPage;
-        if (pages*iconsPerPage != iconCount) {
-            pages++;
-        }
-        return pages;
-    }
-    */
-
     class AAMessage extends RenderScript.RSMessage {
         public void run() {
             sRollo.mScrollPos = ((float)mData[0]) / (1 << 16);
@@ -993,23 +948,12 @@
         private int mHeight;
 
         private Resources mRes;
-        private Script mScript;
-        private Script.Invokable mInvokeMove;
-        private Script.Invokable mInvokeMoveTo;
-        private Script.Invokable mInvokeFling;
-        private Script.Invokable mInvokeResetWAR;
-        private Script.Invokable mInvokeSetZoom;
+        ScriptC_Allapps mScript;
 
-        private ProgramStore mPSIcons;
-        private ProgramFragment mPFTexMip;
-        private ProgramFragment mPFTexMipAlpha;
-        private ProgramFragment mPFTexNearest;
-        private ProgramVertex mPV;
-        private ProgramVertex mPVCurve;
-        private SimpleMesh mMesh;
+        private Mesh mMesh;
         private ProgramVertex.MatrixAllocation mPVA;
 
-        private Allocation mUniformAlloc;
+        private ScriptField_VpConsts mUniformAlloc;
 
         private Allocation mHomeButtonNormal;
         private Allocation mHomeButtonFocused;
@@ -1022,28 +966,15 @@
         private Allocation[] mLabels;
         private int[] mLabelIds;
         private Allocation mAllocLabelIds;
-        private Allocation mSelectedIcon;
 
         private Bitmap mSelectionBitmap;
         private Canvas mSelectionCanvas;
-        
-        private float mScrollPos;        
 
-        Params mParams;
-        State mState;
+        private float mScrollPos;
 
         AllApps3D mAllApps;
         boolean mInitialize;
 
-        class BaseAlloc {
-            Allocation mAlloc;
-            Type mType;
-
-            void save() {
-                mAlloc.data(this);
-            }
-        }
-
         private boolean checkClickOK() {
             return (Math.abs(mAllApps.mVelocity) < 0.4f) &&
                    (Math.abs(mScrollPos - Math.round(mScrollPos)) < 0.4f);
@@ -1061,41 +992,6 @@
             }
         }
 
-        class Params extends BaseAlloc {
-            Params() {
-                mType = Type.createFromClass(sRS, Params.class, 1, "ParamsClass");
-                mAlloc = Allocation.createTyped(sRS, mType);
-                save();
-            }
-            public int bubbleWidth;
-            public int bubbleHeight;
-            public int bubbleBitmapWidth;
-            public int bubbleBitmapHeight;
-
-            public int homeButtonWidth;
-            public int homeButtonHeight;
-            public int homeButtonTextureWidth;
-            public int homeButtonTextureHeight;
-        }
-
-        class State extends BaseAlloc {
-            public float newPositionX;
-            public int newTouchDown;
-            public float flingVelocity;
-            public int iconCount;
-            public int selectedIconIndex = -1;
-            public int selectedIconTexture;
-            public float zoomTarget;
-            public int homeButtonId;
-            public float targetPos;
-
-            State() {
-                mType = Type.createFromClass(sRS, State.class, 1, "StateClass");
-                mAlloc = Allocation.createTyped(sRS, mType);
-                save();
-            }
-        }
-
         public RolloRS(AllApps3D allApps) {
             mAllApps = allApps;
         }
@@ -1104,16 +1000,21 @@
             mRes = res;
             mWidth = width;
             mHeight = height;
+            mScript = new ScriptC_Allapps(sRS, mRes, R.raw.allapps, true);
+
             initProgramVertex();
             initProgramFragment();
             initProgramStore();
             initGl();
             initData();
-            initRs();
+
+            mScript.bind_gIconIDs(mAllocIconIds);
+            mScript.bind_gLabelIDs(mAllocLabelIds);
+            sRS.contextBindRootScript(mScript);
         }
 
         public void initMesh() {
-            SimpleMesh.TriangleMeshBuilder tm = new SimpleMesh.TriangleMeshBuilder(sRS, 2, 0);
+            Mesh.TriangleMeshBuilder tm = new Mesh.TriangleMeshBuilder(sRS, 2, 0);
 
             for (int ct=0; ct < 16; ct++) {
                 float pos = (1.f / (16.f - 1)) * ct;
@@ -1124,8 +1025,8 @@
                 tm.addTriangle(ct, ct+1, ct+2);
                 tm.addTriangle(ct+1, ct+3, ct+2);
             }
-            mMesh = tm.create();
-            mMesh.setName("SMCell");
+            mMesh = tm.create(true);
+            mScript.set_gSMCell(mMesh);
         }
 
         void resize(int w, int h) {
@@ -1140,18 +1041,12 @@
 
             ProgramVertex.Builder pvb = new ProgramVertex.Builder(sRS, null, null);
             pvb.setTextureMatrixEnable(true);
-            mPV = pvb.create();
-            mPV.setName("PV");
-            mPV.bindAllocation(mPVA);
+            ProgramVertex pv = pvb.create();
+            pv.bindAllocation(mPVA);
+            sRS.contextBindProgramVertex(pv);
 
-            Element.Builder eb = new Element.Builder(sRS);
-            eb.add(Element.createVector(sRS, Element.DataType.FLOAT_32, 2), "ImgSize");
-            eb.add(Element.createVector(sRS, Element.DataType.FLOAT_32, 4), "Position");
-            eb.add(Element.createVector(sRS, Element.DataType.FLOAT_32, 2), "BendPos");
-            eb.add(Element.createVector(sRS, Element.DataType.FLOAT_32, 4), "ScaleOffset");
-            Element e = eb.create();
-
-            mUniformAlloc = Allocation.createSized(sRS, e, 1);
+            mUniformAlloc = new ScriptField_VpConsts(sRS, 1);
+            mScript.bind_vpConstants(mUniformAlloc);
 
             initMesh();
             ProgramVertex.ShaderBuilder sb = new ProgramVertex.ShaderBuilder(sRS);
@@ -1213,13 +1108,12 @@
                     "}\n";
             sb.setShader(t);
             sb.addConstant(mUniformAlloc.getType());
-            sb.addInput(mMesh.getVertexType(0).getElement());
-            mPVCurve = sb.create();
-            mPVCurve.setName("PVCurve");
-            mPVCurve.bindAllocation(mPVA);
-            mPVCurve.bindConstants(mUniformAlloc, 1);
+            sb.addInput(mMesh.getVertexAllocation(0).getType().getElement());
+            ProgramVertex pvc = sb.create();
+            pvc.bindAllocation(mPVA);
+            pvc.bindConstants(mUniformAlloc.getAllocation(), 1);
 
-            sRS.contextBindProgramVertex(mPV);
+            mScript.set_gPVCurve(pvc);
         }
 
         private void initProgramFragment() {
@@ -1237,20 +1131,23 @@
             ProgramFragment.Builder bf = new ProgramFragment.Builder(sRS);
             bf.setTexture(ProgramFragment.Builder.EnvMode.MODULATE,
                           ProgramFragment.Builder.Format.RGBA, 0);
-            mPFTexMip = bf.create();
-            mPFTexMip.setName("PFTexMip");
-            mPFTexMip.bindSampler(linear, 0);
+            bf.setVaryingColor(true);
+            ProgramFragment pfTexMip = bf.create();
+            pfTexMip.bindSampler(linear, 0);
 
-            mPFTexNearest = bf.create();
-            mPFTexNearest.setName("PFTexNearest");
-            mPFTexNearest.bindSampler(nearest, 0);
+            bf.setVaryingColor(false);
+            ProgramFragment pfTexNearest = bf.create();
+            pfTexNearest.bindSampler(nearest, 0);
 
             bf.setTexture(ProgramFragment.Builder.EnvMode.MODULATE,
                           ProgramFragment.Builder.Format.ALPHA, 0);
-            mPFTexMipAlpha = bf.create();
-            mPFTexMipAlpha.setName("PFTexMipAlpha");
-            mPFTexMipAlpha.bindSampler(linear, 0);
+            bf.setVaryingColor(true);
+            ProgramFragment pfTexMipAlpha = bf.create();
+            pfTexMipAlpha.bindSampler(linear, 0);
 
+            mScript.set_gPFTexNearest(pfTexNearest);
+            mScript.set_gPFTexMip(pfTexMip);
+            mScript.set_gPFTexMipAlpha(pfTexMipAlpha);
         }
 
         private void initProgramStore() {
@@ -1260,23 +1157,17 @@
             bs.setDitherEnable(true);
             bs.setBlendFunc(ProgramStore.BlendSrcFunc.SRC_ALPHA,
                             ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA);
-            mPSIcons = bs.create();
-            mPSIcons.setName("PSIcons");
+            mScript.set_gPS(bs.create());
         }
 
         private void initGl() {
         }
 
         private void initData() {
-            mParams = new Params();
-            mState = new State();
-
-            final Utilities.BubbleText bubble = new Utilities.BubbleText(mAllApps.getContext());
-
-            mParams.bubbleWidth = bubble.getBubbleWidth();
-            mParams.bubbleHeight = bubble.getMaxBubbleHeight();
-            mParams.bubbleBitmapWidth = bubble.getBitmapWidth();
-            mParams.bubbleBitmapHeight = bubble.getBitmapHeight();
+            mScript.set_COLUMNS_PER_PAGE_PORTRAIT(Defines.COLUMNS_PER_PAGE_PORTRAIT);
+            mScript.set_ROWS_PER_PAGE_PORTRAIT(Defines.ROWS_PER_PAGE_PORTRAIT);
+            mScript.set_COLUMNS_PER_PAGE_LANDSCAPE(Defines.COLUMNS_PER_PAGE_LANDSCAPE);
+            mScript.set_ROWS_PER_PAGE_LANDSCAPE(Defines.ROWS_PER_PAGE_LANDSCAPE);
 
             mHomeButtonNormal = Allocation.createFromBitmapResource(sRS, mRes,
                     R.drawable.home_button_normal, Element.RGBA_8888(sRS), false);
@@ -1287,15 +1178,8 @@
             mHomeButtonPressed = Allocation.createFromBitmapResource(sRS, mRes,
                     R.drawable.home_button_pressed, Element.RGBA_8888(sRS), false);
             mHomeButtonPressed.uploadToTexture(0);
-            mParams.homeButtonWidth = 76;
-            mParams.homeButtonHeight = 68;
-            mParams.homeButtonTextureWidth = 128;
-            mParams.homeButtonTextureHeight = 128;
 
-            mState.homeButtonId = mHomeButtonNormal.getID();
-
-            mParams.save();
-            mState.save();
+            mScript.set_gHomeButton(mHomeButtonNormal);
 
             mSelectionBitmap = Bitmap.createBitmap(Defines.SELECTION_TEXTURE_WIDTH_PX,
                     Defines.SELECTION_TEXTURE_HEIGHT_PX, Bitmap.Config.ARGB_8888);
@@ -1304,30 +1188,6 @@
             setApps(null);
         }
 
-        private void initRs() {
-            ScriptC.Builder sb = new ScriptC.Builder(sRS);
-            sb.setScript(mRes, R.raw.allapps);
-            sb.setRoot(true);
-            sb.addDefines(mAllApps.mDefines);
-            sb.setType(mParams.mType, "params", Defines.ALLOC_PARAMS);
-            sb.setType(mState.mType, "state", Defines.ALLOC_STATE);
-            sb.setType(mUniformAlloc.getType(), "vpConstants", Defines.ALLOC_VP_CONSTANTS);
-            mInvokeMove = sb.addInvokable("move");
-            mInvokeFling = sb.addInvokable("fling");
-            mInvokeMoveTo = sb.addInvokable("moveTo");
-            mInvokeResetWAR = sb.addInvokable("resetHWWar");
-            mInvokeSetZoom = sb.addInvokable("setZoom");
-            mScript = sb.create();
-            mScript.setClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-            mScript.bindAllocation(mParams.mAlloc, Defines.ALLOC_PARAMS);
-            mScript.bindAllocation(mState.mAlloc, Defines.ALLOC_STATE);
-            mScript.bindAllocation(mAllocIconIds, Defines.ALLOC_ICON_IDS);
-            mScript.bindAllocation(mAllocLabelIds, Defines.ALLOC_LABEL_IDS);
-            mScript.bindAllocation(mUniformAlloc, Defines.ALLOC_VP_CONSTANTS);
-
-            sRS.contextBindRootScript(mScript);
-        }
-
         void dirtyCheck() {
             if (sZoomDirty) {
                 setZoom(mAllApps.sNextZoom, mAllApps.sAnimateNextZoom);
@@ -1345,20 +1205,21 @@
 
             mIcons = new Allocation[count];
             mIconIds = new int[allocCount];
-            mAllocIconIds = Allocation.createSized(sRS, Element.USER_I32(sRS), allocCount);
+            mAllocIconIds = Allocation.createSized(sRS, Element.I32(sRS), allocCount);
 
             mLabels = new Allocation[count];
             mLabelIds = new int[allocCount];
-            mAllocLabelIds = Allocation.createSized(sRS, Element.USER_I32(sRS), allocCount);
+            mAllocLabelIds = Allocation.createSized(sRS, Element.I32(sRS), allocCount);
 
-            mState.iconCount = count;
-            for (int i=0; i < mState.iconCount; i++) {
+            mScript.set_gIconCount(count);
+            for (int i=0; i < count; i++) {
                 createAppIconAllocations(i, list.get(i));
             }
-            for (int i=0; i < mState.iconCount; i++) {
+            for (int i=0; i < count; i++) {
                 uploadAppIcon(i, list.get(i));
             }
             saveAppsList();
+            android.util.Log.e("rs", "setApps");
             sRollo.resume();
         }
 
@@ -1367,15 +1228,7 @@
                 sRollo.clearSelectedIcon();
                 sRollo.setHomeSelected(SELECTED_NONE);
             }
-            if (zoom > 0.001f) {
-                sRollo.mState.zoomTarget = zoom;
-            } else {
-                sRollo.mState.zoomTarget = 0;
-            }
-            sRollo.mState.save();
-            if (!animate) {
-                sRollo.mInvokeSetZoom.execute();
-            }
+            sRollo.mScript.invoke_setZoom(zoom, animate ? 1 : 0);
         }
 
         private void createAppIconAllocations(int index, ApplicationInfo item) {
@@ -1405,13 +1258,13 @@
         private void reallocAppsList(int count) {
             Allocation[] icons = new Allocation[count];
             int[] iconIds = new int[count];
-            mAllocIconIds = Allocation.createSized(sRS, Element.USER_I32(sRS), count);
+            mAllocIconIds = Allocation.createSized(sRS, Element.I32(sRS), count);
 
             Allocation[] labels = new Allocation[count];
             int[] labelIds = new int[count];
-            mAllocLabelIds = Allocation.createSized(sRS, Element.USER_I32(sRS), count);
+            mAllocLabelIds = Allocation.createSized(sRS, Element.I32(sRS), count);
 
-            final int oldCount = sRollo.mState.iconCount;
+            final int oldCount = sRollo.mScript.get_gIconCount();
 
             System.arraycopy(mIcons, 0, icons, 0, oldCount);
             System.arraycopy(mIconIds, 0, iconIds, 0, oldCount);
@@ -1428,7 +1281,7 @@
          * Handle the allocations for the new app.  Make sure you call saveAppsList when done.
          */
         private void addApp(int index, ApplicationInfo item) {
-            final int count = mState.iconCount - index;
+            final int count = mScript.get_gIconCount() - index;
             final int dest = index + 1;
 
             System.arraycopy(mIcons, index, mIcons, dest, count);
@@ -1438,14 +1291,15 @@
 
             createAppIconAllocations(index, item);
             uploadAppIcon(index, item);
-            sRollo.mState.iconCount++;
+
+            mScript.set_gIconCount(mScript.get_gIconCount() + 1);
         }
 
         /**
          * Handle the allocations for the removed app.  Make sure you call saveAppsList when done.
          */
         private void removeApp(int index) {
-            final int count = mState.iconCount - index - 1;
+            final int count = mScript.get_gIconCount() - index - 1;
             final int src = index + 1;
 
             System.arraycopy(mIcons, src, mIcons, index, count);
@@ -1453,8 +1307,8 @@
             System.arraycopy(mLabels, src, mLabels, index, count);
             System.arraycopy(mLabelIds, src, mLabelIds, index, count);
 
-            sRollo.mState.iconCount--;
-            final int last = mState.iconCount;
+            mScript.set_gIconCount(mScript.get_gIconCount() - 1);
+            final int last = mScript.get_gIconCount();
 
             mIcons[last] = null;
             mIconIds[last] = 0;
@@ -1471,31 +1325,21 @@
                 mAllocIconIds.data(mIconIds);
                 mAllocLabelIds.data(mLabelIds);
 
-                mScript.bindAllocation(mAllocIconIds, Defines.ALLOC_ICON_IDS);
-                mScript.bindAllocation(mAllocLabelIds, Defines.ALLOC_LABEL_IDS);
-
-                mState.save();
-
-                // Note: mScript may be null if we haven't initialized it yet.
-                // In that case, this is a no-op.
-                if (mInvokeResetWAR != null) {
-                    mInvokeResetWAR.execute();
-                }
+                mScript.bind_gIconIDs(mAllocIconIds);
+                mScript.bind_gLabelIDs(mAllocLabelIds);
             }
         }
 
-        void fling() {
-            mInvokeFling.execute();
+        void fling(float pos, float v) {
+            mScript.invoke_fling(pos, v);
         }
 
-        void move() {
-            mInvokeMove.execute();
+        void move(float pos) {
+            mScript.invoke_move(pos);
         }
 
         void moveTo(float row) {
-            mState.targetPos = row;
-            mState.save();
-            mInvokeMoveTo.execute();
+            mScript.invoke_moveTo(row);
         }
 
         /**
@@ -1525,7 +1369,7 @@
                 if (mAllApps != null) {
                     mAllApps.mRestoreFocusIndex = index;
                 }
-                mState.selectedIconIndex = -1;
+                mScript.set_gSelectedIconIndex(-1);
                 if (mAllApps.mLastSelection == SELECTION_ICONS) {
                     mAllApps.mLastSelection = SELECTION_NONE;
                 }
@@ -1534,8 +1378,8 @@
                     mAllApps.mLastSelection = SELECTION_ICONS;
                 }
 
-                int prev = mState.selectedIconIndex;
-                mState.selectedIconIndex = index;
+                int prev = mScript.get_gSelectedIconIndex();
+                mScript.set_gSelectedIconIndex(index);
 
                 ApplicationInfo info = appsList.get(index);
                 Bitmap selectionBitmap = mSelectionBitmap;
@@ -1544,10 +1388,10 @@
                         selectionBitmap.getWidth(), selectionBitmap.getHeight(),
                         pressed == SELECTED_PRESSED, info.iconBitmap);
 
-                mSelectedIcon = Allocation.createFromBitmap(sRS, selectionBitmap,
+                Allocation si = Allocation.createFromBitmap(sRS, selectionBitmap,
                         Element.RGBA_8888(sRS), false);
-                mSelectedIcon.uploadToTexture(0);
-                mState.selectedIconTexture = mSelectedIcon.getID();
+                si.uploadToTexture(0);
+                mScript.set_gSelectedIconTexture(si);
 
                 if (prev != index) {
                     if (info.title != null && info.title.length() > 0) {
@@ -1562,24 +1406,24 @@
          * You need to call save() on mState on your own after calling this.
          */
         void clearSelectedIcon() {
-            mState.selectedIconIndex = -1;
+            mScript.set_gSelectedIconIndex(-1);
         }
 
         void setHomeSelected(int mode) {
             final int prev = mAllApps.mLastSelection;
             switch (mode) {
             case SELECTED_NONE:
-                mState.homeButtonId = mHomeButtonNormal.getID();
+                mScript.set_gHomeButton(mHomeButtonNormal);
                 break;
             case SELECTED_FOCUSED:
                 mAllApps.mLastSelection = SELECTION_HOME;
-                mState.homeButtonId = mHomeButtonFocused.getID();
+                mScript.set_gHomeButton(mHomeButtonFocused);
                 if (prev != SELECTION_HOME) {
                     mAllApps.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
                 }
                 break;
             case SELECTED_PRESSED:
-                mState.homeButtonId = mHomeButtonPressed.getID();
+                mScript.set_gHomeButton(mHomeButtonPressed);
                 break;
             }
         }
@@ -1599,23 +1443,15 @@
                 Log.d(TAG, "sRollo.mLabelIds.length=" + mLabelIds.length);
             }
             Log.d(TAG, "sRollo.mLabelIds=" +  Arrays.toString(mLabelIds));
-            Log.d(TAG, "sRollo.mState.newPositionX=" + mState.newPositionX);
-            Log.d(TAG, "sRollo.mState.newTouchDown=" + mState.newTouchDown);
-            Log.d(TAG, "sRollo.mState.flingVelocity=" + mState.flingVelocity);
-            Log.d(TAG, "sRollo.mState.iconCount=" + mState.iconCount);
-            Log.d(TAG, "sRollo.mState.selectedIconIndex=" + mState.selectedIconIndex);
-            Log.d(TAG, "sRollo.mState.selectedIconTexture=" + mState.selectedIconTexture);
-            Log.d(TAG, "sRollo.mState.zoomTarget=" + mState.zoomTarget);
-            Log.d(TAG, "sRollo.mState.homeButtonId=" + mState.homeButtonId);
-            Log.d(TAG, "sRollo.mState.targetPos=" + mState.targetPos);
-            Log.d(TAG, "sRollo.mParams.bubbleWidth=" + mParams.bubbleWidth);
-            Log.d(TAG, "sRollo.mParams.bubbleHeight=" + mParams.bubbleHeight);
-            Log.d(TAG, "sRollo.mParams.bubbleBitmapWidth=" + mParams.bubbleBitmapWidth);
-            Log.d(TAG, "sRollo.mParams.bubbleBitmapHeight=" + mParams.bubbleBitmapHeight);
-            Log.d(TAG, "sRollo.mParams.homeButtonWidth=" + mParams.homeButtonWidth);
-            Log.d(TAG, "sRollo.mParams.homeButtonHeight=" + mParams.homeButtonHeight);
-            Log.d(TAG, "sRollo.mParams.homeButtonTextureWidth=" + mParams.homeButtonTextureWidth);
-            Log.d(TAG, "sRollo.mParams.homeButtonTextureHeight=" + mParams.homeButtonTextureHeight);
+            //Log.d(TAG, "sRollo.mState.newPositionX=" + mState.newPositionX);
+            //Log.d(TAG, "sRollo.mState.newTouchDown=" + mState.newTouchDown);
+            //Log.d(TAG, "sRollo.mState.flingVelocity=" + mState.flingVelocity);
+            //Log.d(TAG, "sRollo.mState.iconCount=" + mState.iconCount);
+            //Log.d(TAG, "sRollo.mState.selectedIconIndex=" + mState.selectedIconIndex);
+            //Log.d(TAG, "sRollo.mState.selectedIconTexture=" + mState.selectedIconTexture);
+            //Log.d(TAG, "sRollo.mState.zoomTarget=" + mState.zoomTarget);
+            //Log.d(TAG, "sRollo.mState.homeButtonId=" + mState.homeButtonId);
+            //Log.d(TAG, "sRollo.mState.targetPos=" + mState.targetPos);
         }
     }
 
@@ -1646,5 +1482,3 @@
         }
     }
 }
-
-
diff --git a/src/com/android/launcher2/AllAppsList.java b/src/com/android/launcher2/AllAppsList.java
index 3a5baea..4c9bc5e 100644
--- a/src/com/android/launcher2/AllAppsList.java
+++ b/src/com/android/launcher2/AllAppsList.java
@@ -16,17 +16,16 @@
 
 package com.android.launcher2;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import android.content.ComponentName;
-import android.content.Intent;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
 
 /**
  * Stores the list of all applications for the all apps view.
@@ -92,7 +91,7 @@
 
         if (matches.size() > 0) {
             for (ResolveInfo info : matches) {
-                add(new ApplicationInfo(info, mIconCache));
+                add(new ApplicationInfo(context.getPackageManager(), info, mIconCache));
             }
         }
     }
@@ -143,7 +142,7 @@
                         info.activityInfo.applicationInfo.packageName,
                         info.activityInfo.name);
                 if (applicationInfo == null) {
-                    add(new ApplicationInfo(info, mIconCache));
+                    add(new ApplicationInfo(context.getPackageManager(), info, mIconCache));
                 } else {
                     mIconCache.remove(applicationInfo.componentName);
                     mIconCache.getTitleAndIcon(applicationInfo, info);
diff --git a/src/com/android/launcher2/AllAppsPagedView.java b/src/com/android/launcher2/AllAppsPagedView.java
new file mode 100644
index 0000000..a673304
--- /dev/null
+++ b/src/com/android/launcher2/AllAppsPagedView.java
@@ -0,0 +1,344 @@
+/*
+ * 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.
+ */
+
+package com.android.launcher2;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.widget.TextView;
+
+import com.android.launcher.R;
+
+/**
+ * An implementation of PagedView that populates the pages of the workspace
+ * with all of the user's applications.
+ */
+public class AllAppsPagedView extends PagedView
+        implements AllAppsView, View.OnClickListener, View.OnLongClickListener, DragSource {
+
+    private static final String TAG = "AllAppsPagedView";
+    private static final boolean DEBUG = false;
+
+    private Launcher mLauncher;
+    private DragController mDragController;
+
+    // preserve compatibility with 3D all apps:
+    //    0.0 -> hidden
+    //    1.0 -> shown and opaque
+    //    intermediate values -> partially shown & partially opaque
+    private float mZoom;
+
+    // set of all applications
+    private ArrayList<ApplicationInfo> mApps;
+    private ArrayList<ApplicationInfo> mFilteredApps;
+
+    // the types of applications to filter
+    static final int ALL_APPS_FLAG = -1;
+    private int mAppFilter = ALL_APPS_FLAG;
+
+    private int mCellCountX;
+    private int mCellCountY;
+
+    private final LayoutInflater mInflater;
+
+    public AllAppsPagedView(Context context) {
+        this(context, null);
+    }
+
+    public AllAppsPagedView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public AllAppsPagedView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PagedView, defStyle, 0);
+        mCellCountX = a.getInt(R.styleable.PagedView_cellCountX, 6);
+        mCellCountY = a.getInt(R.styleable.PagedView_cellCountY, 4);
+        mInflater = LayoutInflater.from(context);
+        a.recycle();
+        setSoundEffectsEnabled(false);
+    }
+
+    @Override
+    public void setLauncher(Launcher launcher) {
+        mLauncher = launcher;
+    }
+
+    @Override
+    public void setDragController(DragController dragger) {
+        mDragController = dragger;
+    }
+
+    public void setAppFilter(int filterType) {
+        mAppFilter = filterType;
+        if (mApps != null) {
+            mFilteredApps = rebuildFilteredApps(mApps);
+            setCurrentPage(0);
+            invalidatePageData();
+        }
+    }
+
+    @Override
+    public void zoom(float zoom, boolean animate) {
+        mZoom = zoom;
+        cancelLongPress();
+
+        if (isVisible()) {
+            getParent().bringChildToFront(this);
+            setVisibility(View.VISIBLE);
+            if (animate) {
+                startAnimation(AnimationUtils.loadAnimation(getContext(),
+                        R.anim.all_apps_2d_fade_in));
+            } else {
+                onAnimationEnd();
+            }
+        } else {
+            if (animate) {
+                startAnimation(AnimationUtils.loadAnimation(getContext(),
+                        R.anim.all_apps_2d_fade_out));
+            } else {
+                onAnimationEnd();
+            }
+        }
+    }
+
+    protected void onAnimationEnd() {
+        if (!isVisible()) {
+            setVisibility(View.GONE);
+            mZoom = 0.0f;
+        } else {
+            mZoom = 1.0f;
+        }
+
+        if (mLauncher != null)
+            mLauncher.zoomed(mZoom);
+    }
+
+    private int getChildIndexForGrandChild(View v) {
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; ++i) {
+            PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(i);
+            if (layout.indexOfChild(v) > -1) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    @Override
+    public void onClick(View v) {
+        int childIndex = getChildIndexForGrandChild(v);
+        if (childIndex == getCurrentPage()) {
+            final ApplicationInfo app = (ApplicationInfo) v.getTag();
+
+            // animate some feedback to the click
+            animateClickFeedback(v, new Runnable() {
+                @Override
+                public void run() {
+                    mLauncher.startActivitySafely(app.intent, app);
+                }
+            });
+        }
+    }
+
+    @Override
+    public boolean onLongClick(View v) {
+        if (!v.isInTouchMode()) {
+            return false;
+        }
+
+        ApplicationInfo app = (ApplicationInfo) v.getTag();
+        app = new ApplicationInfo(app);
+
+        mDragController.startDrag(v, this, app, DragController.DRAG_ACTION_COPY);
+        return true;
+    }
+
+    @Override
+    public void onDropCompleted(View target, boolean success) {
+        // do nothing
+    }
+
+    @Override
+    public boolean isVisible() {
+        return mZoom > 0.001f;
+    }
+
+    @Override
+    public boolean isAnimating() {
+        return (getAnimation() != null);
+    }
+
+    private ArrayList<ApplicationInfo> rebuildFilteredApps(ArrayList<ApplicationInfo> apps) {
+        ArrayList<ApplicationInfo> filteredApps = new ArrayList<ApplicationInfo>();
+        if (mAppFilter == ALL_APPS_FLAG) {
+            return apps;
+        } else {
+            final int length = apps.size();
+            for (int i = 0; i < length; ++i) {
+                ApplicationInfo info = apps.get(i);
+                if ((info.flags & mAppFilter) > 0) {
+                    filteredApps.add(info);
+                }
+            }
+        }
+        return filteredApps;
+    }
+
+    @Override
+    public void setApps(ArrayList<ApplicationInfo> list) {
+        mApps = list;
+        Collections.sort(mApps, LauncherModel.APP_NAME_COMPARATOR);
+        mFilteredApps = rebuildFilteredApps(mApps);
+        mPageViewIconCache.clear();
+        invalidatePageData();
+    }
+
+    private void addAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) {
+        // we add it in place, in alphabetical order
+        final int count = list.size();
+        for (int i = 0; i < count; ++i) {
+            final ApplicationInfo info = list.get(i);
+            final int index = Collections.binarySearch(mApps, info, LauncherModel.APP_NAME_COMPARATOR);
+            if (index < 0) {
+                mApps.add(-(index + 1), info);
+            }
+        }
+        mFilteredApps = rebuildFilteredApps(mApps);
+    }
+    @Override
+    public void addApps(ArrayList<ApplicationInfo> list) {
+        addAppsWithoutInvalidate(list);
+        invalidatePageData();
+    }
+
+    private void removeAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) {
+        // loop through all the apps and remove apps that have the same component
+        final int length = list.size();
+        for (int i = 0; i < length; ++i) {
+            final ApplicationInfo info = list.get(i);
+            int removeIndex = findAppByComponent(mApps, info);
+            if (removeIndex > -1) {
+                mApps.remove(removeIndex);
+                mPageViewIconCache.removeOutline(info);
+            }
+        }
+        mFilteredApps = rebuildFilteredApps(mApps);
+    }
+    @Override
+    public void removeApps(ArrayList<ApplicationInfo> list) {
+        removeAppsWithoutInvalidate(list);
+        invalidatePageData();
+    }
+
+    @Override
+    public void updateApps(ArrayList<ApplicationInfo> list) {
+        removeAppsWithoutInvalidate(list);
+        addAppsWithoutInvalidate(list);
+        invalidatePageData();
+    }
+
+    private int findAppByComponent(ArrayList<ApplicationInfo> list, ApplicationInfo item) {
+        ComponentName removeComponent = item.intent.getComponent();
+        final int length = list.size();
+        for (int i = 0; i < length; ++i) {
+            ApplicationInfo info = list.get(i);
+            if (info.intent.getComponent().equals(removeComponent)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    @Override
+    public void dumpState() {
+        ApplicationInfo.dumpApplicationInfoList(TAG, "mApps", mApps);
+    }
+
+    @Override
+    public void surrender() {
+        // do nothing?
+    }
+
+    @Override
+    public void syncPages() {
+        // ensure that we have the right number of pages
+        int numPages = (int) Math.ceil((float) mFilteredApps.size() / (mCellCountX * mCellCountY));
+        int curNumPages = getChildCount();
+        // remove any extra pages after the "last" page
+        int extraPageDiff = curNumPages - numPages;
+        for (int i = 0; i < extraPageDiff; ++i) {
+            removeViewAt(numPages);
+        }
+        // add any necessary pages
+        for (int i = curNumPages; i < numPages; ++i) {
+            PagedViewCellLayout layout = new PagedViewCellLayout(getContext());
+            layout.setCellCount(mCellCountX, mCellCountY);
+            addView(layout);
+        }
+
+        // bound the current page
+        setCurrentPage(Math.max(0, Math.min(numPages - 1, getCurrentPage())));
+    }
+
+    @Override
+    public void syncPageItems(int page) {
+        // ensure that we have the right number of items on the pages
+        final int cellsPerPage = mCellCountX * mCellCountY;
+        final int startIndex = page * cellsPerPage;
+        final int endIndex = Math.min(startIndex + cellsPerPage, mFilteredApps.size());
+        PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page);
+
+        final int curNumPageItems = layout.getChildCount();
+        final int numPageItems = endIndex - startIndex;
+
+        // remove any extra items
+        int extraPageItemsDiff = curNumPageItems - numPageItems;
+        for (int i = 0; i < extraPageItemsDiff; ++i) {
+            layout.removeViewAt(numPageItems);
+        }
+        // add any necessary items
+        for (int i = curNumPageItems; i < numPageItems; ++i) {
+            TextView text = (TextView) mInflater.inflate(R.layout.all_apps_paged_view_application, layout, false);
+            text.setOnClickListener(this);
+            text.setOnLongClickListener(this);
+
+            layout.addViewToCellLayout(text, -1, i,
+                new PagedViewCellLayout.LayoutParams(0, 0, 1, 1));
+        }
+
+        // actually reapply to the existing text views
+        for (int i = startIndex; i < endIndex; ++i) {
+            final int index = i - startIndex;
+            final ApplicationInfo info = mFilteredApps.get(i);
+            PagedViewIcon icon = (PagedViewIcon) layout.getChildAt(index);
+            icon.applyFromApplicationInfo(info, mPageViewIconCache);
+
+            PagedViewCellLayout.LayoutParams params = 
+                (PagedViewCellLayout.LayoutParams) icon.getLayoutParams();
+            params.cellX = index % mCellCountX;
+            params.cellY = index / mCellCountX;
+        }
+    }
+}
diff --git a/src/com/android/launcher2/AllAppsTabbed.java b/src/com/android/launcher2/AllAppsTabbed.java
new file mode 100644
index 0000000..5df580c
--- /dev/null
+++ b/src/com/android/launcher2/AllAppsTabbed.java
@@ -0,0 +1,201 @@
+/*
+ * 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.
+ */
+
+package com.android.launcher2;
+
+import java.util.ArrayList;
+
+import android.animation.Animatable;
+import android.animation.AnimatableListenerAdapter;
+import android.animation.Animator;
+import android.animation.PropertyAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.RelativeLayout;
+import android.widget.TabHost;
+import android.widget.TabWidget;
+import android.widget.TextView;
+
+import com.android.launcher.R;
+
+/**
+ * Implements a tabbed version of AllApps2D.
+ */
+public class AllAppsTabbed extends TabHost implements AllAppsView {
+
+    private static final String TAG = "Launcher.AllAppsTabbed";
+
+    private static final String TAG_ALL = "ALL";
+    private static final String TAG_APPS = "APPS";
+    private static final String TAG_GAMES = "GAMES";
+    private static final String TAG_DOWNLOADED = "DOWNLOADED";
+
+    private AllAppsPagedView mAllApps;
+    private Context mContext;
+
+    public AllAppsTabbed(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mContext = context;
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        // setup the tab host
+        setup();
+
+        try {
+            mAllApps = (AllAppsPagedView) findViewById(R.id.all_apps_paged_view);
+            if (mAllApps == null) throw new Resources.NotFoundException();
+        } catch (Resources.NotFoundException e) {
+            Log.e(TAG, "Can't find necessary layout elements for AllAppsTabbed");
+        }
+
+        // share the same AllApps workspace across all the tabs
+        TabContentFactory contentFactory = new TabContentFactory() {
+            public View createTabContent(String tag) {
+                return mAllApps;
+            }
+        };
+
+        String label = mContext.getString(R.string.all_apps_tab_all);
+        addTab(newTabSpec(TAG_ALL).setIndicator(label).setContent(contentFactory));
+
+        label = mContext.getString(R.string.all_apps_tab_apps);
+        addTab(newTabSpec(TAG_APPS).setIndicator(label).setContent(contentFactory));
+
+        label = mContext.getString(R.string.all_apps_tab_games);
+        addTab(newTabSpec(TAG_GAMES).setIndicator(label).setContent(contentFactory));
+
+        label = mContext.getString(R.string.all_apps_tab_downloaded);
+        addTab(newTabSpec(TAG_DOWNLOADED).setIndicator(label).setContent(contentFactory));
+
+        // TEMP: just styling the tab widget to be a bit nicer until we get the actual
+        // new assets
+        TabWidget tabWidget = getTabWidget();
+        for (int i = 0; i < tabWidget.getChildCount(); ++i) {
+            RelativeLayout tab = (RelativeLayout) tabWidget.getChildTabViewAt(i);
+            TextView text = (TextView) tab.getChildAt(1);
+            text.setTextSize(20.0f);
+            text.setPadding(20, 0, 20, 0);
+            text.setShadowLayer(1.0f, 0.0f, 1.0f, Color.BLACK);
+            tab.setBackgroundDrawable(null);
+        }
+
+        setOnTabChangedListener(new OnTabChangeListener() {
+            public void onTabChanged(String tabId) {
+                // animate the changing of the tab content by fading pages in and out
+                final int duration = 150;
+                final float alpha = mAllApps.getAlpha();
+                Animator alphaAnim = new PropertyAnimator(duration, mAllApps, "alpha", alpha, 0.0f);
+                alphaAnim.addListener(new AnimatableListenerAdapter() {
+                    public void onAnimationEnd(Animatable animation) {
+                        String tag = getCurrentTabTag();
+                        if (tag == TAG_ALL) {
+                            mAllApps.setAppFilter(AllAppsPagedView.ALL_APPS_FLAG);
+                        } else if (tag == TAG_APPS) {
+                            mAllApps.setAppFilter(ApplicationInfo.APP_FLAG);
+                        } else if (tag == TAG_GAMES) {
+                            mAllApps.setAppFilter(ApplicationInfo.GAME_FLAG);
+                        } else if (tag == TAG_DOWNLOADED) {
+                            mAllApps.setAppFilter(ApplicationInfo.DOWNLOADED_FLAG);
+                        }
+
+                        final float alpha = mAllApps.getAlpha();
+                        Animator alphaAnim = 
+                            new PropertyAnimator(duration, mAllApps, "alpha", alpha, 1.0f);
+                        alphaAnim.start();
+                    }
+                });
+                alphaAnim.start();
+            }
+        });
+
+        setCurrentTab(0);
+
+        // It needs to be INVISIBLE so that it will be measured in the layout.
+        // Otherwise the animations is messed up when we show it for the first time.
+        setVisibility(INVISIBLE);
+    }
+
+    @Override
+    public void setLauncher(Launcher launcher) {
+        mAllApps.setLauncher(launcher);
+    }
+
+    @Override
+    public void setDragController(DragController dragger) {
+        mAllApps.setDragController(dragger);
+    }
+
+    @Override
+    public void zoom(float zoom, boolean animate) {
+        // NOTE: animate parameter is ignored for the TabHost itself
+        setVisibility((zoom == 0.0f) ? View.GONE : View.VISIBLE);
+        mAllApps.zoom(zoom, animate);
+    }
+
+    @Override
+    public void setVisibility(int visibility) {
+        final boolean isVisible = (visibility == View.VISIBLE); 
+        super.setVisibility(visibility);
+        float zoom = (isVisible ? 1.0f : 0.0f);
+        mAllApps.zoom(zoom, false);
+    }
+
+    @Override
+    public boolean isVisible() {
+        return mAllApps.isVisible();
+    }
+
+    @Override
+    public boolean isAnimating() {
+        return (getAnimation() != null);
+    }
+
+    @Override
+    public void setApps(ArrayList<ApplicationInfo> list) {
+        mAllApps.setApps(list);
+    }
+
+    @Override
+    public void addApps(ArrayList<ApplicationInfo> list) {
+        mAllApps.addApps(list);
+    }
+
+    @Override
+    public void removeApps(ArrayList<ApplicationInfo> list) {
+        mAllApps.removeApps(list);
+    }
+
+    @Override
+    public void updateApps(ArrayList<ApplicationInfo> list) {
+        mAllApps.updateApps(list);
+    }
+
+    @Override
+    public void dumpState() {
+        mAllApps.dumpState();
+    }
+
+    @Override
+    public void surrender() {
+        mAllApps.surrender();
+    }
+}
diff --git a/src/com/android/launcher2/AllAppsView.java b/src/com/android/launcher2/AllAppsView.java
index 877c075..007ecf8 100644
--- a/src/com/android/launcher2/AllAppsView.java
+++ b/src/com/android/launcher2/AllAppsView.java
@@ -31,7 +31,7 @@
 
     public boolean isVisible();
 
-    public boolean isOpaque();
+    public boolean isAnimating();
 
     public void setApps(ArrayList<ApplicationInfo> list);
 
diff --git a/src/com/android/launcher2/ApplicationInfo.java b/src/com/android/launcher2/ApplicationInfo.java
index 5bb5037..6d78a44 100644
--- a/src/com/android/launcher2/ApplicationInfo.java
+++ b/src/com/android/launcher2/ApplicationInfo.java
@@ -17,12 +17,11 @@
 package com.android.launcher2;
 
 import android.content.ComponentName;
-import android.content.ContentValues;
-import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.graphics.Bitmap;
-import android.graphics.drawable.Drawable;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -31,6 +30,7 @@
  * Represents an app in AllAppsView.
  */
 class ApplicationInfo extends ItemInfo {
+    private static final String TAG = "Launcher2.ApplicationInfo";
 
     /**
      * The application name.
@@ -54,6 +54,10 @@
 
     ComponentName componentName;
 
+    static final int APP_FLAG = 1;
+    static final int GAME_FLAG = 2;
+    static final int DOWNLOADED_FLAG = 4;
+    int flags = 0;
 
     ApplicationInfo() {
         itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
@@ -62,15 +66,32 @@
     /**
      * Must not hold the Context.
      */
-    public ApplicationInfo(ResolveInfo info, IconCache iconCache) {
-        this.componentName = new ComponentName(
-                info.activityInfo.applicationInfo.packageName,
-                info.activityInfo.name);
+    public ApplicationInfo(PackageManager pm, ResolveInfo info, IconCache iconCache) {
+        final String packageName = info.activityInfo.applicationInfo.packageName;
 
+        this.componentName = new ComponentName(packageName, info.activityInfo.name);
         this.container = ItemInfo.NO_ID;
         this.setActivity(componentName,
                 Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
 
+        try {
+            int appFlags = pm.getApplicationInfo(packageName, 0).flags;
+            if ((appFlags & android.content.pm.ApplicationInfo.FLAG_SYSTEM) == 0) {
+                flags |= DOWNLOADED_FLAG;
+            }
+            if ((appFlags & android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+                flags |= DOWNLOADED_FLAG;
+            }
+            // TODO: Figure out how to determine what is a game
+
+            // If it's not a game, it's an app
+            if ((flags & GAME_FLAG) == 0) {
+                flags |= APP_FLAG;
+            }
+        } catch (NameNotFoundException e) {
+            Log.d(TAG, "PackageManager.getApplicationInfo failed for " + packageName);
+        }
+
         iconCache.getTitleAndIcon(this, info);
     }
     
diff --git a/src/com/android/launcher2/ApplicationInfoDropTarget.java b/src/com/android/launcher2/ApplicationInfoDropTarget.java
new file mode 100644
index 0000000..737d198
--- /dev/null
+++ b/src/com/android/launcher2/ApplicationInfoDropTarget.java
@@ -0,0 +1,143 @@
+/*
+ * 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.
+ */
+
+package com.android.launcher2;
+
+import android.content.Context;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+
+/**
+ * Implements a DropTarget which allows applications to be dropped on it,
+ * in order to launch the application info for that app.
+ */
+public class ApplicationInfoDropTarget extends ImageView implements DropTarget, DragController.DragListener {
+    private Launcher mLauncher;
+    private DragController mDragController;
+    private boolean mActive = false;
+
+    private View mHandle;
+
+    private final Paint mPaint = new Paint();
+
+    public ApplicationInfoDropTarget(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public ApplicationInfoDropTarget(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+    }
+
+    /**
+     * Set the color that will be used as a filter over objects dragged over this object.
+     */
+    public void setDragColor(int color) {
+        mPaint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP));
+    }
+
+    public boolean acceptDrop(DragSource source, int x, int y, int xOffset, int yOffset,
+            DragView dragView, Object dragInfo) {
+
+        // acceptDrop is called just before onDrop. We do the work here, rather than
+        // in onDrop, because it allows us to reject the drop (by returning false)
+        // so that the object being dragged isn't removed from the home screen.
+
+        String packageName = null;
+        if (dragInfo instanceof ApplicationInfo) {
+            packageName = ((ApplicationInfo)dragInfo).componentName.getPackageName();
+        } else if (dragInfo instanceof ShortcutInfo) {
+            packageName = ((ShortcutInfo)dragInfo).intent.getComponent().getPackageName();
+        }
+        mLauncher.startApplicationDetailsActivity(packageName);
+        return false;
+    }
+
+    public Rect estimateDropLocation(DragSource source, int x, int y, int xOffset, int yOffset,
+            DragView dragView, Object dragInfo, Rect recycle) {
+        return null;
+    }
+
+    public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
+            DragView dragView, Object dragInfo) {
+
+    }
+
+    public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset,
+            DragView dragView, Object dragInfo) {
+        dragView.setPaint(mPaint);
+    }
+
+    public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
+            DragView dragView, Object dragInfo) {
+    }
+
+    public void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset,
+            DragView dragView, Object dragInfo) {
+        // TODO: Animate out
+        dragView.setPaint(null);
+    }
+
+    public void onDragStart(DragSource source, Object info, int dragAction) {
+        if (info != null) {
+            mActive = true;
+
+            // TODO: Animate these in and out
+
+            // Only show the info icon when an application is selected
+            if (((ItemInfo)info).itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+                setVisibility(VISIBLE);
+            }
+            mHandle.setVisibility(INVISIBLE);
+        }
+    }
+
+    public void onDragEnd() {
+        if (mActive) {
+            mActive = false;
+            // TODO: Animate these in and out
+            setVisibility(GONE);
+            mHandle.setVisibility(VISIBLE);
+        }
+    }
+
+    void setLauncher(Launcher launcher) {
+        mLauncher = launcher;
+    }
+
+    void setDragController(DragController dragController) {
+        mDragController = dragController;
+    }
+
+    void setHandle(View view) {
+        mHandle = view;
+    }
+
+    @Override
+    public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset,
+            DragView dragView, Object dragInfo) {
+        return null;
+    }
+}
diff --git a/src/com/android/launcher2/BubbleTextView.java b/src/com/android/launcher2/BubbleTextView.java
index 4a56e1b..076f574 100644
--- a/src/com/android/launcher2/BubbleTextView.java
+++ b/src/com/android/launcher2/BubbleTextView.java
@@ -144,4 +144,10 @@
         super.onDetachedFromWindow();
         mBackground.setCallback(null);
     }
+
+    @Override
+    protected boolean onSetAlpha(int alpha) {
+        mPaint.setAlpha(alpha);
+        return super.onSetAlpha(alpha);
+    }
 }
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index 9d39c2c..7ae26bb 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -16,54 +16,100 @@
 
 package com.android.launcher2;
 
+import com.android.launcher.R;
+
+import android.app.WallpaperManager;
 import android.content.Context;
-import android.content.res.TypedArray;
 import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.RectF;
-import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.ContextMenu;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewDebug;
 import android.view.ViewGroup;
-import android.app.WallpaperManager;
+import android.view.View.OnTouchListener;
+import android.view.animation.Animation;
+import android.view.animation.LayoutAnimationController;
 
 import java.util.ArrayList;
-
-import com.android.launcher.R;
+import java.util.Arrays;
 
 public class CellLayout extends ViewGroup {
+    static final String TAG = "CellLayout";
+    // we make the dimmed bitmap smaller than the screen itself for memory + perf reasons
+    static final float DIMMED_BITMAP_SCALE = 0.25f;
+
     private boolean mPortrait;
 
     private int mCellWidth;
     private int mCellHeight;
-    
-    private int mLongAxisStartPadding;
-    private int mLongAxisEndPadding;
 
-    private int mShortAxisStartPadding;
-    private int mShortAxisEndPadding;
+    private int mLeftPadding;
+    private int mRightPadding;
+    private int mTopPadding;
+    private int mBottomPadding;
 
-    private int mShortAxisCells;
-    private int mLongAxisCells;
+    private int mCountX;
+    private int mCountY;
 
     private int mWidthGap;
     private int mHeightGap;
 
     private final Rect mRect = new Rect();
+    private final RectF mRectF = new RectF();
     private final CellInfo mCellInfo = new CellInfo();
-    
-    int[] mCellXY = new int[2];
+
+    // This is a temporary variable to prevent having to allocate a new object just to
+    // return an (x, y) value from helper functions. Do NOT use it to maintain other state.
+    private final int[] mTmpCellXY = new int[2];
+
     boolean[][] mOccupied;
 
-    private RectF mDragRect = new RectF();
+    private OnTouchListener mInterceptTouchListener;
+
+    // this is what the home screen fades to when it shrinks
+    //   (ie in all apps and in home screen customize mode)
+    private Bitmap mDimmedBitmap;
+    private Canvas mDimmedBitmapCanvas;
+    private float mDimmedBitmapAlpha;
+    private boolean mDimmedBitmapDirty = false;
+    private final Paint mDimmedBitmapPaint = new Paint();
+    private final Rect mLayoutRect = new Rect();
+    private final Rect mDimmedBitmapRect = new Rect();
+    private Drawable mDimmedBitmapBackground;
+    private Drawable mDimmedBitmapBackgroundHover;
+    // If we're actively dragging something over this screen and it's small,
+    // mHover is true
+    private boolean mHover = false;
+
+    private final RectF mDragRect = new RectF();
+
+    // When dragging, used to indicate a vacant drop location
+    private Drawable mVacantDrawable;
+
+    // When dragging, used to indicate an occupied drop location
+    private Drawable mOccupiedDrawable;
+
+    // Updated to point to mVacantDrawable or mOccupiedDrawable, as appropriate
+    private Drawable mDragRectDrawable;
+
+    // When a drag operation is in progress, holds the nearest cell to the touch point
+    private final int[] mDragCell = new int[2];
 
     private boolean mDirtyTag;
     private boolean mLastDownOnOccupiedCell = false;
-    
-    private final WallpaperManager mWallpaperManager;     
+
+    private final WallpaperManager mWallpaperManager;
 
     public CellLayout(Context context) {
         this(context, null);
@@ -75,44 +121,86 @@
 
     public CellLayout(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
+
+        // A ViewGroup usually does not draw, but CellLayout needs to draw a rectangle to show
+        // the user where a dragged item will land when dropped.
+        setWillNotDraw(false);
+        mVacantDrawable = getResources().getDrawable(R.drawable.rounded_rect_green);
+        mOccupiedDrawable = getResources().getDrawable(R.drawable.rounded_rect_red);
+
+        if (LauncherApplication.isScreenXLarge()) {
+            mDimmedBitmapBackground = getResources().getDrawable(
+                    R.drawable.mini_home_screen_bg);
+
+            mDimmedBitmapBackgroundHover = getResources().getDrawable(
+                    R.drawable.mini_home_screen_bg_hover);
+        }
+
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0);
 
         mCellWidth = a.getDimensionPixelSize(R.styleable.CellLayout_cellWidth, 10);
         mCellHeight = a.getDimensionPixelSize(R.styleable.CellLayout_cellHeight, 10);
-        
-        mLongAxisStartPadding = 
-            a.getDimensionPixelSize(R.styleable.CellLayout_longAxisStartPadding, 10);
-        mLongAxisEndPadding = 
-            a.getDimensionPixelSize(R.styleable.CellLayout_longAxisEndPadding, 10);
-        mShortAxisStartPadding =
-            a.getDimensionPixelSize(R.styleable.CellLayout_shortAxisStartPadding, 10);
-        mShortAxisEndPadding = 
-            a.getDimensionPixelSize(R.styleable.CellLayout_shortAxisEndPadding, 10);
-        
-        mShortAxisCells = a.getInt(R.styleable.CellLayout_shortAxisCells, 4);
-        mLongAxisCells = a.getInt(R.styleable.CellLayout_longAxisCells, 4);
+
+        mLeftPadding =
+            a.getDimensionPixelSize(R.styleable.CellLayout_xAxisStartPadding, 10);
+        mRightPadding =
+            a.getDimensionPixelSize(R.styleable.CellLayout_xAxisEndPadding, 10);
+        mTopPadding =
+            a.getDimensionPixelSize(R.styleable.CellLayout_yAxisStartPadding, 10);
+        mBottomPadding =
+            a.getDimensionPixelSize(R.styleable.CellLayout_yAxisEndPadding, 10);
+
+        mCountX = LauncherModel.getCellCountX();
+        mCountY = LauncherModel.getCellCountY();
 
         a.recycle();
 
         setAlwaysDrawnWithCacheEnabled(false);
 
-        if (mOccupied == null) {
-            if (mPortrait) {
-                mOccupied = new boolean[mShortAxisCells][mLongAxisCells];
-            } else {
-                mOccupied = new boolean[mLongAxisCells][mShortAxisCells];
-            }
-        }
-        
         mWallpaperManager = WallpaperManager.getInstance(getContext());
+
+        mDimmedBitmapPaint.setFilterBitmap(true);
+    }
+
+    public void setHover(boolean value) {
+        if (mHover != value) {
+            invalidate();
+        }
+        mHover = value;
     }
 
     @Override
     public void dispatchDraw(Canvas canvas) {
+        if (mDimmedBitmapAlpha > 0.0f) {
+            final Drawable bg = mHover ? mDimmedBitmapBackgroundHover : mDimmedBitmapBackground;
+            bg.setAlpha((int) (mDimmedBitmapAlpha * 255));
+            bg.draw(canvas);
+        }
         super.dispatchDraw(canvas);
     }
 
     @Override
+    protected void onDraw(Canvas canvas) {
+        if (!mDragRect.isEmpty()) {
+            mDragRectDrawable.setBounds(
+                    (int)mDragRect.left,
+                    (int)mDragRect.top,
+                    (int)mDragRect.right,
+                    (int)mDragRect.bottom);
+            mDragRectDrawable.draw(canvas);
+        }
+        if (mDimmedBitmap != null && mDimmedBitmapAlpha > 0.0f) {
+            if (mDimmedBitmapDirty) {
+                updateDimmedBitmap();
+                mDimmedBitmapDirty = false;
+            }
+            mDimmedBitmapPaint.setAlpha((int) (mDimmedBitmapAlpha * 255));
+
+            canvas.drawBitmap(mDimmedBitmap, mDimmedBitmapRect, mLayoutRect, mDimmedBitmapPaint);
+        }
+    }
+
+    @Override
     public void cancelLongPress() {
         super.cancelLongPress();
 
@@ -124,22 +212,49 @@
         }
     }
 
+    public void setOnInterceptTouchListener(View.OnTouchListener listener) {
+        mInterceptTouchListener = listener;
+    }
+
     int getCountX() {
-        return mPortrait ? mShortAxisCells : mLongAxisCells;
+        return mCountX;
     }
 
     int getCountY() {
-        return mPortrait ? mLongAxisCells : mShortAxisCells;
+        return mCountY;
+    }
+
+    // Takes canonical layout parameters
+    public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params) {
+        final LayoutParams lp = params;
+
+        // Generate an id for each view, this assumes we have at most 256x256 cells
+        // per workspace screen
+        if (lp.cellX >= 0 && lp.cellX <= mCountX - 1 && lp.cellY >= 0 && lp.cellY <= mCountY - 1) {
+            // If the horizontal or vertical span is set to -1, it is taken to
+            // mean that it spans the extent of the CellLayout
+            if (lp.cellHSpan < 0) lp.cellHSpan = mCountX;
+            if (lp.cellVSpan < 0) lp.cellVSpan = mCountY;
+
+            child.setId(childId);
+
+            // We might be in the middle or end of shrinking/fading to a dimmed view
+            // Make sure this view's alpha is set the same as all the rest of the views
+            child.setAlpha(1.0f - mDimmedBitmapAlpha);
+
+            addView(child, index, lp);
+
+            // next time we draw the dimmed bitmap we need to update it
+            mDimmedBitmapDirty = true;
+            return true;
+        }
+        return false;
     }
 
     @Override
-    public void addView(View child, int index, ViewGroup.LayoutParams params) {
-        // Generate an id for each view, this assumes we have at most 256x256 cells
-        // per workspace screen
-        final LayoutParams cellParams = (LayoutParams) params;
-        cellParams.regenerateId = true;
-
-        super.addView(child, index, params);
+    public void removeView(View view) {
+        super.removeView(view);
+        mDimmedBitmapDirty = true;
     }
 
     @Override
@@ -158,67 +273,80 @@
         mCellInfo.screen = ((ViewGroup) getParent()).indexOfChild(this);
     }
 
+    public void setTagToCellInfoForPoint(int touchX, int touchY) {
+        final CellInfo cellInfo = mCellInfo;
+        final Rect frame = mRect;
+        final int x = touchX + mScrollX;
+        final int y = touchY + mScrollY;
+        final int count = getChildCount();
+
+        boolean found = false;
+        for (int i = count - 1; i >= 0; i--) {
+            final View child = getChildAt(i);
+
+            if ((child.getVisibility()) == VISIBLE || child.getAnimation() != null) {
+                child.getHitRect(frame);
+                if (frame.contains(x, y)) {
+                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+                    cellInfo.cell = child;
+                    cellInfo.cellX = lp.cellX;
+                    cellInfo.cellY = lp.cellY;
+                    cellInfo.spanX = lp.cellHSpan;
+                    cellInfo.spanY = lp.cellVSpan;
+                    cellInfo.intersectX = lp.cellX;
+                    cellInfo.intersectY = lp.cellY;
+                    cellInfo.valid = true;
+                    found = true;
+                    mDirtyTag = false;
+                    break;
+                }
+            }
+        }
+
+        mLastDownOnOccupiedCell = found;
+
+        if (!found) {
+            final int cellXY[] = mTmpCellXY;
+            pointToCellExact(x, y, cellXY);
+
+            final boolean portrait = mPortrait;
+            final int xCount = mCountX;
+            final int yCount = mCountY;
+
+            final boolean[][] occupied = mOccupied;
+            findOccupiedCells(xCount, yCount, occupied, null, true);
+
+            cellInfo.cell = null;
+            cellInfo.cellX = cellXY[0];
+            cellInfo.cellY = cellXY[1];
+            cellInfo.spanX = 1;
+            cellInfo.spanY = 1;
+            cellInfo.intersectX = cellXY[0];
+            cellInfo.intersectY = cellXY[1];
+            cellInfo.valid = cellXY[0] >= 0 && cellXY[1] >= 0 && cellXY[0] < xCount &&
+                    cellXY[1] < yCount && !occupied[cellXY[0]][cellXY[1]];
+
+            // Instead of finding the interesting vacant cells here, wait until a
+            // caller invokes getTag() to retrieve the result. Finding the vacant
+            // cells is a bit expensive and can generate many new objects, it's
+            // therefore better to defer it until we know we actually need it.
+
+            mDirtyTag = true;
+        }
+        setTag(cellInfo);
+    }
+
+
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
+        if (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev)) {
+            return true;
+        }
         final int action = ev.getAction();
         final CellInfo cellInfo = mCellInfo;
 
         if (action == MotionEvent.ACTION_DOWN) {
-            final Rect frame = mRect;
-            final int x = (int) ev.getX() + mScrollX;
-            final int y = (int) ev.getY() + mScrollY;
-            final int count = getChildCount();
-
-            boolean found = false;
-            for (int i = count - 1; i >= 0; i--) {
-                final View child = getChildAt(i);
-
-                if ((child.getVisibility()) == VISIBLE || child.getAnimation() != null) {
-                    child.getHitRect(frame);
-                    if (frame.contains(x, y)) {
-                        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                        cellInfo.cell = child;
-                        cellInfo.cellX = lp.cellX;
-                        cellInfo.cellY = lp.cellY;
-                        cellInfo.spanX = lp.cellHSpan;
-                        cellInfo.spanY = lp.cellVSpan;
-                        cellInfo.valid = true;
-                        found = true;
-                        mDirtyTag = false;
-                        break;
-                    }
-                }
-            }
-            
-            mLastDownOnOccupiedCell = found;
-
-            if (!found) {
-                int cellXY[] = mCellXY;
-                pointToCellExact(x, y, cellXY);
-
-                final boolean portrait = mPortrait;
-                final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
-                final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
-
-                final boolean[][] occupied = mOccupied;
-                findOccupiedCells(xCount, yCount, occupied, null);
-
-                cellInfo.cell = null;
-                cellInfo.cellX = cellXY[0];
-                cellInfo.cellY = cellXY[1];
-                cellInfo.spanX = 1;
-                cellInfo.spanY = 1;
-                cellInfo.valid = cellXY[0] >= 0 && cellXY[1] >= 0 && cellXY[0] < xCount &&
-                        cellXY[1] < yCount && !occupied[cellXY[0]][cellXY[1]];
-
-                // Instead of finding the interesting vacant cells here, wait until a
-                // caller invokes getTag() to retrieve the result. Finding the vacant
-                // cells is a bit expensive and can generate many new objects, it's
-                // therefore better to defer it until we know we actually need it.
-
-                mDirtyTag = true;
-            }
-            setTag(cellInfo);
+            setTagToCellInfoForPoint((int) ev.getX(), (int) ev.getY());
         } else if (action == MotionEvent.ACTION_UP) {
             cellInfo.cell = null;
             cellInfo.cellX = -1;
@@ -237,93 +365,22 @@
     public CellInfo getTag() {
         final CellInfo info = (CellInfo) super.getTag();
         if (mDirtyTag && info.valid) {
-            final boolean portrait = mPortrait;
-            final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
-            final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
+            final int xCount = mCountX;
+            final int yCount = mCountY;
 
             final boolean[][] occupied = mOccupied;
-            findOccupiedCells(xCount, yCount, occupied, null);
+            findOccupiedCells(xCount, yCount, occupied, null, true);
 
-            findIntersectingVacantCells(info, info.cellX, info.cellY, xCount, yCount, occupied);
+            info.updateOccupiedCells(occupied, mCountX, mCountY);
 
             mDirtyTag = false;
         }
         return info;
     }
 
-    private static void findIntersectingVacantCells(CellInfo cellInfo, int x, int y,
-            int xCount, int yCount, boolean[][] occupied) {
-
-        cellInfo.maxVacantSpanX = Integer.MIN_VALUE;
-        cellInfo.maxVacantSpanXSpanY = Integer.MIN_VALUE;
-        cellInfo.maxVacantSpanY = Integer.MIN_VALUE;
-        cellInfo.maxVacantSpanYSpanX = Integer.MIN_VALUE;
-        cellInfo.clearVacantCells();
-
-        if (occupied[x][y]) {
-            return;
-        }
-
-        cellInfo.current.set(x, y, x, y);
-
-        findVacantCell(cellInfo.current, xCount, yCount, occupied, cellInfo);
-    }
-
-    private static void findVacantCell(Rect current, int xCount, int yCount, boolean[][] occupied,
-            CellInfo cellInfo) {
-
-        addVacantCell(current, cellInfo);
-
-        if (current.left > 0) {
-            if (isColumnEmpty(current.left - 1, current.top, current.bottom, occupied)) {
-                current.left--;
-                findVacantCell(current, xCount, yCount, occupied, cellInfo);
-                current.left++;
-            }
-        }
-
-        if (current.right < xCount - 1) {
-            if (isColumnEmpty(current.right + 1, current.top, current.bottom, occupied)) {
-                current.right++;
-                findVacantCell(current, xCount, yCount, occupied, cellInfo);
-                current.right--;
-            }
-        }
-
-        if (current.top > 0) {
-            if (isRowEmpty(current.top - 1, current.left, current.right, occupied)) {
-                current.top--;
-                findVacantCell(current, xCount, yCount, occupied, cellInfo);
-                current.top++;
-            }
-        }
-
-        if (current.bottom < yCount - 1) {
-            if (isRowEmpty(current.bottom + 1, current.left, current.right, occupied)) {
-                current.bottom++;
-                findVacantCell(current, xCount, yCount, occupied, cellInfo);
-                current.bottom--;
-            }
-        }
-    }
-
-    private static void addVacantCell(Rect current, CellInfo cellInfo) {
-        CellInfo.VacantCell cell = CellInfo.VacantCell.acquire();
-        cell.cellX = current.left;
-        cell.cellY = current.top;
-        cell.spanX = current.right - current.left + 1;
-        cell.spanY = current.bottom - current.top + 1;
-        if (cell.spanX > cellInfo.maxVacantSpanX) {
-            cellInfo.maxVacantSpanX = cell.spanX;
-            cellInfo.maxVacantSpanXSpanY = cell.spanY;
-        }
-        if (cell.spanY > cellInfo.maxVacantSpanY) {
-            cellInfo.maxVacantSpanY = cell.spanY;
-            cellInfo.maxVacantSpanYSpanX = cell.spanX;
-        }
-        cellInfo.vacantCells.add(cell);
-    }
-
+    /**
+     * Check if the column 'x' is empty from rows 'top' to 'bottom', inclusive.
+     */
     private static boolean isColumnEmpty(int x, int top, int bottom, boolean[][] occupied) {
         for (int y = top; y <= bottom; y++) {
             if (occupied[x][y]) {
@@ -333,6 +390,9 @@
         return true;
     }
 
+    /**
+     * Check if the row 'y' is empty from columns 'left' to 'right', inclusive.
+     */
     private static boolean isRowEmpty(int y, int left, int right, boolean[][] occupied) {
         for (int x = left; x <= right; x++) {
             if (occupied[x][y]) {
@@ -342,10 +402,9 @@
         return true;
     }
 
-    CellInfo findAllVacantCells(boolean[] occupiedCells, View ignoreView) {
-        final boolean portrait = mPortrait;
-        final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
-        final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
+    CellInfo updateOccupiedCells(boolean[] occupiedCells, View ignoreView) {
+        final int xCount = mCountX;
+        final int yCount = mCountY;
 
         boolean[][] occupied = mOccupied;
 
@@ -356,65 +415,51 @@
                 }
             }
         } else {
-            findOccupiedCells(xCount, yCount, occupied, ignoreView);
+            findOccupiedCells(xCount, yCount, occupied, ignoreView, true);
         }
 
         CellInfo cellInfo = new CellInfo();
 
         cellInfo.cellX = -1;
         cellInfo.cellY = -1;
+        cellInfo.intersectX = -1;
+        cellInfo.intersectY = -1;
         cellInfo.spanY = 0;
         cellInfo.spanX = 0;
-        cellInfo.maxVacantSpanX = Integer.MIN_VALUE;
-        cellInfo.maxVacantSpanXSpanY = Integer.MIN_VALUE;
-        cellInfo.maxVacantSpanY = Integer.MIN_VALUE;
-        cellInfo.maxVacantSpanYSpanX = Integer.MIN_VALUE;
         cellInfo.screen = mCellInfo.screen;
 
-        Rect current = cellInfo.current;
+        cellInfo.updateOccupiedCells(occupied, mCountX, mCountY);
 
-        for (int x = 0; x < xCount; x++) {
-            for (int y = 0; y < yCount; y++) {
-                if (!occupied[x][y]) {
-                    current.set(x, y, x, y);
-                    findVacantCell(current, xCount, yCount, occupied, cellInfo);
-                    occupied[x][y] = true;
-                }
-            }
-        }
-
-        cellInfo.valid = cellInfo.vacantCells.size() > 0;
+        cellInfo.valid = cellInfo.existsEmptyCell();
 
         // Assume the caller will perform their own cell searching, otherwise we
         // risk causing an unnecessary rebuild after findCellForSpan()
-        
+
         return cellInfo;
     }
 
     /**
-     * Given a point, return the cell that strictly encloses that point 
+     * Given a point, return the cell that strictly encloses that point
      * @param x X coordinate of the point
      * @param y Y coordinate of the point
      * @param result Array of 2 ints to hold the x and y coordinate of the cell
      */
     void pointToCellExact(int x, int y, int[] result) {
-        final boolean portrait = mPortrait;
-        
-        final int hStartPadding = portrait ? mShortAxisStartPadding : mLongAxisStartPadding;
-        final int vStartPadding = portrait ? mLongAxisStartPadding : mShortAxisStartPadding;
+        final int hStartPadding = getLeftPadding();
+        final int vStartPadding = getTopPadding();
 
         result[0] = (x - hStartPadding) / (mCellWidth + mWidthGap);
         result[1] = (y - vStartPadding) / (mCellHeight + mHeightGap);
 
-        final int xAxis = portrait ? mShortAxisCells : mLongAxisCells;
-        final int yAxis = portrait ? mLongAxisCells : mShortAxisCells;
+        final int xAxis = mCountX;
+        final int yAxis = mCountY;
 
         if (result[0] < 0) result[0] = 0;
         if (result[0] >= xAxis) result[0] = xAxis - 1;
         if (result[1] < 0) result[1] = 0;
         if (result[1] >= yAxis) result[1] = yAxis - 1;
     }
-    
+
     /**
      * Given a point, return the cell that most closely encloses that point
      * @param x X coordinate of the point
@@ -427,18 +472,15 @@
 
     /**
      * Given a cell coordinate, return the point that represents the upper left corner of that cell
-     * 
-     * @param cellX X coordinate of the cell 
+     *
+     * @param cellX X coordinate of the cell
      * @param cellY Y coordinate of the cell
-     * 
+     *
      * @param result Array of 2 ints to hold the x and y coordinate of the point
      */
     void cellToPoint(int cellX, int cellY, int[] result) {
-        final boolean portrait = mPortrait;
-        
-        final int hStartPadding = portrait ? mShortAxisStartPadding : mLongAxisStartPadding;
-        final int vStartPadding = portrait ? mLongAxisStartPadding : mShortAxisStartPadding;
-
+        final int hStartPadding = getLeftPadding();
+        final int vStartPadding = getTopPadding();
 
         result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap);
         result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap);
@@ -453,97 +495,66 @@
     }
 
     int getLeftPadding() {
-        return mPortrait ? mShortAxisStartPadding : mLongAxisStartPadding;
+        return mLeftPadding;
     }
 
     int getTopPadding() {
-        return mPortrait ? mLongAxisStartPadding : mShortAxisStartPadding;        
+        return mTopPadding;
     }
 
     int getRightPadding() {
-        return mPortrait ? mShortAxisEndPadding : mLongAxisEndPadding;
+        return mRightPadding;
     }
 
     int getBottomPadding() {
-        return mPortrait ? mLongAxisEndPadding : mShortAxisEndPadding;        
+        return mBottomPadding;
     }
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         // TODO: currently ignoring padding
-        
+
         int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
-        int widthSpecSize =  MeasureSpec.getSize(widthMeasureSpec);
-        
+        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
+
         int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
         int heightSpecSize =  MeasureSpec.getSize(heightMeasureSpec);
-        
+
         if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
             throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions");
         }
 
-        final int shortAxisCells = mShortAxisCells;
-        final int longAxisCells = mLongAxisCells;
-        final int longAxisStartPadding = mLongAxisStartPadding;
-        final int longAxisEndPadding = mLongAxisEndPadding;
-        final int shortAxisStartPadding = mShortAxisStartPadding;
-        final int shortAxisEndPadding = mShortAxisEndPadding;
         final int cellWidth = mCellWidth;
         final int cellHeight = mCellHeight;
 
-        mPortrait = heightSpecSize > widthSpecSize;
-
-        int numShortGaps = shortAxisCells - 1;
-        int numLongGaps = longAxisCells - 1;
-
-        if (mPortrait) {
-            int vSpaceLeft = heightSpecSize - longAxisStartPadding - longAxisEndPadding
-                    - (cellHeight * longAxisCells);
-            mHeightGap = vSpaceLeft / numLongGaps;
-
-            int hSpaceLeft = widthSpecSize - shortAxisStartPadding - shortAxisEndPadding
-                    - (cellWidth * shortAxisCells);
-            if (numShortGaps > 0) {
-                mWidthGap = hSpaceLeft / numShortGaps;
-            } else {
-                mWidthGap = 0;
-            }
-        } else {
-            int hSpaceLeft = widthSpecSize - longAxisStartPadding - longAxisEndPadding
-                    - (cellWidth * longAxisCells);
-            mWidthGap = hSpaceLeft / numLongGaps;
-
-            int vSpaceLeft = heightSpecSize - shortAxisStartPadding - shortAxisEndPadding
-                    - (cellHeight * shortAxisCells);
-            if (numShortGaps > 0) {
-                mHeightGap = vSpaceLeft / numShortGaps;
-            } else {
-                mHeightGap = 0;
-            }
+        if (mOccupied == null) {
+            mOccupied = new boolean[mCountX][mCountY];
         }
-        
+
+        int numWidthGaps = mCountX - 1;
+        int numHeightGaps = mCountY - 1;
+
+        int vSpaceLeft = heightSpecSize - mTopPadding
+                - mBottomPadding - (cellHeight * mCountY);
+        mHeightGap = vSpaceLeft / numHeightGaps;
+
+        int hSpaceLeft = widthSpecSize - mLeftPadding
+                - mRightPadding - (cellWidth * mCountX);
+        mWidthGap = hSpaceLeft / numWidthGaps;
+
         int count = getChildCount();
 
         for (int i = 0; i < count; i++) {
             View child = getChildAt(i);
             LayoutParams lp = (LayoutParams) child.getLayoutParams();
+            lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap,
+                    mLeftPadding, mTopPadding);
 
-            if (mPortrait) {
-                lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap, shortAxisStartPadding,
-                        longAxisStartPadding);
-            } else {
-                lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap, longAxisStartPadding,
-                        shortAxisStartPadding);
-            }
-            
-            if (lp.regenerateId) {
-                child.setId(((getId() & 0xFF) << 16) | (lp.cellX & 0xFF) << 8 | (lp.cellY & 0xFF));
-                lp.regenerateId = false;
-            }
+            int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width,
+                    MeasureSpec.EXACTLY);
+            int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height,
+                    MeasureSpec.EXACTLY);
 
-            int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
-            int childheightMeasureSpec =
-                    MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
             child.measure(childWidthMeasureSpec, childheightMeasureSpec);
         }
 
@@ -567,7 +578,7 @@
                 if (lp.dropped) {
                     lp.dropped = false;
 
-                    final int[] cellXY = mCellXY;
+                    final int[] cellXY = mTmpCellXY;
                     getLocationOnScreen(cellXY);
                     mWallpaperManager.sendWallpaperCommand(getWindowToken(), "android.home.drop",
                             cellXY[0] + childLeft + lp.width / 2,
@@ -578,6 +589,19 @@
     }
 
     @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        mLayoutRect.set(0, 0, w, h);
+        mDimmedBitmapRect.set(0, 0, (int) (DIMMED_BITMAP_SCALE * w), (int) (DIMMED_BITMAP_SCALE * h));
+        if (mDimmedBitmapBackground != null) {
+            mDimmedBitmapBackground.setBounds(mLayoutRect);
+        }
+        if (mDimmedBitmapBackgroundHover != null) {
+            mDimmedBitmapBackgroundHover.setBounds(mLayoutRect);
+        }
+    }
+
+    @Override
     protected void setChildrenDrawingCacheEnabled(boolean enabled) {
         final int count = getChildCount();
         for (int i = 0; i < count; i++) {
@@ -593,10 +617,156 @@
         super.setChildrenDrawnWithCacheEnabled(enabled);
     }
 
+    public float getDimmedBitmapAlpha() {
+        return mDimmedBitmapAlpha;
+    }
+
+    public void setDimmedBitmapAlpha(float alpha) {
+        // If we're dimming the screen after it was not dimmed, refresh
+        // to allow for updated widgets. We don't continually refresh it
+        // after this point, however, as an optimization
+        if (mDimmedBitmapAlpha == 0.0f && alpha > 0.0f) {
+            updateDimmedBitmap();
+        }
+        mDimmedBitmapAlpha = alpha;
+        setChildrenAlpha(1.0f - mDimmedBitmapAlpha);
+        invalidate();
+    }
+
+    private void setChildrenAlpha(float alpha) {
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            getChildAt(i).setAlpha(alpha);
+        }
+    }
+
+    public void updateDimmedBitmap() {
+        if (mDimmedBitmap == null) {
+            mDimmedBitmap = Bitmap.createBitmap((int) (getWidth() * DIMMED_BITMAP_SCALE),
+                    (int) (getHeight() * DIMMED_BITMAP_SCALE), Bitmap.Config.ARGB_8888);
+            mDimmedBitmapCanvas = new Canvas(mDimmedBitmap);
+            mDimmedBitmapCanvas.scale(DIMMED_BITMAP_SCALE, DIMMED_BITMAP_SCALE);
+        }
+        // clear the canvas
+        mDimmedBitmapCanvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
+
+        // draw the screen into the bitmap
+        // just for drawing to the bitmap, make all the items on the screen opaque
+        setChildrenAlpha(1.0f);
+        // call our superclass's dispatchdraw so we don't draw the background
+        super.dispatchDraw(mDimmedBitmapCanvas);
+        setChildrenAlpha(1.0f - mDimmedBitmapAlpha);
+
+        // replace all colored areas with a dark  (semi-transparent black)
+        mDimmedBitmapCanvas.drawColor(Color.argb(160, 0, 0, 0), PorterDuff.Mode.SRC_IN);
+    }
+
+    private boolean isVacant(int originX, int originY, int spanX, int spanY) {
+        for (int i = 0; i < spanY; i++) {
+            if (!isRowEmpty(originY + i, originX, originX + spanX - 1, mOccupied)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public View getChildAt(int x, int y) {
+        final int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            View child = getChildAt(i);
+            LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+            if ((lp.cellX <= x) && (x < lp.cellX + lp.cellHSpan) &&
+                    (lp.cellY <= y) && (y < lp.cellY + lp.cellHSpan)) {
+                return child;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Estimate the size that a child with the given dimensions will take in the layout.
+     */
+    void estimateChildSize(int minWidth, int minHeight, int[] result) {
+        // Assuming it's placed at 0, 0, find where the bottom right cell will land
+        rectToCell(minWidth, minHeight, result);
+
+        // Then figure out the rect it will occupy
+        cellToRect(0, 0, result[0], result[1], mRectF);
+        result[0] = (int)mRectF.width();
+        result[1] = (int)mRectF.height();
+    }
+
+    /**
+     * Estimate where the top left cell of the dragged item will land if it is dropped.
+     *
+     * @param originX The X value of the top left corner of the item
+     * @param originY The Y value of the top left corner of the item
+     * @param spanX The number of horizontal cells that the item spans
+     * @param spanY The number of vertical cells that the item spans
+     * @param result The estimated drop cell X and Y.
+     */
+    void estimateDropCell(int originX, int originY, int spanX, int spanY, int[] result) {
+        final int countX = mCountX;
+        final int countY = mCountY;
+
+        // pointToCellRounded takes the top left of a cell but will pad that with
+        // cellWidth/2 and cellHeight/2 when finding the matching cell
+        pointToCellRounded(originX, originY, result);
+
+        // If the item isn't fully on this screen, snap to the edges
+        int rightOverhang = result[0] + spanX - countX;
+        if (rightOverhang > 0) {
+            result[0] -= rightOverhang; // Snap to right
+        }
+        result[0] = Math.max(0, result[0]); // Snap to left
+        int bottomOverhang = result[1] + spanY - countY;
+        if (bottomOverhang > 0) {
+            result[1] -= bottomOverhang; // Snap to bottom
+        }
+        result[1] = Math.max(0, result[1]); // Snap to top
+    }
+
+    void visualizeDropLocation(View view, int originX, int originY, int spanX, int spanY) {
+        final int[] originCell = mDragCell;
+        final int[] cellXY = mTmpCellXY;
+        estimateDropCell(originX, originY, spanX, spanY, cellXY);
+
+        // Only recalculate the bounding rect when necessary
+        if (!Arrays.equals(cellXY, originCell)) {
+            originCell[0] = cellXY[0];
+            originCell[1] = cellXY[1];
+
+            // Find the top left corner of the rect the object will occupy
+            final int[] topLeft = mTmpCellXY;
+            cellToPoint(originCell[0], originCell[1], topLeft);
+            final int left = topLeft[0];
+            final int top = topLeft[1];
+
+            // Now find the bottom right
+            final int[] bottomRight = mTmpCellXY;
+            cellToPoint(originCell[0] + spanX - 1, originCell[1] + spanY - 1, bottomRight);
+            bottomRight[0] += mCellWidth;
+            bottomRight[1] += mCellHeight;
+
+            final int countX = mCountX;
+            final int countY = mCountY;
+            // TODO: It's not necessary to do this every time, but it's not especially expensive
+            findOccupiedCells(countX, countY, mOccupied, view, false);
+
+            boolean vacant = isVacant(originCell[0], originCell[1], spanX, spanY);
+            mDragRectDrawable = vacant ? mVacantDrawable : mOccupiedDrawable;
+
+            // mDragRect will be rendered in onDraw()
+            mDragRect.set(left, top, bottomRight[0], bottomRight[1]);
+            invalidate();
+        }
+    }
+
     /**
      * Find a vacant area that will fit the given bounds nearest the requested
      * cell location. Uses Euclidean distance to score multiple vacant areas.
-     * 
+     *
      * @param pixelX The X location at which you want to search for a vacant area.
      * @param pixelY The Y location at which you want to search for a vacant area.
      * @param spanX Horizontal span of the object.
@@ -608,77 +778,84 @@
      */
     int[] findNearestVacantArea(int pixelX, int pixelY, int spanX, int spanY,
             CellInfo vacantCells, int[] recycle) {
-        
+
         // Keep track of best-scoring drop area
         final int[] bestXY = recycle != null ? recycle : new int[2];
-        final int[] cellXY = mCellXY;
         double bestDistance = Double.MAX_VALUE;
-        
-        // Bail early if vacant cells aren't valid
-        if (!vacantCells.valid) {
-            return null;
-        }
 
-        // Look across all vacant cells for best fit
-        final int size = vacantCells.vacantCells.size();
-        for (int i = 0; i < size; i++) {
-            final CellInfo.VacantCell cell = vacantCells.vacantCells.get(i);
-            
-            // Reject if vacant cell isn't our exact size
-            if (cell.spanX != spanX || cell.spanY != spanY) {
-                continue;
-            }
-            
-            // Score is center distance from requested pixel
-            cellToPoint(cell.cellX, cell.cellY, cellXY);
-            
-            double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2) +
-                    Math.pow(cellXY[1] - pixelY, 2));
-            if (distance <= bestDistance) {
-                bestDistance = distance;
-                bestXY[0] = cell.cellX;
-                bestXY[1] = cell.cellY;
+        for (int x = 0; x < mCountX - (spanX - 1); x++) {
+            inner:
+            for (int y = 0; y < mCountY - (spanY - 1); y++) {
+                for (int i = 0; i < spanX; i++) {
+                    for (int j = 0; j < spanY; j++) {
+                        if (mOccupied[x + i][y + j]) {
+                            // small optimization: we can skip to below the row we just found
+                            // an occupied cell
+                            y += j;
+                            continue inner;
+                        }
+                    }
+                }
+                final int[] cellXY = mTmpCellXY;
+                cellToPoint(x, y, cellXY);
+
+                double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2)
+                        + Math.pow(cellXY[1] - pixelY, 2));
+                if (distance <= bestDistance) {
+                    bestDistance = distance;
+                    bestXY[0] = x;
+                    bestXY[1] = y;
+                }
             }
         }
 
-        // Return null if no suitable location found 
+        // Return null if no suitable location found
         if (bestDistance < Double.MAX_VALUE) {
             return bestXY;
         } else {
             return null;
         }
     }
-    
+
     /**
-     * Drop a child at the specified position
+     * Called when a drag and drop operation has finished (successfully or not).
+     */
+    void onDragComplete() {
+        // Invalidate the drag data
+        mDragCell[0] = -1;
+        mDragCell[1] = -1;
+
+        setHover(false);
+        mDragRect.setEmpty();
+        invalidate();
+    }
+
+    /**
+     * Mark a child as having been dropped.
      *
      * @param child The child that is being dropped
-     * @param targetXY Destination area to move to
      */
-    void onDropChild(View child, int[] targetXY) {
+    void onDropChild(View child) {
         if (child != null) {
             LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            lp.cellX = targetXY[0];
-            lp.cellY = targetXY[1];
             lp.isDragging = false;
             lp.dropped = true;
             mDragRect.setEmpty();
             child.requestLayout();
-            invalidate();
         }
+        onDragComplete();
     }
 
     void onDropAborted(View child) {
         if (child != null) {
             ((LayoutParams) child.getLayoutParams()).isDragging = false;
-            invalidate();
         }
-        mDragRect.setEmpty();
+        onDragComplete();
     }
 
     /**
      * Start dragging the specified child
-     * 
+     *
      * @param child The child that is being dragged
      */
     void onDragChild(View child) {
@@ -686,58 +863,43 @@
         lp.isDragging = true;
         mDragRect.setEmpty();
     }
-    
-    /**
-     * Drag a child over the specified position
-     * 
-     * @param child The child that is being dropped
-     * @param cellX The child's new x cell location
-     * @param cellY The child's new y cell location 
-     */
-    void onDragOverChild(View child, int cellX, int cellY) {
-        int[] cellXY = mCellXY;
-        pointToCellRounded(cellX, cellY, cellXY);
-        LayoutParams lp = (LayoutParams) child.getLayoutParams();
-        cellToRect(cellXY[0], cellXY[1], lp.cellHSpan, lp.cellVSpan, mDragRect);
-        invalidate();
-    }
-    
+
     /**
      * Computes a bounding rectangle for a range of cells
-     *  
+     *
      * @param cellX X coordinate of upper left corner expressed as a cell position
      * @param cellY Y coordinate of upper left corner expressed as a cell position
-     * @param cellHSpan Width in cells 
+     * @param cellHSpan Width in cells
      * @param cellVSpan Height in cells
-     * @param dragRect Rectnagle into which to put the results
+     * @param resultRect Rect into which to put the results
      */
-    public void cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, RectF dragRect) {
-        final boolean portrait = mPortrait;
+    public void cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, RectF resultRect) {
         final int cellWidth = mCellWidth;
         final int cellHeight = mCellHeight;
         final int widthGap = mWidthGap;
         final int heightGap = mHeightGap;
-        
-        final int hStartPadding = portrait ? mShortAxisStartPadding : mLongAxisStartPadding;
-        final int vStartPadding = portrait ? mLongAxisStartPadding : mShortAxisStartPadding;
-        
+
+        final int hStartPadding = getLeftPadding();
+        final int vStartPadding = getTopPadding();
+
         int width = cellHSpan * cellWidth + ((cellHSpan - 1) * widthGap);
         int height = cellVSpan * cellHeight + ((cellVSpan - 1) * heightGap);
 
         int x = hStartPadding + cellX * (cellWidth + widthGap);
         int y = vStartPadding + cellY * (cellHeight + heightGap);
-        
-        dragRect.set(x, y, x + width, y + height);
+
+        resultRect.set(x, y, x + width, y + height);
     }
-    
+
     /**
-     * Computes the required horizontal and vertical cell spans to always 
+     * Computes the required horizontal and vertical cell spans to always
      * fit the given rectangle.
-     *  
+     *
      * @param width Width in pixels
      * @param height Height in pixels
+     * @param result An array of length 2 in which to store the result (may be null).
      */
-    public int[] rectToCell(int width, int height) {
+    public int[] rectToCell(int width, int height, int[] result) {
         // Always assume we're working with the smallest span to make sure we
         // reserve enough space in both orientations.
         final Resources resources = getResources();
@@ -749,7 +911,12 @@
         int spanX = (width + smallerSize) / smallerSize;
         int spanY = (height + smallerSize) / smallerSize;
 
-        return new int[] { spanX, spanY };
+        if (result == null) {
+            return new int[] { spanX, spanY };
+        }
+        result[0] = spanX;
+        result[1] = spanY;
+        return result;
     }
 
     /**
@@ -758,16 +925,15 @@
      * @param vacant Holds the x and y coordinate of the vacant cell
      * @param spanX Horizontal cell span.
      * @param spanY Vertical cell span.
-     * 
+     *
      * @return True if a vacant cell was found
      */
     public boolean getVacantCell(int[] vacant, int spanX, int spanY) {
-        final boolean portrait = mPortrait;
-        final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
-        final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
+        final int xCount = mCountX;
+        final int yCount = mCountY;
         final boolean[][] occupied = mOccupied;
 
-        findOccupiedCells(xCount, yCount, occupied, null);
+        findOccupiedCells(xCount, yCount, occupied, null, true);
 
         return findVacantCell(vacant, spanX, spanY, xCount, yCount, occupied);
     }
@@ -796,13 +962,15 @@
         return false;
     }
 
-    boolean[] getOccupiedCells() {
-        final boolean portrait = mPortrait;
-        final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
-        final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
+    /**
+     * Update the array of occupied cells (mOccupied), and return a flattened copy of the array.
+     */
+    boolean[] getOccupiedCellsFlattened() {
+        final int xCount = mCountX;
+        final int yCount = mCountY;
         final boolean[][] occupied = mOccupied;
 
-        findOccupiedCells(xCount, yCount, occupied, null);
+        findOccupiedCells(xCount, yCount, occupied, null, true);
 
         final boolean[] flat = new boolean[xCount * yCount];
         for (int y = 0; y < yCount; y++) {
@@ -814,7 +982,14 @@
         return flat;
     }
 
-    private void findOccupiedCells(int xCount, int yCount, boolean[][] occupied, View ignoreView) {
+    /**
+     * Update the array of occupied cells.
+     * @param ignoreView If non-null, the space occupied by this View is treated as vacant
+     * @param ignoreFolders If true, a cell occupied by a Folder is treated as vacant
+     */
+    private void findOccupiedCells(
+            int xCount, int yCount, boolean[][] occupied, View ignoreView, boolean ignoreFolders) {
+
         for (int x = 0; x < xCount; x++) {
             for (int y = 0; y < yCount; y++) {
                 occupied[x][y] = false;
@@ -824,7 +999,7 @@
         int count = getChildCount();
         for (int i = 0; i < count; i++) {
             View child = getChildAt(i);
-            if (child instanceof Folder || child.equals(ignoreView)) {
+            if ((ignoreFolders && child instanceof Folder) || child.equals(ignoreView)) {
                 continue;
             }
             LayoutParams lp = (LayoutParams) child.getLayoutParams();
@@ -852,6 +1027,17 @@
         return new CellLayout.LayoutParams(p);
     }
 
+    public static class CellLayoutAnimationController extends LayoutAnimationController {
+        public CellLayoutAnimationController(Animation animation, float delay) {
+            super(animation, delay);
+        }
+
+        @Override
+        protected long getDelayForView(View view) {
+            return (int) (Math.random() * 150);
+        }
+    }
+
     public static class LayoutParams extends ViewGroup.MarginLayoutParams {
         /**
          * Horizontal location of the item in the grid.
@@ -876,7 +1062,7 @@
          */
         @ViewDebug.ExportedProperty
         public int cellVSpan;
-        
+
         /**
          * Is this item currently being dragged
          */
@@ -889,8 +1075,6 @@
         @ViewDebug.ExportedProperty
         int y;
 
-        boolean regenerateId;
-        
         boolean dropped;
 
         public LayoutParams(Context c, AttributeSet attrs) {
@@ -904,7 +1088,15 @@
             cellHSpan = 1;
             cellVSpan = 1;
         }
-        
+
+        public LayoutParams(LayoutParams source) {
+            super(source);
+            this.cellX = source.cellX;
+            this.cellY = source.cellY;
+            this.cellHSpan = source.cellHSpan;
+            this.cellVSpan = source.cellVSpan;
+        }
+
         public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
             super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
             this.cellX = cellX;
@@ -915,12 +1107,12 @@
 
         public void setup(int cellWidth, int cellHeight, int widthGap, int heightGap,
                 int hStartPadding, int vStartPadding) {
-            
+
             final int myCellHSpan = cellHSpan;
             final int myCellVSpan = cellVSpan;
             final int myCellX = cellX;
             final int myCellY = cellY;
-            
+
             width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) -
                     leftMargin - rightMargin;
             height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) -
@@ -929,111 +1121,55 @@
             x = hStartPadding + myCellX * (cellWidth + widthGap) + leftMargin;
             y = vStartPadding + myCellY * (cellHeight + heightGap) + topMargin;
         }
+
+        public String toString() {
+            return "(" + this.cellX + ", " + this.cellY + ")";
+        }
     }
 
     static final class CellInfo implements ContextMenu.ContextMenuInfo {
-        /**
-         * See View.AttachInfo.InvalidateInfo for futher explanations about
-         * the recycling mechanism. In this case, we recycle the vacant cells
-         * instances because up to several hundreds can be instanciated when
-         * the user long presses an empty cell.
-         */
-        static final class VacantCell {
-            int cellX;
-            int cellY;
-            int spanX;
-            int spanY;
-
-            // We can create up to 523 vacant cells on a 4x4 grid, 100 seems
-            // like a reasonable compromise given the size of a VacantCell and
-            // the fact that the user is not likely to touch an empty 4x4 grid
-            // very often 
-            private static final int POOL_LIMIT = 100;
-            private static final Object sLock = new Object();
-
-            private static int sAcquiredCount = 0;
-            private static VacantCell sRoot;
-
-            private VacantCell next;
-
-            static VacantCell acquire() {
-                synchronized (sLock) {
-                    if (sRoot == null) {
-                        return new VacantCell();
-                    }
-
-                    VacantCell info = sRoot;
-                    sRoot = info.next;
-                    sAcquiredCount--;
-
-                    return info;
-                }
-            }
-
-            void release() {
-                synchronized (sLock) {
-                    if (sAcquiredCount < POOL_LIMIT) {
-                        sAcquiredCount++;
-                        next = sRoot;
-                        sRoot = this;
-                    }
-                }
-            }
-
-            @Override
-            public String toString() {
-                return "VacantCell[x=" + cellX + ", y=" + cellY + ", spanX=" + spanX +
-                        ", spanY=" + spanY + "]";
-            }
-        }
-
+        private boolean[][] mOccupied;
+        private int mCountX;
+        private int mCountY;
         View cell;
-        int cellX;
-        int cellY;
+        int cellX = -1;
+        int cellY = -1;
+        // intersectX and intersectY constrain the results of findCellForSpan; any empty space
+        // it results must include this point (unless intersectX and intersectY are -1)
+        int intersectX = -1;
+        int intersectY = -1;
         int spanX;
         int spanY;
         int screen;
         boolean valid;
 
-        final ArrayList<VacantCell> vacantCells = new ArrayList<VacantCell>(VacantCell.POOL_LIMIT);
-        int maxVacantSpanX;
-        int maxVacantSpanXSpanY;
-        int maxVacantSpanY;
-        int maxVacantSpanYSpanX;
-        final Rect current = new Rect();
-
-        void clearVacantCells() {
-            final ArrayList<VacantCell> list = vacantCells;
-            final int count = list.size();
-
-            for (int i = 0; i < count; i++) list.get(i).release();
-
-            list.clear();
+        void updateOccupiedCells(boolean[][] occupied, int xCount, int yCount) {
+            mOccupied = occupied.clone();
+            mCountX = xCount;
+            mCountY = yCount;
         }
 
-        void findVacantCellsFromOccupied(boolean[] occupied, int xCount, int yCount) {
-            if (cellX < 0 || cellY < 0) {
-                maxVacantSpanX = maxVacantSpanXSpanY = Integer.MIN_VALUE;
-                maxVacantSpanY = maxVacantSpanYSpanX = Integer.MIN_VALUE;
-                clearVacantCells();
-                return;
+        void updateOccupiedCells(boolean[] occupied, int xCount, int yCount) {
+            if (mOccupied == null || mCountX != xCount || mCountY != yCount) {
+                mOccupied = new boolean[xCount][yCount];
             }
-
-            final boolean[][] unflattened = new boolean[xCount][yCount];
+            mCountX = xCount;
+            mCountY = yCount;
             for (int y = 0; y < yCount; y++) {
                 for (int x = 0; x < xCount; x++) {
-                    unflattened[x][y] = occupied[y * xCount + x];
+                    mOccupied[x][y] = occupied[y * xCount + x];
                 }
             }
-            CellLayout.findIntersectingVacantCells(this, cellX, cellY, xCount, yCount, unflattened);
         }
 
+        boolean existsEmptyCell() {
+            return findCellForSpan(null, 1, 1);
+        }
         /**
-         * This method can be called only once! Calling #findVacantCellsFromOccupied will
-         * restore the ability to call this method.
-         *
          * Finds the upper-left coordinate of the first rectangle in the grid that can
-         * hold a cell of the specified dimensions.
+         * hold a cell of the specified dimensions. If intersectX and intersectY are not -1,
+         * then this method will only return coordinates for rectangles that contain the cell
+         * (intersectX, intersectY)
          *
          * @param cellXY The array that will contain the position of a vacant cell if such a cell
          *               can be found.
@@ -1043,52 +1179,61 @@
          * @return True if a vacant cell of the specified dimension was found, false otherwise.
          */
         boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
-            return findCellForSpan(cellXY, spanX, spanY, true);
-        }
+            // return the span represented by the CellInfo only there is no view there
+            //   (this.cell == null) and there is enough space
 
-        boolean findCellForSpan(int[] cellXY, int spanX, int spanY, boolean clear) {
-            final ArrayList<VacantCell> list = vacantCells;
-            final int count = list.size();
-
-            boolean found = false;
-
-            if (this.spanX >= spanX && this.spanY >= spanY) {
-                cellXY[0] = cellX;
-                cellXY[1] = cellY;
-                found = true;
+            if (this.cell == null && this.spanX >= spanX && this.spanY >= spanY) {
+                if (cellXY != null) {
+                    cellXY[0] = cellX;
+                    cellXY[1] = cellY;
+                }
+                return true;
             }
 
-            // Look for an exact match first
-            for (int i = 0; i < count; i++) {
-                VacantCell cell = list.get(i);
-                if (cell.spanX == spanX && cell.spanY == spanY) {
-                    cellXY[0] = cell.cellX;
-                    cellXY[1] = cell.cellY;
-                    found = true;
-                    break;
+            int startX = 0;
+            if (intersectX >= 0) {
+                startX = Math.max(startX, intersectX - (spanX - 1));
+            }
+            int endX = mCountX - (spanX - 1);
+            if (intersectX >= 0) {
+                endX = Math.min(endX, intersectX + (spanX - 1));
+            }
+            int startY = 0;
+            if (intersectY >= 0) {
+                startY = Math.max(startY, intersectY - (spanY - 1));
+            }
+            int endY = mCountY - (spanY - 1);
+            if (intersectY >= 0) {
+                endY = Math.min(endY, intersectY + (spanY - 1));
+            }
+
+            for (int x = startX; x < endX; x++) {
+                inner:
+                for (int y = startY; y < endY; y++) {
+                    for (int i = 0; i < spanX; i++) {
+                        for (int j = 0; j < spanY; j++) {
+                            if (mOccupied[x + i][y + j]) {
+                                // small optimization: we can skip to below the row we just found
+                                // an occupied cell
+                                y += j;
+                                continue inner;
+                            }
+                        }
+                    }
+                    if (cellXY != null) {
+                        cellXY[0] = x;
+                        cellXY[1] = y;
+                    }
+                    return true;
                 }
             }
-
-            // Look for the first cell large enough
-            for (int i = 0; i < count; i++) {
-                VacantCell cell = list.get(i);
-                if (cell.spanX >= spanX && cell.spanY >= spanY) {
-                    cellXY[0] = cell.cellX;
-                    cellXY[1] = cell.cellY;
-                    found = true;
-                    break;
-                }
-            }
-
-            if (clear) clearVacantCells();
-
-            return found;
+            return false;
         }
 
         @Override
         public String toString() {
-            return "Cell[view=" + (cell == null ? "null" : cell.getClass()) + ", x=" + cellX +
-                    ", y=" + cellY + "]";
+            return "Cell[view=" + (cell == null ? "null" : cell.getClass())
+                    + ", x=" + cellX + ", y=" + cellY + "]";
         }
     }
 
@@ -1096,5 +1241,3 @@
         return mLastDownOnOccupiedCell;
     }
 }
-
-
diff --git a/src/com/android/launcher2/CustomizePagedView.java b/src/com/android/launcher2/CustomizePagedView.java
new file mode 100644
index 0000000..786300b
--- /dev/null
+++ b/src/com/android/launcher2/CustomizePagedView.java
@@ -0,0 +1,545 @@
+/*
+ * 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.
+ */
+
+package com.android.launcher2;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.Region.Op;
+import android.graphics.drawable.Drawable;
+import android.provider.LiveFolders;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.launcher.R;
+
+public class CustomizePagedView extends PagedView
+    implements View.OnLongClickListener,
+                DragSource {
+
+    public enum CustomizationType {
+        WidgetCustomization,
+        FolderCustomization,
+        ShortcutCustomization,
+        WallpaperCustomization
+    }
+
+    private static final String TAG = "CustomizeWorkspace";
+    private static final boolean DEBUG = false;
+
+    private Launcher mLauncher;
+    private DragController mDragController;
+    private PackageManager mPackageManager;
+
+    private CustomizationType mCustomizationType;
+
+    private PagedViewCellLayout mTmpWidgetLayout;
+    private ArrayList<ArrayList<PagedViewCellLayout.LayoutParams>> mWidgetPages;
+    private List<AppWidgetProviderInfo> mWidgetList;
+    private List<ResolveInfo> mFolderList;
+    private List<ResolveInfo> mShortcutList;
+
+    private int mCellCountX;
+    private int mCellCountY;
+
+    private final Canvas mCanvas = new Canvas();
+    private final LayoutInflater mInflater;
+
+    public CustomizePagedView(Context context) {
+        this(context, null);
+    }
+
+    public CustomizePagedView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mCellCountX = 8;
+        mCellCountY = 4;
+        mCustomizationType = CustomizationType.WidgetCustomization;
+        mWidgetPages = new ArrayList<ArrayList<PagedViewCellLayout.LayoutParams>>();
+        mTmpWidgetLayout = new PagedViewCellLayout(context);
+        mInflater = LayoutInflater.from(context);
+        setupPage(mTmpWidgetLayout);
+        setVisibility(View.GONE);
+        setSoundEffectsEnabled(false);
+    }
+
+    public void setLauncher(Launcher launcher) {
+        Context context = getContext();
+        mLauncher = launcher;
+        mPackageManager = context.getPackageManager();
+    }
+
+    public void update() {
+        Context context = getContext();
+
+        // get the list of widgets
+        mWidgetList = AppWidgetManager.getInstance(mLauncher).getInstalledProviders();
+        Collections.sort(mWidgetList, new Comparator<AppWidgetProviderInfo>() {
+            @Override
+            public int compare(AppWidgetProviderInfo object1, AppWidgetProviderInfo object2) {
+                return object1.label.compareTo(object2.label);
+            }
+        });
+
+        Comparator<ResolveInfo> resolveInfoComparator = new Comparator<ResolveInfo>() {
+            @Override
+            public int compare(ResolveInfo object1, ResolveInfo object2) {
+                return object1.loadLabel(mPackageManager).toString().compareTo(
+                        object2.loadLabel(mPackageManager).toString());
+            }
+        };
+
+        // get the list of live folder intents
+        Intent liveFolderIntent = new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER);
+        mFolderList = mPackageManager.queryIntentActivities(liveFolderIntent, 0);
+
+        // manually create a separate entry for creating a folder in Launcher
+        ResolveInfo folder = new ResolveInfo();
+        folder.icon = R.drawable.ic_launcher_folder;
+        folder.labelRes = R.string.group_folder;
+        folder.resolvePackageName = context.getPackageName();
+        mFolderList.add(0, folder);
+        Collections.sort(mFolderList, resolveInfoComparator);
+
+        // get the list of shortcuts
+        Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
+        mShortcutList = mPackageManager.queryIntentActivities(shortcutsIntent, 0);
+        Collections.sort(mShortcutList, resolveInfoComparator);
+
+        // reset the icon cache
+        mPageViewIconCache.clear();
+
+        invalidatePageData();
+    }
+
+    public void setDragController(DragController dragger) {
+        mDragController = dragger;
+    }
+
+    public void setCustomizationFilter(CustomizationType filterType) {
+        mCustomizationType = filterType;
+        setCurrentPage(0);
+        invalidatePageData();
+    }
+
+    @Override
+    public void onDropCompleted(View target, boolean success) {
+        // do nothing
+    }
+
+    @Override
+    public boolean onLongClick(View v) {
+        if (!v.isInTouchMode()) {
+            return false;
+        }
+
+        final View animView = v;
+        switch (mCustomizationType) {
+        case WidgetCustomization:
+            // We assume that the view v is a TextView with a compound drawable on top, and that the
+            // whole text view is centered horizontally and top aligned. We get a more precise
+            // drag point using this information
+            final TextView textView = (TextView) animView;
+            final Drawable[] drawables = textView.getCompoundDrawables();
+            final Drawable icon = drawables[1];
+            int dragPointOffsetX = 0;
+            int dragPointOffsetY = 0;
+            Rect bounds = null;
+            if (icon != null) {
+                bounds = icon.getBounds();
+                bounds.left = (v.getWidth() - bounds.right) / 2;
+                bounds.right += bounds.left;
+            }
+
+            AppWidgetProviderInfo appWidgetInfo = (AppWidgetProviderInfo) v.getTag();
+            LauncherAppWidgetInfo dragInfo = new LauncherAppWidgetInfo(appWidgetInfo.provider);
+            dragInfo.minWidth = appWidgetInfo.minWidth;
+            dragInfo.minHeight = appWidgetInfo.minHeight;
+            mDragController.startDrag(v, this, dragInfo, DragController.DRAG_ACTION_COPY, bounds);
+            return true;
+        case FolderCustomization:
+            // animate some feedback to the long press
+            animateClickFeedback(v, new Runnable() {
+                @Override
+                public void run() {
+                    // add the folder
+                    ResolveInfo resolveInfo = (ResolveInfo) animView.getTag();
+                    Intent createFolderIntent = new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER);
+                    if (resolveInfo.labelRes == R.string.group_folder) {
+                        // Create app shortcuts is a special built-in case of shortcuts
+                        createFolderIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME,
+                            getContext().getString(R.string.group_folder));
+                    } else {
+                        ComponentName name = new ComponentName(resolveInfo.activityInfo.packageName,
+                                resolveInfo.activityInfo.name);
+                        createFolderIntent.setComponent(name);
+                    }
+                    mLauncher.prepareAddItemFromHomeCustomizationDrawer();
+                    mLauncher.addLiveFolder(createFolderIntent);
+                }
+            });
+            return true;
+        case ShortcutCustomization:
+            // animate some feedback to the long press
+            animateClickFeedback(v, new Runnable() {
+                @Override
+                public void run() {
+                    // add the shortcut
+                    ResolveInfo info = (ResolveInfo) animView.getTag();
+                    Intent createShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
+                    if (info.labelRes == R.string.group_applications) {
+                        // Create app shortcuts is a special built-in case of shortcuts
+                        createShortcutIntent.putExtra(
+                                Intent.EXTRA_SHORTCUT_NAME,getContext().getString(
+                                        R.string.group_applications));
+                    } else {
+                        ComponentName name = new ComponentName(info.activityInfo.packageName, 
+                                info.activityInfo.name);
+                        createShortcutIntent.setComponent(name);
+                    }
+                    mLauncher.prepareAddItemFromHomeCustomizationDrawer();
+                    mLauncher.processShortcut(createShortcutIntent);
+                }
+            });
+            return true;
+        }
+        return false;
+    }
+
+    private int relayoutWidgets() {
+        final int widgetCount = mWidgetList.size();
+        if (widgetCount == 0) return 0;
+
+        mWidgetPages.clear();
+        ArrayList<PagedViewCellLayout.LayoutParams> page = 
+            new ArrayList<PagedViewCellLayout.LayoutParams>();
+        mWidgetPages.add(page);
+        int rowOffsetX = 0;
+        int rowOffsetY = 0;
+        int curRowHeight = 0;
+        // we only get the cell dims this way for the layout calculations because
+        // we know that we aren't going to change the dims when we construct it
+        // afterwards
+        for (int i = 0; i < widgetCount; ++i) {
+            AppWidgetProviderInfo info = mWidgetList.get(i);
+            PagedViewCellLayout.LayoutParams params;
+
+            final int cellSpanX = mTmpWidgetLayout.estimateCellHSpan(info.minWidth);
+            final int cellSpanY = mTmpWidgetLayout.estimateCellVSpan(info.minHeight);
+
+            if (((rowOffsetX + cellSpanX) <= mCellCountX) &&
+                    ((rowOffsetY + cellSpanY) <= mCellCountY)) {
+                // just add to end of current row
+                params = new PagedViewCellLayout.LayoutParams(rowOffsetX, rowOffsetY,
+                        cellSpanX, cellSpanY);
+
+                rowOffsetX += cellSpanX;
+                curRowHeight = Math.max(curRowHeight, cellSpanY);
+            } else {
+                /*
+                // fix all the items in this last row to be bottom aligned
+                int prevRowOffsetX = rowOffsetX;
+                for (int j = page.size() - 1; j >= 0; --j) {
+                    PagedViewCellLayout.LayoutParams params = page.get(j);
+                    // skip once we get to the previous row
+                    if (params.cellX > prevRowOffsetX)
+                        break;
+
+                    params.cellY += curRowHeight - params.cellVSpan;
+                    prevRowOffsetX = params.cellX;
+                }
+                */
+
+                // doesn't fit on current row, see if we can start a new row on
+                // this page
+                if ((rowOffsetY + curRowHeight + cellSpanY) > mCellCountY) {
+                    // start a new page and add this item to it
+                    page = new ArrayList<PagedViewCellLayout.LayoutParams>();
+                    mWidgetPages.add(page);
+
+                    params = new PagedViewCellLayout.LayoutParams(0, 0, cellSpanX, cellSpanY);
+                    rowOffsetX = cellSpanX;
+                    rowOffsetY = 0;
+                    curRowHeight = cellSpanY;
+                } else {
+                    // add it to the current page on this new row
+                    params = new PagedViewCellLayout.LayoutParams(0, rowOffsetY + curRowHeight,
+                            cellSpanX, cellSpanY);
+
+                    rowOffsetX = cellSpanX;
+                    rowOffsetY += curRowHeight;
+                    curRowHeight = cellSpanY;
+                }
+            }
+
+            params.setTag(info);
+            page.add(params);
+        }
+        return mWidgetPages.size();
+    }
+
+    private Drawable getWidgetIcon(PagedViewCellLayout.LayoutParams params, 
+            AppWidgetProviderInfo info) {
+        PackageManager packageManager = mLauncher.getPackageManager();
+        String packageName = info.provider.getPackageName();
+        Drawable drawable = null;
+        if (info.previewImage != 0) {
+            drawable = packageManager.getDrawable(packageName, info.previewImage, null);
+            if (drawable == null) {
+                Log.w(TAG, "Can't load icon drawable 0x" + Integer.toHexString(info.icon)
+                        + " for provider: " + info.provider);
+            } else {
+                return drawable;
+            }
+        }
+
+        // If we don't have a preview image, create a default one
+        if (drawable == null) {
+            Resources resources = mLauncher.getResources();
+
+            // Determine the size the widget will take in the layout
+            // Create a new bitmap to hold the widget preview
+            int[] dims = mTmpWidgetLayout.estimateCellDimensions(getMeasuredWidth(), 
+                    getMeasuredHeight(), params.cellHSpan, params.cellVSpan);
+            final int width = dims[0];
+            final int height = dims[1] - 35;
+            // TEMP
+            // TEMP: HACK ABOVE TO GET TEXT TO SHOW
+            // TEMP
+            Bitmap bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
+            mCanvas.setBitmap(bitmap);
+            // For some reason, we must re-set the clip rect here, otherwise it will be wrong
+            mCanvas.clipRect(0, 0, width, height, Op.REPLACE);
+
+            Drawable background = resources.getDrawable(R.drawable.default_widget_preview);
+            background.setBounds(0, 0, width, height);
+            background.draw(mCanvas);
+
+            // Draw the icon vertically centered, flush left
+            try {
+                Rect tmpRect = new Rect();
+                Drawable icon = null;
+                if (info.icon != 0) {
+                    icon = packageManager.getDrawable(packageName, info.icon, null);
+                } else {
+                    icon = resources.getDrawable(R.drawable.ic_launcher_application);
+                }
+                background.getPadding(tmpRect);
+
+                final int iconSize = Math.min(
+                        Math.min(icon.getIntrinsicWidth(), width - tmpRect.left - tmpRect.right),
+                        Math.min(icon.getIntrinsicHeight(), height - tmpRect.top - tmpRect.bottom));
+                final int left = (width / 2) - (iconSize / 2);
+                final int top = (height / 2) - (iconSize / 2);
+                icon.setBounds(new Rect(left, top, left + iconSize, top + iconSize));
+                icon.draw(mCanvas);
+            } catch (Resources.NotFoundException e) {
+                // if we can't find the icon, then just don't draw it
+            }
+
+            drawable = new FastBitmapDrawable(bitmap);
+        }
+        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
+        return drawable;
+    }
+
+    private void setupPage(PagedViewCellLayout layout) {
+        layout.setCellCount(mCellCountX, mCellCountY);
+        layout.setPadding(20, 10, 20, 0);
+    }
+
+    private void syncWidgetPages() {
+        if (mWidgetList == null) return;
+
+        // calculate the layout for all the widget pages first and ensure that
+        // we have the right number of pages
+        int numPages = relayoutWidgets();
+        int curNumPages = getChildCount();
+        // remove any extra pages after the "last" page
+        int extraPageDiff = curNumPages - numPages;
+        for (int i = 0; i < extraPageDiff; ++i) {
+            removeViewAt(numPages);
+        }
+        // add any necessary pages
+        for (int i = curNumPages; i < numPages; ++i) {
+            PagedViewCellLayout layout = new PagedViewCellLayout(getContext());
+            setupPage(layout);
+            addView(layout);
+        }
+    }
+
+    private void syncWidgetPageItems(int page) {
+        // ensure that we have the right number of items on the pages
+        PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page);
+        final ArrayList<PagedViewCellLayout.LayoutParams> list = mWidgetPages.get(page);
+        final int count = list.size();
+        layout.removeAllViews();
+        for (int i = 0; i < count; ++i) {
+            PagedViewCellLayout.LayoutParams params = list.get(i);
+            AppWidgetProviderInfo info = (AppWidgetProviderInfo) params.getTag();
+            final Drawable icon = getWidgetIcon(params, info);
+            TextView text = (TextView) mInflater.inflate(R.layout.customize_paged_view_widget, 
+                    layout, false);
+            text.setCompoundDrawablesWithIntrinsicBounds(null, icon, null, null);
+            text.setText(info.label);
+            text.setTag(info);
+            text.setOnLongClickListener(this);
+
+            layout.addViewToCellLayout(text, -1, mWidgetList.indexOf(info), params);
+        }
+    }
+
+    private void syncListPages(List<ResolveInfo> list) {
+        // ensure that we have the right number of pages
+        int numPages = (int) Math.ceil((float) list.size() / (mCellCountX * mCellCountY));
+        int curNumPages = getChildCount();
+        // remove any extra pages after the "last" page
+        int extraPageDiff = curNumPages - numPages;
+        for (int i = 0; i < extraPageDiff; ++i) {
+            removeViewAt(numPages);
+        }
+        // add any necessary pages
+        for (int i = curNumPages; i < numPages; ++i) {
+            PagedViewCellLayout layout = new PagedViewCellLayout(getContext());
+            setupPage(layout);
+            addView(layout);
+        }
+    }
+
+    private void syncListPageItems(int page, List<ResolveInfo> list) {
+        // ensure that we have the right number of items on the pages
+        int numCells = mCellCountX * mCellCountY;
+        int startIndex = page * numCells;
+        int endIndex = Math.min(startIndex + numCells, list.size());
+        PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page);
+        // TODO: we can optimize by just re-applying to existing views
+        layout.removeAllViews();
+        for (int i = startIndex; i < endIndex; ++i) {
+            ResolveInfo info = list.get(i);
+            PagedViewIcon icon = (PagedViewIcon) mInflater.inflate(
+                    R.layout.customize_paged_view_item, layout, false);
+            icon.applyFromResolveInfo(info, mPackageManager, mPageViewIconCache);
+            icon.setOnLongClickListener(this);
+
+            final int index = i - startIndex;
+            final int x = index % mCellCountX;
+            final int y = index / mCellCountX;
+            layout.addViewToCellLayout(icon, -1, i, new PagedViewCellLayout.LayoutParams(x,y, 1,1));
+        }
+    }
+
+    private void syncWallpaperPages() {
+        // NOT CURRENTLY IMPLEMENTED
+        // ensure that we have the right number of pages
+        int numPages = 1;
+        int curNumPages = getChildCount();
+        // remove any extra pages after the "last" page
+        int extraPageDiff = curNumPages - numPages;
+        for (int i = 0; i < extraPageDiff; ++i) {
+            removeViewAt(numPages);
+        }
+        // add any necessary pages
+        for (int i = curNumPages; i < numPages; ++i) {
+            PagedViewCellLayout layout = new PagedViewCellLayout(getContext());
+            setupPage(layout);
+            addView(layout);
+        }
+    }
+
+    private void syncWallpaperPageItems(int page) {
+        PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page);
+        layout.removeAllViews();
+
+        TextView text = (TextView) mInflater.inflate(
+                R.layout.customize_paged_view_wallpaper_placeholder, layout, false);
+        // NOTE: this is just place holder text until MikeJurka implements wallpaper picker
+        text.setText("Wallpaper customization coming soon!");
+
+        layout.addViewToCellLayout(text, -1, 0, new PagedViewCellLayout.LayoutParams(0, 0, 3, 1));
+    }
+
+    @Override
+    public void syncPages() {
+        switch (mCustomizationType) {
+        case WidgetCustomization:
+            syncWidgetPages();
+            break;
+        case FolderCustomization:
+            syncListPages(mFolderList);
+            break;
+        case ShortcutCustomization:
+            syncListPages(mShortcutList);
+            break;
+        case WallpaperCustomization:
+            syncWallpaperPages();
+            break;
+        default:
+            removeAllViews();
+            setCurrentPage(0);
+            break;
+        }
+
+        // only try and center the page if there is one page
+        final int childCount = getChildCount();
+        if (childCount == 1) {
+            PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(0);
+            layout.enableCenteredContent(true);
+        } else {
+            for (int i = 0; i < childCount; ++i) {
+                PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(i);
+                layout.enableCenteredContent(false);
+            }
+        }
+
+        // bound the current page
+        setCurrentPage(Math.max(0, Math.min(childCount - 1, getCurrentPage())));
+    }
+
+    @Override
+    public void syncPageItems(int page) {
+        switch (mCustomizationType) {
+        case WidgetCustomization:
+            syncWidgetPageItems(page);
+            break;
+        case FolderCustomization:
+            syncListPageItems(page, mFolderList);
+            break;
+        case ShortcutCustomization:
+            syncListPageItems(page, mShortcutList);
+            break;
+        case WallpaperCustomization:
+            syncWallpaperPageItems(page);
+            break;
+        }
+    }
+}
diff --git a/src/com/android/launcher2/DeferredHandler.java b/src/com/android/launcher2/DeferredHandler.java
index 7801642..0323c7f 100644
--- a/src/com/android/launcher2/DeferredHandler.java
+++ b/src/com/android/launcher2/DeferredHandler.java
@@ -16,13 +16,12 @@
 
 package com.android.launcher2;
 
+import java.util.LinkedList;
+
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.MessageQueue;
-import android.util.Log;
-
-import java.util.LinkedList;
 
 /**
  * Queue of things to run on a looper thread.  Items posted with {@link #post} will not
diff --git a/src/com/android/launcher2/DeleteZone.java b/src/com/android/launcher2/DeleteZone.java
index 3a6c63d..b11b1dd 100644
--- a/src/com/android/launcher2/DeleteZone.java
+++ b/src/com/android/launcher2/DeleteZone.java
@@ -254,4 +254,10 @@
             return false;
         }
     }
+
+    @Override
+    public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset,
+            DragView dragView, Object dragInfo) {
+        return null;
+    }
 }
diff --git a/src/com/android/launcher2/DragController.java b/src/com/android/launcher2/DragController.java
index b4f972b..e18470a 100644
--- a/src/com/android/launcher2/DragController.java
+++ b/src/com/android/launcher2/DragController.java
@@ -99,7 +99,7 @@
     /** Who can receive drop events */
     private ArrayList<DropTarget> mDropTargets = new ArrayList<DropTarget>();
 
-    private DragListener mListener;
+    private ArrayList<DragListener> mListeners = new ArrayList<DragListener>();
 
     /** The window token used as the parent for the DragView. */
     private IBinder mWindowToken;
@@ -151,7 +151,7 @@
 
     /**
      * Starts a drag.
-     * 
+     *
      * @param v The view that is being dragged
      * @param source An object representing where the drag originated
      * @param dragInfo The data associated with the object that is being dragged
@@ -159,6 +159,22 @@
      *        {@link #DRAG_ACTION_COPY}
      */
     public void startDrag(View v, DragSource source, Object dragInfo, int dragAction) {
+        startDrag(v, source, dragInfo, dragAction, null);
+    }
+
+    /**
+     * Starts a drag.
+     *
+     * @param v The view that is being dragged
+     * @param source An object representing where the drag originated
+     * @param dragInfo The data associated with the object that is being dragged
+     * @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or
+     *        {@link #DRAG_ACTION_COPY}
+     * @param dragRegion Coordinates within the bitmap b for the position of item being dragged.
+     *          Makes dragging feel more precise, e.g. you can clip out a transparent border
+     */
+    public void startDrag(View v, DragSource source, Object dragInfo, int dragAction,
+            Rect dragRegion) {
         mOriginator = v;
 
         Bitmap b = getViewBitmap(v);
@@ -174,7 +190,7 @@
         int screenY = loc[1];
 
         startDrag(b, screenX, screenY, 0, 0, b.getWidth(), b.getHeight(),
-                source, dragInfo, dragAction);
+                source, dragInfo, dragAction, dragRegion);
 
         b.recycle();
 
@@ -185,7 +201,7 @@
 
     /**
      * Starts a drag.
-     * 
+     *
      * @param b The bitmap to display as the drag image.  It will be re-scaled to the
      *          enlarged size.
      * @param screenX The x position on screen of the left-top of the bitmap.
@@ -202,6 +218,31 @@
     public void startDrag(Bitmap b, int screenX, int screenY,
             int textureLeft, int textureTop, int textureWidth, int textureHeight,
             DragSource source, Object dragInfo, int dragAction) {
+        startDrag(b, screenX, screenY, textureLeft, textureTop, textureWidth, textureHeight,
+                source, dragInfo, dragAction, null);
+    }
+
+    /**
+     * Starts a drag.
+     *
+     * @param b The bitmap to display as the drag image.  It will be re-scaled to the
+     *          enlarged size.
+     * @param screenX The x position on screen of the left-top of the bitmap.
+     * @param screenY The y position on screen of the left-top of the bitmap.
+     * @param textureLeft The left edge of the region inside b to use.
+     * @param textureTop The top edge of the region inside b to use.
+     * @param textureWidth The width of the region inside b to use.
+     * @param textureHeight The height of the region inside b to use.
+     * @param source An object representing where the drag originated
+     * @param dragInfo The data associated with the object that is being dragged
+     * @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or
+     *        {@link #DRAG_ACTION_COPY}
+     * @param dragRegion Coordinates within the bitmap b for the position of item being dragged.
+     *          Makes dragging feel more precise, e.g. you can clip out a transparent border
+     */
+    public void startDrag(Bitmap b, int screenX, int screenY,
+            int textureLeft, int textureTop, int textureWidth, int textureHeight,
+            DragSource source, Object dragInfo, int dragAction, Rect dragRegion) {
         if (PROFILE_DRAWING_DURING_DRAG) {
             android.os.Debug.startMethodTracing("Launcher");
         }
@@ -213,15 +254,17 @@
         }
         mInputMethodManager.hideSoftInputFromWindow(mWindowToken, 0);
 
-        if (mListener != null) {
-            mListener.onDragStart(source, dragInfo, dragAction);
+        for (DragListener listener : mListeners) {
+            listener.onDragStart(source, dragInfo, dragAction);
         }
 
         int registrationX = ((int)mMotionDownX) - screenX;
         int registrationY = ((int)mMotionDownY) - screenY;
 
-        mTouchOffsetX = mMotionDownX - screenX;
-        mTouchOffsetY = mMotionDownY - screenY;
+        final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left;
+        final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top;
+        mTouchOffsetX = mMotionDownX - screenX - dragRegionLeft;
+        mTouchOffsetY = mMotionDownY - screenY - dragRegionTop;
 
         mDragging = true;
         mDragSource = source;
@@ -231,6 +274,12 @@
 
         DragView dragView = mDragView = new DragView(mContext, b, registrationX, registrationY,
                 textureLeft, textureTop, textureWidth, textureHeight);
+
+        if (dragRegion != null) {
+            dragView.setDragRegion(dragRegionLeft, dragRegion.top,
+                    dragRegion.right - dragRegionLeft, dragRegion.bottom - dragRegionTop);
+        }
+
         dragView.show(mWindowToken, (int)mMotionDownX, (int)mMotionDownY);
     }
 
@@ -297,8 +346,8 @@
             if (mOriginator != null) {
                 mOriginator.setVisibility(View.VISIBLE);
             }
-            if (mListener != null) {
-                mListener.onDragEnd();
+            for (DragListener listener : mListeners) {
+                listener.onDragEnd();
             }
             if (mDragView != null) {
                 mDragView.remove();
@@ -395,6 +444,13 @@
             final int[] coordinates = mCoordinatesTemp;
             DropTarget dropTarget = findDropTarget(screenX, screenY, coordinates);
             if (dropTarget != null) {
+                DropTarget delegate = dropTarget.getDropTargetDelegate(
+                        mDragSource, coordinates[0], coordinates[1],
+                        (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);
+                if (delegate != null) {
+                    dropTarget = delegate;
+                }
+
                 if (mLastDropTarget == dropTarget) {
                     dropTarget.onDragOver(mDragSource, coordinates[0], coordinates[1],
                         (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);
@@ -482,13 +538,25 @@
         final ArrayList<DropTarget> dropTargets = mDropTargets;
         final int count = dropTargets.size();
         for (int i=count-1; i>=0; i--) {
-            final DropTarget target = dropTargets.get(i);
+            DropTarget target = dropTargets.get(i);
             target.getHitRect(r);
+
+            // Convert the hit rect to screen coordinates
             target.getLocationOnScreen(dropCoordinates);
             r.offset(dropCoordinates[0] - target.getLeft(), dropCoordinates[1] - target.getTop());
+
             if (r.contains(x, y)) {
+                DropTarget delegate = target.getDropTargetDelegate(mDragSource,
+                        x, y, (int)mTouchOffsetX, (int)mTouchOffsetY, mDragView, mDragInfo);
+                if (delegate != null) {
+                    target = delegate;
+                    target.getLocationOnScreen(dropCoordinates);
+                }
+
+                // Make dropCoordinates relative to the DropTarget
                 dropCoordinates[0] = x - dropCoordinates[0];
                 dropCoordinates[1] = y - dropCoordinates[1];
+
                 return target;
             }
         }
@@ -528,15 +596,15 @@
     /**
      * Sets the drag listner which will be notified when a drag starts or ends.
      */
-    public void setDragListener(DragListener l) {
-        mListener = l;
+    public void addDragListener(DragListener l) {
+        mListeners.add(l);
     }
 
     /**
      * Remove a previously installed drag listener.
      */
     public void removeDragListener(DragListener l) {
-        mListener = null;
+        mListeners.remove(l);
     }
 
     /**
diff --git a/src/com/android/launcher2/DragLayer.java b/src/com/android/launcher2/DragLayer.java
index c683207..ab71670 100644
--- a/src/com/android/launcher2/DragLayer.java
+++ b/src/com/android/launcher2/DragLayer.java
@@ -18,13 +18,13 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
-import android.view.MotionEvent;
 import android.view.KeyEvent;
+import android.view.MotionEvent;
 import android.view.View;
 import android.widget.FrameLayout;
 
 /**
- * A ViewGroup that coordinated dragging across its dscendants
+ * A ViewGroup that coordinates dragging across its descendants
  */
 public class DragLayer extends FrameLayout {
     DragController mDragController;
@@ -33,7 +33,7 @@
      * Used to create a new DragLayer from XML.
      *
      * @param context The application's context.
-     * @param attrs The attribtues set containing the Workspace's customization values.
+     * @param attrs The attributes set containing the Workspace's customization values.
      */
     public DragLayer(Context context, AttributeSet attrs) {
         super(context, attrs);
diff --git a/src/com/android/launcher2/DragView.java b/src/com/android/launcher2/DragView.java
index 248712e..41e76f0 100644
--- a/src/com/android/launcher2/DragView.java
+++ b/src/com/android/launcher2/DragView.java
@@ -18,20 +18,15 @@
 package com.android.launcher2;
 
 import android.content.Context;
-import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
-import android.graphics.Point;
 import android.os.IBinder;
-import android.util.AttributeSet;
-import android.util.Log;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.KeyEvent;
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
 
@@ -44,6 +39,11 @@
     private int mRegistrationX;
     private int mRegistrationY;
 
+    private int mDragRegionLeft = 0;
+    private int mDragRegionTop = 0;
+    private int mDragRegionWidth;
+    private int mDragRegionHeight;
+
     SymmetricalLinearTween mTween;
     private float mScale;
     private float mAnimationScale = 1.0f;
@@ -76,12 +76,37 @@
         scale.setScale(scaleFactor, scaleFactor);
 
         mBitmap = Bitmap.createBitmap(bitmap, left, top, width, height, scale, true);
+        mDragRegionWidth = width;
+        mDragRegionHeight = height;
 
         // The point in our scaled bitmap that the touch events are located
         mRegistrationX = registrationX + (DRAG_SCALE / 2);
         mRegistrationY = registrationY + (DRAG_SCALE / 2);
     }
 
+    public void setDragRegion(int left, int top, int width, int height) {
+        mDragRegionLeft = left;
+        mDragRegionTop = top;
+        mDragRegionWidth = width;
+        mDragRegionHeight = height;
+    }
+
+    public int getDragRegionLeft() {
+        return mDragRegionLeft;
+    }
+
+    public int getDragRegionTop() {
+        return mDragRegionTop;
+    }
+
+    public int getDragRegionWidth() {
+        return mDragRegionWidth;
+    }
+
+    public int getDragRegionHeight() {
+        return mDragRegionHeight;
+    }
+
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         setMeasuredDimension(mBitmap.getWidth(), mBitmap.getHeight());
diff --git a/src/com/android/launcher2/DropTarget.java b/src/com/android/launcher2/DropTarget.java
index 72eb330..e33ec9a 100644
--- a/src/com/android/launcher2/DropTarget.java
+++ b/src/com/android/launcher2/DropTarget.java
@@ -51,9 +51,28 @@
             DragView dragView, Object dragInfo);
 
     /**
+     * Allows a DropTarget to delegate drag and drop events to another object.
+     *
+     * Most subclasses will should just return null from this method.
+     *
+     * @param source DragSource where the drag started
+     * @param x X coordinate of the drop location
+     * @param y Y coordinate of the drop location
+     * @param xOffset Horizontal offset with the object being dragged where the original
+     *          touch happened
+     * @param yOffset Vertical offset with the object being dragged where the original
+     *          touch happened
+     * @param dragView The DragView that's being dragged around on screen.
+     * @param dragInfo Data associated with the object being dragged
+     *
+     * @return The DropTarget to delegate to, or null to not delegate to another object.
+     */
+    DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset,
+            DragView dragView, Object dragInfo);
+
+    /**
      * Check if a drop action can occur at, or near, the requested location.
-     * This may be called repeatedly during a drag, so any calls should return
-     * quickly.
+     * This will be called just before onDrop.
      * 
      * @param source DragSource where the drag started
      * @param x X coordinate of the drop location
diff --git a/src/com/android/launcher2/FastBitmapDrawable.java b/src/com/android/launcher2/FastBitmapDrawable.java
index 226d6d8..1cafa09 100644
--- a/src/com/android/launcher2/FastBitmapDrawable.java
+++ b/src/com/android/launcher2/FastBitmapDrawable.java
@@ -17,6 +17,7 @@
 package com.android.launcher2;
 
 import android.graphics.drawable.Drawable;
+import android.graphics.Paint;
 import android.graphics.PixelFormat;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -26,6 +27,7 @@
     private Bitmap mBitmap;
     private int mWidth;
     private int mHeight;
+    private final Paint mPaint = new Paint();
 
     FastBitmapDrawable(Bitmap b) {
         mBitmap = b;
@@ -39,7 +41,7 @@
 
     @Override
     public void draw(Canvas canvas) {
-        canvas.drawBitmap(mBitmap, 0.0f, 0.0f, null);
+        canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint);
     }
 
     @Override
@@ -49,6 +51,7 @@
 
     @Override
     public void setAlpha(int alpha) {
+        mPaint.setAlpha(alpha);
     }
 
     @Override
diff --git a/src/com/android/launcher2/Folder.java b/src/com/android/launcher2/Folder.java
index 7ff8328..018b284 100644
--- a/src/com/android/launcher2/Folder.java
+++ b/src/com/android/launcher2/Folder.java
@@ -21,11 +21,11 @@
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.widget.AbsListView;
 import android.widget.AdapterView;
+import android.widget.BaseAdapter;
 import android.widget.Button;
 import android.widget.LinearLayout;
-import android.widget.AbsListView;
-import android.widget.BaseAdapter;
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.AdapterView.OnItemLongClickListener;
 
@@ -148,7 +148,7 @@
 
     void onClose() {
         final Workspace workspace = mLauncher.getWorkspace();
-        workspace.getChildAt(workspace.getCurrentScreen()).requestFocus();
+        workspace.getChildAt(workspace.getCurrentPage()).requestFocus();
     }
 
     void bind(FolderInfo info) {
diff --git a/src/com/android/launcher2/FolderIcon.java b/src/com/android/launcher2/FolderIcon.java
index 0013644..78a9516 100644
--- a/src/com/android/launcher2/FolderIcon.java
+++ b/src/com/android/launcher2/FolderIcon.java
@@ -91,7 +91,9 @@
 
     public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset,
             DragView dragView, Object dragInfo) {
-        setCompoundDrawablesWithIntrinsicBounds(null, mOpenIcon, null, null);
+        if (acceptDrop(source, x, y, xOffset, yOffset, dragView, dragInfo)) {
+            setCompoundDrawablesWithIntrinsicBounds(null, mOpenIcon, null, null);
+        }
     }
 
     public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
@@ -102,4 +104,10 @@
             DragView dragView, Object dragInfo) {
         setCompoundDrawablesWithIntrinsicBounds(null, mCloseIcon, null, null);
     }
+
+    @Override
+    public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset,
+            DragView dragView, Object dragInfo) {
+        return null;
+    }
 }
diff --git a/src/com/android/launcher2/InstallShortcutReceiver.java b/src/com/android/launcher2/InstallShortcutReceiver.java
index 3fc568b..992bab1 100644
--- a/src/com/android/launcher2/InstallShortcutReceiver.java
+++ b/src/com/android/launcher2/InstallShortcutReceiver.java
@@ -16,11 +16,11 @@
 
 package com.android.launcher2;
 
+import java.util.ArrayList;
+
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.ContentResolver;
-import android.database.Cursor;
 import android.widget.Toast;
 
 import com.android.launcher.R;
@@ -84,40 +84,26 @@
     }
 
     private static boolean findEmptyCell(Context context, int[] xy, int screen) {
-        final int xCount = Launcher.NUMBER_CELLS_X;
-        final int yCount = Launcher.NUMBER_CELLS_Y;
-
+        final int xCount = LauncherModel.getCellCountX();
+        final int yCount = LauncherModel.getCellCountY();
         boolean[][] occupied = new boolean[xCount][yCount];
 
-        final ContentResolver cr = context.getContentResolver();
-        Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
-            new String[] { LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY,
-                    LauncherSettings.Favorites.SPANX, LauncherSettings.Favorites.SPANY },
-            LauncherSettings.Favorites.SCREEN + "=?",
-            new String[] { String.valueOf(screen) }, null);
-
-        final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
-        final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
-        final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
-        final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
-
-        try {
-            while (c.moveToNext()) {
-                int cellX = c.getInt(cellXIndex);
-                int cellY = c.getInt(cellYIndex);
-                int spanX = c.getInt(spanXIndex);
-                int spanY = c.getInt(spanYIndex);
-
+        ArrayList<ItemInfo> items = LauncherModel.getItemsInLocalCoordinates(context);
+        ItemInfo item = null;
+        int cellX, cellY, spanX, spanY;
+        for (int i = 0; i < items.size(); ++i) {
+            item = items.get(i);
+            if (item.screen == screen) {
+                cellX = item.cellX;
+                cellY = item.cellY;
+                spanX = item.spanX;
+                spanY = item.spanY;
                 for (int x = cellX; x < cellX + spanX && x < xCount; x++) {
                     for (int y = cellY; y < cellY + spanY && y < yCount; y++) {
                         occupied[x][y] = true;
                     }
                 }
             }
-        } catch (Exception e) {
-            return false;
-        } finally {
-            c.close();
         }
 
         return CellLayout.findVacantCell(xy, 1, 1, xCount, yCount, occupied);
diff --git a/src/com/android/launcher2/ItemInfo.java b/src/com/android/launcher2/ItemInfo.java
index a96d9ae..dc45750 100644
--- a/src/com/android/launcher2/ItemInfo.java
+++ b/src/com/android/launcher2/ItemInfo.java
@@ -112,6 +112,11 @@
         }
     }
 
+    void updateValuesWithCoordinates(ContentValues values, int cellX, int cellY) {
+        values.put(LauncherSettings.Favorites.CELLX, cellX);
+        values.put(LauncherSettings.Favorites.CELLY, cellY);
+    }
+
     static byte[] flattenBitmap(Bitmap bitmap) {
         // Try go guesstimate how much space the icon will take when serialized
         // to avoid unnecessary allocations/copies during the write.
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index 28a6712..4e33985 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -1,3 +1,4 @@
+
 /*
  * Copyright (C) 2008 The Android Open Source Project
  *
@@ -17,13 +18,22 @@
 package com.android.launcher2;
 
 import com.android.common.Search;
+import com.android.launcher.R;
 
+import android.animation.Animatable;
+import android.animation.AnimatableListenerAdapter;
+import android.animation.Animator;
+import android.animation.PropertyAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.Sequencer;
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.SearchManager;
 import android.app.StatusBarManager;
 import android.app.WallpaperManager;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -35,16 +45,18 @@
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
-import android.graphics.Rect;
 import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
+import android.graphics.Color;
+import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
@@ -53,6 +65,7 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.provider.LiveFolders;
+import android.provider.Settings;
 import android.text.Selection;
 import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
@@ -64,34 +77,40 @@
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
+import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.View.OnLongClickListener;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.PopupWindow;
+import android.widget.RelativeLayout;
+import android.widget.TabHost;
+import android.widget.TabHost.OnTabChangeListener;
+import android.widget.TabHost.TabContentFactory;
+import android.widget.TabWidget;
 import android.widget.TextView;
 import android.widget.Toast;
-import android.widget.ImageView;
-import android.widget.PopupWindow;
-import android.widget.LinearLayout;
-import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProviderInfo;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.HashMap;
+import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.io.DataInputStream;
-
-import com.android.launcher.R;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
 
 /**
  * Default launcher application.
  */
 public final class Launcher extends Activity
-        implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {
+        implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
+                   AllAppsView.Watcher, View.OnTouchListener {
     static final String TAG = "Launcher";
     static final boolean LOGD = false;
 
@@ -123,8 +142,6 @@
 
     static final int SCREEN_COUNT = 5;
     static final int DEFAULT_SCREEN = 2;
-    static final int NUMBER_CELLS_X = 4;
-    static final int NUMBER_CELLS_Y = 4;
 
     static final int DIALOG_CREATE_SHORTCUT = 1;
     static final int DIALOG_RENAME_FOLDER = 2;
@@ -158,6 +175,17 @@
     // Type: long
     private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME_ID = "launcher.rename_folder_id";
 
+    // tags for the customization tabs
+    private static final String WIDGETS_TAG = "widgets";
+    private static final String FOLDERS_TAG = "folders";
+    private static final String SHORTCUTS_TAG = "shortcuts";
+    private static final String WALLPAPERS_TAG = "wallpapers";
+
+    private static final String TOOLBAR_ICON_METADATA_NAME = "com.android.launcher.toolbar_icon";
+
+    /** The different states that Launcher can be in. */
+    private enum State { WORKSPACE, ALL_APPS, CUSTOMIZE, OVERVIEW };
+
     static final int APPWIDGET_HOST_ID = 1024;
 
     private static final Object sLock = new Object();
@@ -176,6 +204,7 @@
     private LauncherAppWidgetHost mAppWidgetHost;
 
     private CellLayout.CellInfo mAddItemCellInfo;
+    private int[] mAddItemCoordinates;
     private CellLayout.CellInfo mMenuAddInfo;
     private final int[] mCellCoordinates = new int[2];
     private FolderInfo mFolderInfo;
@@ -183,6 +212,8 @@
     private DeleteZone mDeleteZone;
     private HandleView mHandleView;
     private AllAppsView mAllAppsGrid;
+    private TabHost mHomeCustomizationDrawer;
+    private CustomizePagedView mCustomizePagedView;
 
     private Bundle mSavedState;
 
@@ -208,16 +239,23 @@
     private ImageView mNextView;
 
     // Hotseats (quick-launch icons next to AllApps)
-    private static final int NUM_HOTSEATS = 2;
     private String[] mHotseatConfig = null;
     private Intent[] mHotseats = null;
     private Drawable[] mHotseatIcons = null;
     private CharSequence[] mHotseatLabels = null;
 
+    private Intent mAppMarketIntent = null;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        if (LauncherApplication.isInPlaceRotationEnabled()) {
+            // hide the status bar (temporary until we get the status bar design figured out)
+            getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
+            this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
+        }
+
         LauncherApplication app = ((LauncherApplication)getApplication());
         mModel = app.setLauncher(this);
         mIconCache = app.getIconCache();
@@ -235,8 +273,80 @@
         loadHotseats();
         checkForLocaleChange();
         setWallpaperDimension();
-
         setContentView(R.layout.launcher);
+        mHomeCustomizationDrawer = (TabHost) findViewById(com.android.internal.R.id.tabhost);
+        if (mHomeCustomizationDrawer != null) {
+            mHomeCustomizationDrawer.setup();
+
+            // share the same customization workspace across all the tabs
+            mCustomizePagedView = new CustomizePagedView(this);
+            TabContentFactory contentFactory = new TabContentFactory() {
+                public View createTabContent(String tag) {
+                    return mCustomizePagedView;
+                }
+            };
+
+            String widgetsLabel = getString(R.string.widgets_tab_label);
+            mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec(WIDGETS_TAG)
+                    .setIndicator(widgetsLabel).setContent(contentFactory));
+            String foldersLabel = getString(R.string.folders_tab_label);
+            mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec(FOLDERS_TAG)
+                    .setIndicator(foldersLabel).setContent(contentFactory));
+            String shortcutsLabel = getString(R.string.shortcuts_tab_label);
+            mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec(SHORTCUTS_TAG)
+                    .setIndicator(shortcutsLabel).setContent(contentFactory));
+            String wallpapersLabel = getString(R.string.wallpapers_tab_label);
+            mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec(WALLPAPERS_TAG)
+                    .setIndicator(wallpapersLabel).setContent(contentFactory));
+
+            // TEMP: just styling the tab widget to be a bit nicer until we get the actual
+            // new assets
+            TabWidget tabWidget = mHomeCustomizationDrawer.getTabWidget();
+            for (int i = 0; i < tabWidget.getChildCount(); ++i) {
+                RelativeLayout tab = (RelativeLayout) tabWidget.getChildTabViewAt(i);
+                TextView text = (TextView) tab.getChildAt(1);
+                text.setTextSize(20.0f);
+                text.setPadding(20, 0, 20, 0);
+                text.setShadowLayer(1.0f, 0.0f, 1.0f, Color.BLACK);
+                tab.setBackgroundDrawable(null);
+            }
+
+            mHomeCustomizationDrawer.setOnTabChangedListener(new OnTabChangeListener() {
+                public void onTabChanged(String tabId) {
+                    // animate the changing of the tab content by fading pages in and out
+                    final int duration = 150;
+                    final float alpha = mCustomizePagedView.getAlpha();
+                    Animator alphaAnim = new PropertyAnimator(duration, mCustomizePagedView, 
+                            "alpha", alpha, 0.0f);
+                    alphaAnim.addListener(new AnimatableListenerAdapter() {
+                        public void onAnimationEnd(Animatable animation) {
+                            String tag = mHomeCustomizationDrawer.getCurrentTabTag();
+                            if (tag == WIDGETS_TAG) {
+                                mCustomizePagedView.setCustomizationFilter(
+                                    CustomizePagedView.CustomizationType.WidgetCustomization);
+                            } else if (tag == FOLDERS_TAG) {
+                                mCustomizePagedView.setCustomizationFilter(
+                                    CustomizePagedView.CustomizationType.FolderCustomization);
+                            } else if (tag == SHORTCUTS_TAG) {
+                                mCustomizePagedView.setCustomizationFilter(
+                                    CustomizePagedView.CustomizationType.ShortcutCustomization);
+                            } else if (tag == WALLPAPERS_TAG) {
+                                mCustomizePagedView.setCustomizationFilter(
+                                    CustomizePagedView.CustomizationType.WallpaperCustomization);
+                            }
+
+                            final float alpha = mCustomizePagedView.getAlpha();
+                            Animator alphaAnim = new PropertyAnimator(duration, mCustomizePagedView, 
+                                    "alpha", alpha, 1.0f);
+                            alphaAnim.start();
+                        }
+                    });
+                    alphaAnim.start();
+                }
+            });
+    
+            mHomeCustomizationDrawer.setCurrentTab(0);
+        }
         setupViews();
 
         registerContentObservers();
@@ -262,6 +372,12 @@
         registerReceiver(mCloseSystemDialogsReceiver, filter);
     }
 
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        // TODO Auto-generated method stub
+        super.onConfigurationChanged(newConfig);
+    }
+
     private void checkForLocaleChange() {
         if (sLocaleConfiguration == null) {
             new AsyncTask<Void, Void, LocaleConfiguration>() {
@@ -379,11 +495,12 @@
         WallpaperManager wpm = (WallpaperManager)getSystemService(WALLPAPER_SERVICE);
 
         Display display = getWindowManager().getDefaultDisplay();
-        boolean isPortrait = display.getWidth() < display.getHeight();
-
-        final int width = isPortrait ? display.getWidth() : display.getHeight();
-        final int height = isPortrait ? display.getHeight() : display.getWidth();
-        wpm.suggestDesiredDimensions(width * WALLPAPER_SCREENS_SPAN, height);
+        // TODO: Put back when we decide about scrolling the wallpaper
+        // boolean isPortrait = display.getWidth() < display.getHeight();
+        // final int width = isPortrait ? display.getWidth() : display.getHeight();
+        // final int height = isPortrait ? display.getHeight() : display.getWidth();
+        wpm.suggestDesiredDimensions(Math.max(display.getWidth(), display.getHeight()),
+                Math.max(display.getWidth(), display.getHeight()));
     }
 
     // Note: This doesn't do all the client-id magic that BrowserProvider does
@@ -442,7 +559,7 @@
                 // note: if the user launches this without a default set, she
                 // will always be taken to the default URL above; this is
                 // unavoidable as we must specify a valid URL in order for the
-                // chooser to appear, and once the user selects something, that 
+                // chooser to appear, and once the user selects something, that
                 // URL is unavoidably sent to the chosen app.
             } else {
                 try {
@@ -452,7 +569,7 @@
                     // bogus; leave intent=null
                 }
             }
-            
+
             if (intent == null) {
                 mHotseats[i] = null;
                 mHotseatLabels[i] = getText(R.string.activity_not_found);
@@ -460,15 +577,15 @@
             }
 
             if (LOGD) {
-                Log.d(TAG, "loadHotseats: hotseat " + i 
-                    + " initial intent=[" 
+                Log.d(TAG, "loadHotseats: hotseat " + i
+                    + " initial intent=["
                     + intent.toUri(Intent.URI_INTENT_SCHEME)
                     + "]");
             }
 
             ResolveInfo bestMatch = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
             List<ResolveInfo> allMatches = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
-            if (LOGD) { 
+            if (LOGD) {
                 Log.d(TAG, "Best match for intent: " + bestMatch);
                 Log.d(TAG, "All matches: ");
                 for (ResolveInfo ri : allMatches) {
@@ -477,8 +594,8 @@
             }
             // did this resolve to a single app, or the resolver?
             if (allMatches.size() == 0 || bestMatch == null) {
-                // can't find any activity to handle this. let's leave the 
-                // intent as-is and let Launcher show a toast when it fails 
+                // can't find any activity to handle this. let's leave the
+                // intent as-is and let Launcher show a toast when it fails
                 // to launch.
                 mHotseats[i] = intent;
 
@@ -494,7 +611,7 @@
                         break;
                     }
                 }
-                
+
                 if (!found) {
                     if (LOGD) Log.d(TAG, "Multiple options, no default yet");
                     // the bestMatch is probably the ResolveActivity, meaning the
@@ -519,8 +636,8 @@
             }
 
             if (LOGD) {
-                Log.d(TAG, "loadHotseats: hotseat " + i 
-                    + " final intent=[" 
+                Log.d(TAG, "loadHotseats: hotseat " + i
+                    + " final intent=["
                     + ((mHotseats[i] == null)
                         ? "null"
                         : mHotseats[i].toUri(Intent.URI_INTENT_SCHEME))
@@ -559,10 +676,11 @@
                     completeAddLiveFolder(data, mAddItemCellInfo);
                     break;
                 case REQUEST_PICK_APPWIDGET:
-                    addAppWidget(data);
+                    addAppWidgetFromPick(data);
                     break;
                 case REQUEST_CREATE_APPWIDGET:
-                    completeAddAppWidget(data, mAddItemCellInfo);
+                    int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
+                    completeAddAppWidget(appWidgetId, mAddItemCellInfo);
                     break;
                 case REQUEST_PICK_WALLPAPER:
                     // We just wanted the activity result here so we can clear mWaitingForResult
@@ -590,13 +708,21 @@
             mModel.startLoader(this, true);
             mRestoring = false;
         }
+        // When we resume Launcher, a different Activity might be responsible for the app
+        // market intent, so refresh the icon
+        updateAppMarketIcon();
     }
 
     @Override
     protected void onPause() {
         super.onPause();
-        dismissPreview(mPreviousView);
-        dismissPreview(mNextView);
+        // Some launcher layouts don't have a previous and next view
+        if (mPreviousView != null) {
+            dismissPreview(mPreviousView);
+        }
+        if (mNextView != null) {
+            dismissPreview(mNextView);
+        }
         mDragController.cancelDrag();
     }
 
@@ -687,11 +813,12 @@
 
         final int currentScreen = savedState.getInt(RUNTIME_STATE_CURRENT_SCREEN, -1);
         if (currentScreen > -1) {
-            mWorkspace.setCurrentScreen(currentScreen);
+            mWorkspace.setCurrentPage(currentScreen);
         }
 
         final int addScreen = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SCREEN, -1);
         if (addScreen > -1) {
+            mAddItemCoordinates = null;
             mAddItemCellInfo = new CellLayout.CellInfo();
             final CellLayout.CellInfo addItemCellInfo = mAddItemCellInfo;
             addItemCellInfo.valid = true;
@@ -700,7 +827,7 @@
             addItemCellInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y);
             addItemCellInfo.spanX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_X);
             addItemCellInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y);
-            addItemCellInfo.findVacantCellsFromOccupied(
+            addItemCellInfo.updateOccupiedCells(
                     savedState.getBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS),
                     savedState.getInt(RUNTIME_STATE_PENDING_ADD_COUNT_X),
                     savedState.getInt(RUNTIME_STATE_PENDING_ADD_COUNT_Y));
@@ -719,7 +846,7 @@
      * Finds all the views we need and configure them properly.
      */
     private void setupViews() {
-        DragController dragController = mDragController;
+        final DragController dragController = mDragController;
 
         DragLayer dragLayer = (DragLayer) findViewById(R.id.drag_layer);
         dragLayer.setDragController(dragController);
@@ -728,8 +855,15 @@
         mAllAppsGrid.setLauncher(this);
         mAllAppsGrid.setDragController(dragController);
         ((View) mAllAppsGrid).setWillNotDraw(false); // We don't want a hole punched in our window.
-        // Manage focusability manually since this thing is always visible
-        ((View) mAllAppsGrid).setFocusable(false); 
+        // Manage focusability manually since this thing is always visible (in non-xlarge)
+        ((View) mAllAppsGrid).setFocusable(false);
+
+        if (LauncherApplication.isScreenXLarge()) {
+            // They need to be INVISIBLE initially so that they will be measured in the layout.
+            // Otherwise the animations are messed up when we show them for the first time.
+            ((View) mAllAppsGrid).setVisibility(View.INVISIBLE);
+            mHomeCustomizationDrawer.setVisibility(View.INVISIBLE);
+        }
 
         mWorkspace = (Workspace) dragLayer.findViewById(R.id.workspace);
         final Workspace workspace = mWorkspace;
@@ -738,29 +872,39 @@
         DeleteZone deleteZone = (DeleteZone) dragLayer.findViewById(R.id.delete_zone);
         mDeleteZone = deleteZone;
 
-        mHandleView = (HandleView) findViewById(R.id.all_apps_button);
-        mHandleView.setLauncher(this);
-        mHandleView.setOnClickListener(this);
-        mHandleView.setOnLongClickListener(this);
+        View handleView = findViewById(R.id.all_apps_button);
+        if (handleView != null && handleView instanceof HandleView) {
+            // we don't use handle view in xlarge mode
+            mHandleView = (HandleView)handleView;
+            mHandleView.setLauncher(this);
+            mHandleView.setOnClickListener(this);
+            mHandleView.setOnLongClickListener(this);
+        }
 
-        ImageView hotseatLeft = (ImageView) findViewById(R.id.hotseat_left);
-        hotseatLeft.setContentDescription(mHotseatLabels[0]);
-        hotseatLeft.setImageDrawable(mHotseatIcons[0]);
-        ImageView hotseatRight = (ImageView) findViewById(R.id.hotseat_right);
-        hotseatRight.setContentDescription(mHotseatLabels[1]);
-        hotseatRight.setImageDrawable(mHotseatIcons[1]);
+        if (mCustomizePagedView != null) {
+            mCustomizePagedView.setLauncher(this);
+            mCustomizePagedView.setDragController(dragController);
+            mCustomizePagedView.update();
+        } else {
+             ImageView hotseatLeft = (ImageView) findViewById(R.id.hotseat_left);
+             hotseatLeft.setContentDescription(mHotseatLabels[0]);
+             hotseatLeft.setImageDrawable(mHotseatIcons[0]);
+             ImageView hotseatRight = (ImageView) findViewById(R.id.hotseat_right);
+             hotseatRight.setContentDescription(mHotseatLabels[1]);
+             hotseatRight.setImageDrawable(mHotseatIcons[1]);
 
-        mPreviousView = (ImageView) dragLayer.findViewById(R.id.previous_screen);
-        mNextView = (ImageView) dragLayer.findViewById(R.id.next_screen);
+             mPreviousView = (ImageView) dragLayer.findViewById(R.id.previous_screen);
+             mNextView = (ImageView) dragLayer.findViewById(R.id.next_screen);
 
-        Drawable previous = mPreviousView.getDrawable();
-        Drawable next = mNextView.getDrawable();
-        mWorkspace.setIndicators(previous, next);
+             Drawable previous = mPreviousView.getDrawable();
+             Drawable next = mNextView.getDrawable();
+             mWorkspace.setIndicators(previous, next);
 
-        mPreviousView.setHapticFeedbackEnabled(false);
-        mPreviousView.setOnLongClickListener(this);
-        mNextView.setHapticFeedbackEnabled(false);
-        mNextView.setOnLongClickListener(this);
+             mPreviousView.setHapticFeedbackEnabled(false);
+             mPreviousView.setOnLongClickListener(this);
+             mNextView.setHapticFeedbackEnabled(false);
+             mNextView.setOnLongClickListener(this);
+        }
 
         workspace.setOnLongClickListener(this);
         workspace.setDragController(dragController);
@@ -768,16 +912,33 @@
 
         deleteZone.setLauncher(this);
         deleteZone.setDragController(dragController);
-        deleteZone.setHandle(findViewById(R.id.all_apps_button_cluster));
+        int deleteZoneHandleId;
+        if (LauncherApplication.isScreenXLarge()) {
+            deleteZoneHandleId = R.id.all_apps_button;
+        } else {
+            deleteZoneHandleId = R.id.all_apps_button_cluster;
+        }
+        deleteZone.setHandle(findViewById(deleteZoneHandleId));
+        dragController.addDragListener(deleteZone);
+
+        ApplicationInfoDropTarget infoButton = (ApplicationInfoDropTarget)findViewById(R.id.info_button);
+        if (infoButton != null) {
+            infoButton.setLauncher(this);
+            infoButton.setHandle(findViewById(R.id.configure_button));
+            infoButton.setDragColor(getResources().getColor(R.color.app_info_filter));
+            dragController.addDragListener(infoButton);
+        }
 
         dragController.setDragScoller(workspace);
-        dragController.setDragListener(deleteZone);
         dragController.setScrollView(dragLayer);
         dragController.setMoveTarget(workspace);
 
         // The order here is bottom to top.
         dragController.addDropTarget(workspace);
         dragController.addDropTarget(deleteZone);
+        if (infoButton != null) {
+            dragController.addDropTarget(infoButton);
+        }
     }
 
     @SuppressWarnings({"UnusedDeclaration"})
@@ -815,7 +976,7 @@
             );
         }
     }
-    
+
     /**
      * Creates a view representing a shortcut.
      *
@@ -825,7 +986,7 @@
      */
     View createShortcut(ShortcutInfo info) {
         return createShortcut(R.layout.application,
-                (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), info);
+                (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info);
     }
 
     /**
@@ -857,7 +1018,7 @@
      * @param cellInfo The position on screen where to create the shortcut.
      */
     void completeAddApplication(Context context, Intent data, CellLayout.CellInfo cellInfo) {
-        cellInfo.screen = mWorkspace.getCurrentScreen();
+        cellInfo.screen = mWorkspace.getCurrentPage();
         if (!findSingleSlot(cellInfo)) return;
 
         final ShortcutInfo info = mModel.getShortcutInfo(context.getPackageManager(),
@@ -880,7 +1041,7 @@
      * @param cellInfo The position on screen where to create the shortcut.
      */
     private void completeAddShortcut(Intent data, CellLayout.CellInfo cellInfo) {
-        cellInfo.screen = mWorkspace.getCurrentScreen();
+        cellInfo.screen = mWorkspace.getCurrentPage();
         if (!findSingleSlot(cellInfo)) return;
 
         final ShortcutInfo info = mModel.addShortcut(this, data, cellInfo, false);
@@ -896,24 +1057,37 @@
     /**
      * Add a widget to the workspace.
      *
-     * @param data The intent describing the appWidgetId.
+     * @param appWidgetId The app widget id
      * @param cellInfo The position on screen where to create the widget.
      */
-    private void completeAddAppWidget(Intent data, CellLayout.CellInfo cellInfo) {
-        Bundle extras = data.getExtras();
-        int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
-
-        if (LOGD) Log.d(TAG, "dumping extras content=" + extras.toString());
-
+    private void completeAddAppWidget(int appWidgetId, CellLayout.CellInfo cellInfo) {
         AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
 
         // Calculate the grid spans needed to fit this widget
         CellLayout layout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen);
-        int[] spans = layout.rectToCell(appWidgetInfo.minWidth, appWidgetInfo.minHeight);
+        int[] spans = layout.rectToCell(appWidgetInfo.minWidth, appWidgetInfo.minHeight, null);
 
         // Try finding open space on Launcher screen
+        // We have saved the position to which the widget was dragged-- this really only matters
+        // if we are placing widgets on a "spring-loaded" screen
         final int[] xy = mCellCoordinates;
-        if (!findSlot(cellInfo, xy, spans[0], spans[1])) {
+
+        // For now, we don't save the coordinate where we dropped the icon because we're not
+        // supporting spring-loaded mini-screens; however, leaving the ability to directly place
+        // a widget on the home screen in case we want to add it in the future
+        final int[] xyTouch = null;
+        //final int[] xyTouch = mAddItemCoordinates;
+        boolean findNearestVacantAreaFailed = false;
+        if (xyTouch != null) {
+            CellLayout screen = (CellLayout) mWorkspace.getChildAt(cellInfo.screen);
+            int[] result = screen.findNearestVacantArea(
+                    mAddItemCoordinates[0], mAddItemCoordinates[1],
+                    spans[0], spans[1], cellInfo, xy);
+            findNearestVacantAreaFailed = (result == null);
+        }
+
+        if (findNearestVacantAreaFailed ||
+                (xyTouch == null && !findSlot(cellInfo, xy, spans[0], spans[1]))) {
             if (appWidgetId != -1) mAppWidgetHost.deleteAppWidgetId(appWidgetId);
             return;
         }
@@ -925,7 +1099,7 @@
 
         LauncherModel.addItemToDatabase(this, launcherInfo,
                 LauncherSettings.Favorites.CONTAINER_DESKTOP,
-                mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
+                cellInfo.screen, xy[0], xy[1], false);
 
         if (!mRestoring) {
             mDesktopItems.add(launcherInfo);
@@ -936,7 +1110,7 @@
             launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo);
             launcherInfo.hostView.setTag(launcherInfo);
 
-            mWorkspace.addInCurrentScreen(launcherInfo.hostView, xy[0], xy[1],
+            mWorkspace.addInScreen(launcherInfo.hostView, cellInfo.screen, xy[0], xy[1],
                     launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked());
         }
     }
@@ -983,10 +1157,22 @@
             boolean alreadyOnHome = ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
                         != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
             boolean allAppsVisible = isAllAppsVisible();
-            if (!mWorkspace.isDefaultScreenShowing()) {
+            boolean customizationDrawerVisible = isCustomizationDrawerVisible();
+
+            // in all these cases, only animate if we're already on home
+            if (LauncherApplication.isScreenXLarge()) {
+                if (alreadyOnHome && !mWorkspace.isSmall() &&
+                        !allAppsVisible && !customizationDrawerVisible) {
+                    mWorkspace.shrinkToMiddle();
+                } else {
+                    mWorkspace.unshrink(alreadyOnHome);
+                }
+            } else if (!mWorkspace.isDefaultPageShowing()) {
+                // on the phone, we don't animate the change to the workspace if all apps is visible
                 mWorkspace.moveToDefaultScreen(alreadyOnHome && !allAppsVisible);
             }
             closeAllApps(alreadyOnHome && allAppsVisible);
+            hideCustomizationDrawer(alreadyOnHome);
 
             final View v = getWindow().peekDecorView();
             if (v != null && v.getWindowToken() != null) {
@@ -1001,11 +1187,18 @@
     protected void onRestoreInstanceState(Bundle savedInstanceState) {
         // Do not call super here
         mSavedInstanceState = savedInstanceState;
+
+        if (mHomeCustomizationDrawer != null) {
+            String cur = savedInstanceState.getString("currentTab");
+            if (cur != null) {
+                mHomeCustomizationDrawer.setCurrentTabByTag(cur);
+            }
+        }
     }
 
     @Override
     protected void onSaveInstanceState(Bundle outState) {
-        outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getCurrentScreen());
+        outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getCurrentPage());
 
         final ArrayList<Folder> folders = mWorkspace.getOpenFolders();
         if (folders.size() > 0) {
@@ -1037,13 +1230,20 @@
             outState.putInt(RUNTIME_STATE_PENDING_ADD_COUNT_X, layout.getCountX());
             outState.putInt(RUNTIME_STATE_PENDING_ADD_COUNT_Y, layout.getCountY());
             outState.putBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS,
-                   layout.getOccupiedCells());
+                   layout.getOccupiedCellsFlattened());
         }
 
         if (mFolderInfo != null && mWaitingForResult) {
             outState.putBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, true);
             outState.putLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID, mFolderInfo.id);
         }
+
+        if (mHomeCustomizationDrawer != null) {
+            String currentTabTag = mHomeCustomizationDrawer.getCurrentTabTag();
+            if (currentTabTag != null) {
+                outState.putString("currentTab", currentTabTag);
+            }
+        }
     }
 
     @Override
@@ -1063,9 +1263,14 @@
         unbindDesktopItems();
 
         getContentResolver().unregisterContentObserver(mWidgetObserver);
-        
-        dismissPreview(mPreviousView);
-        dismissPreview(mNextView);
+
+        // Some launcher layouts don't have a previous and next view
+        if (mPreviousView != null) {
+            dismissPreview(mPreviousView);
+        }
+        if (mNextView != null) {
+            dismissPreview(mNextView);
+        }
 
         unregisterReceiver(mCloseSystemDialogsReceiver);
     }
@@ -1136,24 +1341,31 @@
 
         // If all apps is animating, don't show the menu, because we don't know
         // which one to show.
-        if (mAllAppsGrid.isVisible() && !mAllAppsGrid.isOpaque()) {
+        if (mAllAppsGrid.isAnimating()) {
             return false;
         }
 
         // Only show the add and wallpaper options when we're not in all apps.
-        boolean visible = !mAllAppsGrid.isOpaque();
+        boolean visible = !mAllAppsGrid.isVisible();
         menu.setGroupVisible(MENU_GROUP_ADD, visible);
         menu.setGroupVisible(MENU_GROUP_WALLPAPER, visible);
 
         // Disable add if the workspace is full.
         if (visible) {
-            mMenuAddInfo = mWorkspace.findAllVacantCells(null);
+            mMenuAddInfo = mWorkspace.updateOccupiedCellsForCurrentScreen(null);
             menu.setGroupEnabled(MENU_GROUP_ADD, mMenuAddInfo != null && mMenuAddInfo.valid);
         }
 
         return true;
     }
 
+    // we need to initialize mAddItemCellInfo before adding something to the homescreen -- when
+    // using the settings menu to add an item, something similar happens in showAddDialog
+    public void prepareAddItemFromHomeCustomizationDrawer() {
+        mMenuAddInfo = mWorkspace.updateOccupiedCellsForCurrentScreen(null);
+        mAddItemCellInfo = mMenuAddInfo;
+    }
+
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         switch (item.getItemId()) {
@@ -1190,13 +1402,37 @@
     }
 
     private void addItems() {
-        closeAllApps(true);
-        showAddDialog(mMenuAddInfo);
+        if (LauncherApplication.isScreenXLarge()) {
+            // Animate the widget chooser up from the bottom of the screen
+            if (!isCustomizationDrawerVisible()) {
+                showCustomizationDrawer(true);
+            }
+        } else {
+            closeAllApps(true);
+            showAddDialog(mMenuAddInfo);
+        }
     }
 
-    void addAppWidget(Intent data) {
+    void addAppWidgetFromDrop(ComponentName appWidgetProvider, CellLayout.CellInfo cellInfo,
+            int[] position) {
+        mAddItemCellInfo = cellInfo;
+
+        // only set mAddItemCoordinates if we dropped on home screen in "spring-loaded" manner
+        mAddItemCoordinates = position;
+        int appWidgetId = getAppWidgetHost().allocateAppWidgetId();
+        AppWidgetManager.getInstance(this).bindAppWidgetId(appWidgetId, appWidgetProvider);
+        addAppWidgetImpl(appWidgetId);
+    }
+
+    void addAppWidgetFromPick(Intent data) {
         // TODO: catch bad widget exception when sent
         int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
+        // TODO: Is this log message meaningful?
+        if (LOGD) Log.d(TAG, "dumping extras content=" + data.getExtras());
+        addAppWidgetImpl(appWidgetId);
+    }
+
+    void addAppWidgetImpl(int appWidgetId) {
         AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
 
         if (appWidget.configure != null) {
@@ -1208,7 +1444,7 @@
             startActivityForResultSafely(intent, REQUEST_CREATE_APPWIDGET);
         } else {
             // Otherwise just add it
-            onActivityResult(REQUEST_CREATE_APPWIDGET, Activity.RESULT_OK, data);
+            completeAddAppWidget(appWidgetId, mAddItemCellInfo);
         }
     }
 
@@ -1246,18 +1482,18 @@
         folderInfo.title = getText(R.string.folder_name);
 
         CellLayout.CellInfo cellInfo = mAddItemCellInfo;
-        cellInfo.screen = mWorkspace.getCurrentScreen();
+        cellInfo.screen = mWorkspace.getCurrentPage();
         if (!findSingleSlot(cellInfo)) return;
 
         // Update the model
         LauncherModel.addItemToDatabase(this, folderInfo,
                 LauncherSettings.Favorites.CONTAINER_DESKTOP,
-                mWorkspace.getCurrentScreen(), cellInfo.cellX, cellInfo.cellY, false);
+                mWorkspace.getCurrentPage(), cellInfo.cellX, cellInfo.cellY, false);
         sFolders.put(folderInfo.id, folderInfo);
 
         // Create the view
         FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
-                (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), folderInfo);
+                (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), folderInfo);
         mWorkspace.addInCurrentScreen(newFolder,
                 cellInfo.cellX, cellInfo.cellY, 1, 1, isWorkspaceLocked());
     }
@@ -1267,14 +1503,14 @@
     }
 
     private void completeAddLiveFolder(Intent data, CellLayout.CellInfo cellInfo) {
-        cellInfo.screen = mWorkspace.getCurrentScreen();
+        cellInfo.screen = mWorkspace.getCurrentPage();
         if (!findSingleSlot(cellInfo)) return;
 
         final LiveFolderInfo info = addLiveFolder(this, data, cellInfo, false);
 
         if (!mRestoring) {
             final View view = LiveFolderIcon.fromXml(R.layout.live_folder_icon, this,
-                (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), info);
+                (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info);
             mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1,
                     isWorkspaceLocked());
         }
@@ -1335,9 +1571,8 @@
 
     private boolean findSlot(CellLayout.CellInfo cellInfo, int[] xy, int spanX, int spanY) {
         if (!cellInfo.findCellForSpan(xy, spanX, spanY)) {
-            boolean[] occupied = mSavedState != null ?
-                    mSavedState.getBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS) : null;
-            cellInfo = mWorkspace.findAllVacantCells(occupied);
+            CellLayout targetLayout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen);
+            cellInfo = targetLayout.updateOccupiedCells(null, null);
             if (!cellInfo.findCellForSpan(xy, spanX, spanY)) {
                 Toast.makeText(this, getString(R.string.out_of_space), Toast.LENGTH_SHORT).show();
                 return false;
@@ -1409,11 +1644,16 @@
     public void onBackPressed() {
         if (isAllAppsVisible()) {
             closeAllApps(true);
+        } else if (isCustomizationDrawerVisible()) {
+            hideCustomizationDrawer(true);
         } else {
             closeFolder();
         }
-        dismissPreview(mPreviousView);
-        dismissPreview(mNextView);
+        // Some launcher layouts don't have a previous and next view
+        if (mPreviousView != null) {
+            dismissPreview(mPreviousView);
+            dismissPreview(mNextView);
+        }
     }
 
     private void closeFolder() {
@@ -1479,6 +1719,58 @@
         }
     }
 
+    public boolean onTouch(View v, MotionEvent event) {
+        // this is an intercepted event being forwarded from mWorkspace;
+        // clicking anywhere on the workspace causes the drawer to slide down
+        hideCustomizationDrawer(true);
+        return false;
+    }
+
+    /**
+     * Event handler for the search button
+     *
+     * @param v The view that was clicked.
+     */
+    public void onClickSearchButton(View v) {
+        Intent i = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
+        View button = findViewById(R.id.search_button);
+        i.setSourceBounds(
+                new Rect(button.getLeft(), button.getTop(), button.getRight(), button.getBottom()));
+        startActivity(i);
+    }
+
+    /**
+     * Event handler for the "gear" button that appears on the home screen, which
+     * enters home screen customization mode.
+     *
+     * @param v The view that was clicked.
+     */
+    public void onClickConfigureButton(View v) {
+        addItems();
+    }
+
+    /**
+     * Event handler for the "grid" button that appears on the home screen, which
+     * enters all apps mode.
+     *
+     * @param v The view that was clicked.
+     */
+    public void onClickAllAppsButton(View v) {
+        showAllApps(true);
+    }
+
+    public void onClickAppMarketButton(View v) {
+        if (mAppMarketIntent != null) {
+            startActivitySafely(mAppMarketIntent, "app market");
+        }
+    }
+
+    void startApplicationDetailsActivity(String packageName) {
+        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
+                Uri.fromParts("package", packageName, null));
+        startActivity(intent);
+    }
+
     void startActivitySafely(Intent intent, Object tag) {
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         try {
@@ -1494,7 +1786,7 @@
                     + "tag="+ tag + " intent=" + intent, e);
         }
     }
-    
+
     void startActivityForResultSafely(Intent intent, int requestCode) {
         try {
             startActivityForResult(intent, requestCode);
@@ -1519,10 +1811,10 @@
             Folder openFolder = mWorkspace.getFolderForTag(folderInfo);
             int folderScreen;
             if (openFolder != null) {
-                folderScreen = mWorkspace.getScreenForView(openFolder);
+                folderScreen = mWorkspace.getPageForView(openFolder);
                 // .. and close it
                 closeFolder(openFolder);
-                if (folderScreen != mWorkspace.getCurrentScreen()) {
+                if (folderScreen != mWorkspace.getCurrentPage()) {
                     // Close any folder open on the current screen
                     closeFolder();
                     // Pull the folder onto this screen
@@ -1539,7 +1831,7 @@
      *
      * @param folderInfo The FolderInfo describing the folder to open.
      */
-    private void openFolder(FolderInfo folderInfo) {
+    public void openFolder(FolderInfo folderInfo) {
         Folder openFolder;
 
         if (folderInfo instanceof UserFolderInfo) {
@@ -1556,7 +1848,8 @@
         openFolder.bind(folderInfo);
         folderInfo.opened = true;
 
-        mWorkspace.addInScreen(openFolder, folderInfo.screen, 0, 0, 4, 4);
+        mWorkspace.addInFullScreen(openFolder, folderInfo.screen);
+
         openFolder.onOpen();
     }
 
@@ -1654,9 +1947,9 @@
         final Workspace workspace = mWorkspace;
 
         CellLayout cell = ((CellLayout) workspace.getChildAt(start));
-        
+
         float max = workspace.getChildCount();
-        
+
         final Rect r = new Rect();
         resources.getDrawable(R.drawable.preview_background).getPadding(r);
         int extraW = (int) ((r.left + r.right) * max);
@@ -1702,12 +1995,12 @@
             image.setOnClickListener(handler);
             image.setOnFocusChangeListener(handler);
             image.setFocusable(true);
-            if (i == mWorkspace.getCurrentScreen()) image.requestFocus();
+            if (i == mWorkspace.getCurrentPage()) image.requestFocus();
 
             preview.addView(image,
                     LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
 
-            bitmaps.add(bitmap);            
+            bitmaps.add(bitmap);
         }
 
         final PopupWindow p = new PopupWindow(this);
@@ -1728,7 +2021,7 @@
 
         anchor.setTag(p);
         anchor.setTag(R.id.workspace, preview);
-        anchor.setTag(R.id.icon, bitmaps);        
+        anchor.setTag(R.id.icon, bitmaps);
     }
 
     class PreviewTouchHandler implements View.OnClickListener, Runnable, View.OnFocusChangeListener {
@@ -1739,17 +2032,17 @@
         }
 
         public void onClick(View v) {
-            mWorkspace.snapToScreen((Integer) v.getTag());
+            mWorkspace.snapToPage((Integer) v.getTag());
             v.post(this);
         }
 
         public void run() {
-            dismissPreview(mAnchor);            
+            dismissPreview(mAnchor);
         }
 
         public void onFocusChange(View v, boolean hasFocus) {
             if (hasFocus) {
-                mWorkspace.snapToScreen((Integer) v.getTag());
+                mWorkspace.snapToPage((Integer) v.getTag());
             }
         }
     }
@@ -1794,11 +2087,13 @@
 
     private void showAddDialog(CellLayout.CellInfo cellInfo) {
         mAddItemCellInfo = cellInfo;
+        mAddItemCoordinates = null;
         mWaitingForResult = true;
         showDialog(DIALOG_CREATE_SHORTCUT);
     }
 
     private void pickShortcut() {
+        // Insert extra item to handle picking application
         Bundle bundle = new Bundle();
 
         ArrayList<String> shortcutNames = new ArrayList<String>();
@@ -1900,22 +2195,310 @@
 
     // Now a part of LauncherModel.Callbacks. Used to reorder loading steps.
     public boolean isAllAppsVisible() {
-        return (mAllAppsGrid != null) ? mAllAppsGrid.isVisible() : false;
+        return mAllAppsGrid != null && mAllAppsGrid.isVisible();
     }
 
     // AllAppsView.Watcher
     public void zoomed(float zoom) {
-        if (zoom == 1.0f) {
+        // In XLarge view, we zoom down the workspace below all apps so it's still visible
+        if (zoom == 1.0f && !LauncherApplication.isScreenXLarge()) {
             mWorkspace.setVisibility(View.GONE);
         }
     }
+    
+    private void showToolbarButton(View button) {
+        button.setAlpha(1.0f);
+        button.setVisibility(View.VISIBLE);
+        button.setFocusable(true);
+        button.setClickable(true);
+    }
+
+    private void hideToolbarButton(View button) {
+        button.setAlpha(0.0f);
+        // We can't set it to GONE, otherwise the RelativeLayout gets screwed up
+        button.setVisibility(View.INVISIBLE);
+        button.setFocusable(false);
+        button.setClickable(false);
+    }
+
+    /**
+     * Helper function for showing or hiding a toolbar button, possibly animated.
+     *
+     * @param show If true, create an animation to the show the item. Otherwise, hide it.
+     * @param view The toolbar button to be animated
+     * @param seq A Sequencer that will be used to animate the transition. If null, the
+     * transition will not be animated.
+     */
+    private void hideOrShowToolbarButton(boolean show, final View view, Sequencer seq) {
+        final boolean showing = show;
+        final boolean hiding = !show;
+
+        final int duration = show ?
+                getResources().getInteger(R.integer.config_toolbarButtonFadeInTime) :
+                getResources().getInteger(R.integer.config_toolbarButtonFadeOutTime);
+
+        if (seq != null) {
+            Animatable anim = new PropertyAnimator<Float>(duration, view, "alpha", show ? 1.0f : 0.0f);
+            anim.addListener(new AnimatableListenerAdapter() {
+                public void onAnimationStart(Animatable animation) {
+                    if (showing) showToolbarButton(view);
+                }
+                public void onAnimationEnd(Animatable animation) {
+                    if (hiding) hideToolbarButton(view);
+                }
+            });
+            seq.play(anim);
+        } else {
+            if (showing) {
+                showToolbarButton(view);
+            } else {
+                hideToolbarButton(view);
+            }
+        }
+    }
+
+    /**
+     * Show/hide the appropriate toolbar buttons for newState.
+     * If showSeq or hideSeq is null, the transition will be done immediately (not animated).
+     *
+     * @param newState The state that is being switched to
+     * @param showSeq Sequencer in which to put "show" animations, or null.
+     * @param hideSeq Sequencer in which to put "hide" animations, or null.
+     */
+    private void hideAndShowToolbarButtons(State newState, Sequencer showSeq, Sequencer hideSeq) {
+        final View searchButton = findViewById(R.id.search_button);
+        final View allAppsButton = findViewById(R.id.all_apps_button);
+        final View marketButton = findViewById(R.id.market_button);
+        final View configureButton = findViewById(R.id.configure_button);
+
+        switch (newState) {
+        case WORKSPACE:
+            hideOrShowToolbarButton(true, searchButton, showSeq);
+            hideOrShowToolbarButton(true, allAppsButton, showSeq);
+            hideOrShowToolbarButton(true, configureButton, showSeq);
+            hideOrShowToolbarButton(false, marketButton, hideSeq);
+            break;
+        case ALL_APPS:
+            hideOrShowToolbarButton(true, configureButton, showSeq);
+            hideOrShowToolbarButton(true, marketButton, showSeq);
+            hideOrShowToolbarButton(false, searchButton, hideSeq);
+            hideOrShowToolbarButton(false, allAppsButton, hideSeq);
+            break;
+        case CUSTOMIZE:
+            hideOrShowToolbarButton(true, allAppsButton, showSeq);
+            hideOrShowToolbarButton(false, searchButton, hideSeq);
+            hideOrShowToolbarButton(false, marketButton, hideSeq);
+            hideOrShowToolbarButton(false, configureButton, hideSeq);
+            break;
+        }
+    }
+
+    /**
+     * Helper method for the cameraZoomIn/cameraZoomOut animations
+     * @param view The view being animated
+     * @param state The state that we are moving in or out of -- either ALL_APPS or CUSTOMIZE
+     * @param scaleFactor The scale factor used for the zoom
+     */
+    private void setPivotsForZoom(View view, State state, float scaleFactor) {
+        final int height = view.getHeight();
+        view.setPivotX(view.getWidth() / 2.0f);
+        // Set pivotY so that at the starting zoom factor, the view is off-screen by a small margin
+        // Assumes that the view is normally anchored to either the top or bottom of the screen
+        final int margin = getResources().getInteger(R.integer.config_allAppsVerticalOffset);
+        if (state == State.ALL_APPS) {
+            view.setPivotY(height + ((view.getTop() + height) / scaleFactor) + margin);
+        } else {
+            view.setPivotY(0.0f - (view.getTop() / scaleFactor) - margin);
+        }
+    }
+
+    /**
+     * Zoom the camera out from the workspace to reveal 'toView'.
+     * Assumes that the view to show is anchored at either the very top or very bottom
+     * of the screen.
+     * @param toState The state to zoom out to. Must be ALL_APPS or CUSTOMIZE.
+     */
+    private void cameraZoomOut(State toState, boolean animated) {
+        final Resources res = getResources();
+        final int duration = res.getInteger(R.integer.config_allAppsZoomInTime);
+        final float scale = (float) res.getInteger(R.integer.config_allAppsZoomScaleFactor);
+        final boolean toAllApps = (toState == State.ALL_APPS);
+        final View toView = toAllApps ? (View) mAllAppsGrid : mHomeCustomizationDrawer;
+
+        setPivotsForZoom(toView, toState, scale);
+
+        if (toState == State.ALL_APPS) {
+            mWorkspace.shrinkToBottom(animated);
+        } else {
+            mWorkspace.shrinkToTop(animated);
+        }
+
+        if (animated) {
+            Animator scaleAnim = new PropertyAnimator(duration, toView,
+                    new PropertyValuesHolder<Float>("scaleX", scale, 1.0f),
+                    new PropertyValuesHolder<Float>("scaleY", scale, 1.0f));
+            scaleAnim.setInterpolator(new DecelerateInterpolator());
+            scaleAnim.addListener(new AnimatableListenerAdapter() {
+                public void onAnimationStart(Animatable animation) {
+                    // Prepare the position
+                    toView.setTranslationX(0.0f);
+                    toView.setTranslationY(0.0f);
+                    toView.setVisibility(View.VISIBLE);
+                }
+            });
+
+            Sequencer toolbarHideAnim = new Sequencer();
+            Sequencer toolbarShowAnim = new Sequencer();
+            hideAndShowToolbarButtons(toState, toolbarShowAnim, toolbarHideAnim);
+
+            // toView should appear right at the end of the workspace shrink animation
+            final int startDelay = res.getInteger(R.integer.config_workspaceShrinkTime) - duration;
+
+            Sequencer s = new Sequencer();
+            s.playTogether(scaleAnim, toolbarHideAnim);
+            s.play(scaleAnim).after(startDelay);
+
+            // Show the new toolbar buttons just as the main animation is ending
+            final int fadeInTime = res.getInteger(R.integer.config_toolbarButtonFadeInTime);
+            s.play(toolbarShowAnim).after(duration + startDelay - fadeInTime);
+            s.start();
+        } else {
+            toView.setTranslationX(0.0f);
+            toView.setTranslationY(0.0f);
+            toView.setScaleX(1.0f);
+            toView.setScaleY(1.0f);
+            toView.setVisibility(View.VISIBLE);
+            hideAndShowToolbarButtons(toState, null, null);
+        }
+    }
+
+    /**
+     * Zoom the camera back into the workspace, hiding 'fromView'.
+     * This is the opposite of cameraZoomOut.
+     * @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) {
+        Resources res = getResources();
+        int duration = res.getInteger(R.integer.config_allAppsZoomOutTime);
+        float scaleFactor = (float) res.getInteger(R.integer.config_allAppsZoomScaleFactor);
+        final View fromView =
+            (fromState == State.ALL_APPS) ? (View) mAllAppsGrid : mHomeCustomizationDrawer;
+
+        setPivotsForZoom(fromView, fromState, scaleFactor);
+
+        mWorkspace.unshrink(animated);
+
+        if (animated) {
+            Sequencer s = new Sequencer();
+            Animator scaleAnim = new PropertyAnimator(duration, fromView,
+                    new PropertyValuesHolder<Float>("scaleX", scaleFactor),
+                    new PropertyValuesHolder<Float>("scaleY", scaleFactor));
+            scaleAnim.setInterpolator(new AccelerateInterpolator());
+            s.addListener(new AnimatableListenerAdapter() {
+                public void onAnimationEnd(Animatable animation) {
+                    fromView.setVisibility(View.GONE);
+                    fromView.setScaleX(1.0f);
+                    fromView.setScaleY(1.0f);
+                }
+            });
+
+            Sequencer toolbarHideAnim = new Sequencer();
+            Sequencer toolbarShowAnim = new Sequencer();
+            hideAndShowToolbarButtons(State.WORKSPACE, toolbarShowAnim, toolbarHideAnim);
+
+            s.playTogether(scaleAnim, toolbarHideAnim);
+
+            // Show the new toolbar buttons at the very end of the whole animation
+            final int fadeInTime = res.getInteger(R.integer.config_toolbarButtonFadeInTime);
+            final int unshrinkTime = res.getInteger(R.integer.config_workspaceUnshrinkTime);
+            s.play(toolbarShowAnim).after(unshrinkTime - fadeInTime);
+            s.start();
+        } else {
+            fromView.setVisibility(View.GONE);
+            hideAndShowToolbarButtons(State.WORKSPACE, null, null);
+        }
+    }
+
+    /**
+     * Pan the camera in the vertical plane between 'fromView' and 'toView'.
+     * This is the transition used on xlarge screens to go between all apps and
+     * the home customization drawer.
+     * @param fromState The view to pan away from. Must be ALL_APPS or CUSTOMIZE.
+     * @param toState The view to pan into the frame. Must be ALL_APPS or CUSTOMIZE.
+     * @param animated If true, the transition will be animated.
+     */
+    private void cameraPan(State fromState, State toState, boolean animated) {
+        final Resources res = getResources();
+        final int duration = res.getInteger(R.integer.config_allAppsCameraPanTime);
+        final int workspaceHeight = mWorkspace.getHeight();
+
+        final boolean fromAllApps = (fromState == State.ALL_APPS);
+        final View fromView = fromAllApps ? (View) mAllAppsGrid : mHomeCustomizationDrawer;
+        final View toView = fromAllApps ? mHomeCustomizationDrawer : (View) mAllAppsGrid;
+
+        final float fromViewStartY = fromAllApps ? 0.0f : fromView.getY();
+        final float fromViewEndY = fromAllApps ? -fromView.getHeight() * 2 : workspaceHeight * 2;
+        final float toViewStartY = fromAllApps ? workspaceHeight * 2 : -toView.getHeight() * 2;
+        final float toViewEndY = fromAllApps ? workspaceHeight - toView.getHeight() : 0.0f;
+
+        if (toState == State.ALL_APPS) {
+            mWorkspace.shrinkToBottom(animated);
+        } else {
+            mWorkspace.shrinkToTop(animated);
+        }
+
+        if (animated) {
+            Sequencer s = new Sequencer();
+            s.addListener(new AnimatableListenerAdapter() {
+                public void onAnimationStart(Animatable animation) {
+                    toView.setVisibility(View.VISIBLE);
+                    toView.setY(toViewStartY);
+                }
+                public void onAnimationEnd(Animatable animation) {
+                    fromView.setVisibility(View.GONE);
+                }
+            });
+
+            Sequencer toolbarHideAnim = new Sequencer();
+            Sequencer toolbarShowAnim = new Sequencer();
+            hideAndShowToolbarButtons(toState, toolbarShowAnim, toolbarHideAnim);
+
+            s.playTogether(
+                    toolbarHideAnim,
+                    new PropertyAnimator(duration, fromView, "y", fromViewStartY, fromViewEndY),
+                    new PropertyAnimator(duration, toView, "y", toViewStartY, toViewEndY));
+
+            // Show the new toolbar buttons just as the main animation is ending
+            final int fadeInTime = res.getInteger(R.integer.config_toolbarButtonFadeInTime);
+            s.play(toolbarShowAnim).after(duration - fadeInTime);
+            s.start();
+        } else {
+            fromView.setY(fromViewEndY);
+            fromView.setVisibility(View.GONE);
+            toView.setY(toViewEndY);
+            toView.setVisibility(View.VISIBLE);
+            hideAndShowToolbarButtons(toState, null, null);
+        }
+    }
 
     void showAllApps(boolean animated) {
-        mAllAppsGrid.zoom(1.0f, animated);
+        if (mAllAppsGrid.isVisible())
+            return;
+
+        if (LauncherApplication.isScreenXLarge()) {
+            if (isCustomizationDrawerVisible()) {
+                cameraPan(State.CUSTOMIZE, State.ALL_APPS, animated);
+            } else {
+                cameraZoomOut(State.ALL_APPS, animated);
+            }
+        } else {
+            mAllAppsGrid.zoom(1.0f, animated);
+        }
 
         ((View) mAllAppsGrid).setFocusable(true);
         ((View) mAllAppsGrid).requestFocus();
-        
+
         // TODO: fade these two too
         mDeleteZone.setVisibility(View.GONE);
     }
@@ -1962,9 +2545,13 @@
     void closeAllApps(boolean animated) {
         if (mAllAppsGrid.isVisible()) {
             mWorkspace.setVisibility(View.VISIBLE);
-            mAllAppsGrid.zoom(0.0f, animated);
+            if (LauncherApplication.isScreenXLarge()) {
+                cameraZoomIn(State.ALL_APPS, animated);
+            } else {
+                mAllAppsGrid.zoom(0.0f, animated);
+            }
             ((View)mAllAppsGrid).setFocusable(false);
-            mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
+            mWorkspace.getChildAt(mWorkspace.getCurrentPage()).requestFocus();
         }
     }
 
@@ -1976,6 +2563,77 @@
         // TODO
     }
 
+    private boolean isCustomizationDrawerVisible() {
+        return mHomeCustomizationDrawer != null &&
+                mHomeCustomizationDrawer.getVisibility() == View.VISIBLE;
+    }
+
+    // Show the customization drawer (only exists in x-large configuration)
+    private void showCustomizationDrawer(boolean animated) {
+        if (isAllAppsVisible()) {
+            cameraPan(State.ALL_APPS, State.CUSTOMIZE, animated);
+        } else {
+            cameraZoomOut(State.CUSTOMIZE, animated);
+        }
+    }
+
+    // Hide the customization drawer (only exists in x-large configuration)
+    void hideCustomizationDrawer(boolean animated) {
+        if (isCustomizationDrawerVisible()) {
+            cameraZoomIn(State.CUSTOMIZE, animated);
+        }
+    }
+
+    void onWorkspaceUnshrink() {
+        final boolean animated = true;
+        if (isAllAppsVisible()) {
+            closeAllApps(animated);
+        }
+        if (isCustomizationDrawerVisible()) {
+            hideCustomizationDrawer(animated);
+        }
+    }
+
+    /**
+     * Sets the app market icon (shown when all apps is visible on x-large screens)
+     */
+    private void updateAppMarketIcon() {
+        if (LauncherApplication.isScreenXLarge()) {
+            // Find the app market activity by resolving an intent.
+            // (If multiple app markets are installed, it will return the ResolverActivity.)
+            PackageManager packageManager = getPackageManager();
+            Intent intent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_APP_MARKET);
+            ComponentName activityName = intent.resolveActivity(getPackageManager());
+            if (activityName != null) {
+                mAppMarketIntent = intent;
+                ImageView marketButton = (ImageView) findViewById(R.id.market_button);
+                Drawable toolbarIcon = null;
+                try {
+                    // Look for the toolbar icon specified in the activity meta-data
+                    Bundle metaData = packageManager.getActivityInfo(
+                            activityName, PackageManager.GET_META_DATA).metaData;
+                    if (metaData != null) {
+                        int iconResId = metaData.getInt(TOOLBAR_ICON_METADATA_NAME);
+                        if (iconResId != 0) {
+                            Resources res = packageManager.getResourcesForActivity(activityName);
+                            toolbarIcon = res.getDrawable(iconResId);
+                        }
+                    }
+                } catch (NameNotFoundException e) {
+                    // Do nothing
+                }
+                // If we were unable to find the icon via the meta-data, use a generic one
+                if (toolbarIcon == null) {
+                    marketButton.setImageResource(R.drawable.app_market_generic);
+                } else {
+                    marketButton.setImageDrawable(toolbarIcon);
+                }
+            } else {
+                mAppMarketIntent = null;
+            }
+        }
+    }
+
     /**
      * Displays the shortcut creation dialog and launches, if necessary, the
      * appropriate activity.
@@ -2028,7 +2686,6 @@
 
             switch (which) {
                 case AddAdapter.ITEM_SHORTCUT: {
-                    // Insert extra item to handle picking application
                     pickShortcut();
                     break;
                 }
@@ -2076,7 +2733,7 @@
         }
 
         public void onShow(DialogInterface dialog) {
-            mWaitingForResult = true;            
+            mWaitingForResult = true;
         }
     }
 
@@ -2094,6 +2751,7 @@
                     animate = false;
                 }
                 closeAllApps(animate);
+                hideCustomizationDrawer(animate);
             }
         }
     }
@@ -2117,7 +2775,7 @@
      */
     public int getCurrentWorkspaceScreen() {
         if (mWorkspace != null) {
-            return mWorkspace.getCurrentScreen();
+            return mWorkspace.getCurrentPage();
         } else {
             return SCREEN_COUNT / 2;
         }
@@ -2170,7 +2828,7 @@
                     break;
                 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
                     final FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
-                            (ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()),
+                            (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
                             (UserFolderInfo) item);
                     workspace.addInScreen(newFolder, item.screen, item.cellX, item.cellY, 1, 1,
                             false);
@@ -2178,7 +2836,7 @@
                 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
                     final FolderIcon newLiveFolder = LiveFolderIcon.fromXml(
                             R.layout.live_folder_icon, this,
-                            (ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()),
+                            (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
                             (LiveFolderInfo) item);
                     workspace.addInScreen(newLiveFolder, item.screen, item.cellX, item.cellY, 1, 1,
                             false);
@@ -2241,7 +2899,7 @@
     public void finishBindingItems() {
         if (mSavedState != null) {
             if (!mWorkspace.hasFocus()) {
-                mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
+                mWorkspace.getChildAt(mWorkspace.getCurrentPage()).requestFocus();
             }
 
             final long[] userFolders = mSavedState.getLongArray(RUNTIME_STATE_USER_FOLDERS);
@@ -2276,6 +2934,7 @@
      */
     public void bindAllApplications(ArrayList<ApplicationInfo> apps) {
         mAllAppsGrid.setApps(apps);
+        updateAppMarketIcon();
     }
 
     /**
@@ -2286,6 +2945,7 @@
     public void bindAppsAdded(ArrayList<ApplicationInfo> apps) {
         removeDialog(DIALOG_CREATE_SHORTCUT);
         mAllAppsGrid.addApps(apps);
+        updateAppMarketIcon();
     }
 
     /**
@@ -2297,6 +2957,7 @@
         removeDialog(DIALOG_CREATE_SHORTCUT);
         mWorkspace.updateShortcuts(apps);
         mAllAppsGrid.updateApps(apps);
+        updateAppMarketIcon();
     }
 
     /**
@@ -2310,6 +2971,16 @@
             mWorkspace.removeItems(apps);
         }
         mAllAppsGrid.removeApps(apps);
+        updateAppMarketIcon();
+    }
+
+    /**
+     * A number of packages were updated.
+     */
+    public void bindPackagesUpdated() {
+        // update the customization drawer contents
+        if (mCustomizePagedView != null)
+            mCustomizePagedView.update();
     }
 
     /**
diff --git a/src/com/android/launcher2/LauncherAppWidgetInfo.java b/src/com/android/launcher2/LauncherAppWidgetInfo.java
index 8499ebb..32c92aa 100644
--- a/src/com/android/launcher2/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher2/LauncherAppWidgetInfo.java
@@ -17,25 +17,50 @@
 package com.android.launcher2;
 
 import android.appwidget.AppWidgetHostView;
+import android.content.ComponentName;
 import android.content.ContentValues;
 
 /**
- * Represents a widget, which just contains an identifier.
+ * Represents a widget (either instantiated or about to be) in the Launcher.
  */
 class LauncherAppWidgetInfo extends ItemInfo {
 
     /**
+     * Indicates that the widget hasn't been instantiated yet.
+     */
+    static final int NO_ID = -1;
+
+    /**
      * Identifier for this widget when talking with
      * {@link android.appwidget.AppWidgetManager} for updates.
      */
-    int appWidgetId;
+    int appWidgetId = NO_ID;
+
+    ComponentName providerName;
     
+    // TODO: Are these necessary here?
+    int minWidth = -1;
+    int minHeight = -1;
+
     /**
      * View that holds this widget after it's been created.  This view isn't created
      * until Launcher knows it's needed.
      */
     AppWidgetHostView hostView = null;
 
+    /**
+     * Constructor for use with AppWidgets that haven't been instantiated yet.
+     */
+    LauncherAppWidgetInfo(ComponentName providerName) {
+        itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
+        this.providerName = providerName;
+
+        // Since the widget isn't instantiated yet, we don't know these values. Set them to -1
+        // to indicate that they should be calculated based on the layout and minWidth/minHeight
+        spanX = -1;
+        spanY = -1;
+    }
+
     LauncherAppWidgetInfo(int appWidgetId) {
         itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
         this.appWidgetId = appWidgetId;
@@ -52,7 +77,6 @@
         return "AppWidget(id=" + Integer.toString(appWidgetId) + ")";
     }
 
-
     @Override
     void unbind() {
         super.unbind();
diff --git a/src/com/android/launcher2/LauncherApplication.java b/src/com/android/launcher2/LauncherApplication.java
index eda92d9..ca08378 100644
--- a/src/com/android/launcher2/LauncherApplication.java
+++ b/src/com/android/launcher2/LauncherApplication.java
@@ -20,6 +20,7 @@
 import android.content.ContentResolver;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.os.Handler;
 import dalvik.system.VMRuntime;
@@ -27,6 +28,8 @@
 public class LauncherApplication extends Application {
     public LauncherModel mModel;
     public IconCache mIconCache;
+    private static boolean sIsScreenXLarge;
+    private static final boolean ENABLE_ROTATION = false;
 
     @Override
     public void onCreate() {
@@ -36,6 +39,7 @@
 
         mIconCache = new IconCache(this);
         mModel = new LauncherModel(this, mIconCache);
+        sIsScreenXLarge = (getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE;
 
         // Register intent receivers
         IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
@@ -89,4 +93,12 @@
     LauncherModel getModel() {
         return mModel;
     }
+
+    public static boolean isInPlaceRotationEnabled() {
+        return sIsScreenXLarge && ENABLE_ROTATION;
+    }
+
+    public static boolean isScreenXLarge() {
+        return sIsScreenXLarge;
+    }
 }
diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java
index c1ecf50..1e363d1 100644
--- a/src/com/android/launcher2/LauncherModel.java
+++ b/src/com/android/launcher2/LauncherModel.java
@@ -16,6 +16,15 @@
 
 package com.android.launcher2;
 
+import java.lang.ref.WeakReference;
+import java.net.URISyntaxException;
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.BroadcastReceiver;
@@ -23,9 +32,9 @@
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.content.ContentValues;
+import android.content.Context;
 import android.content.Intent;
 import android.content.Intent.ShortcutIconResource;
-import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ProviderInfo;
@@ -38,20 +47,10 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Parcelable;
-import android.os.RemoteException;
-import android.util.Log;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.SystemClock;
-
-import java.lang.ref.WeakReference;
-import java.net.URISyntaxException;
-import java.text.Collator;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
+import android.util.Log;
 
 import com.android.launcher.R;
 
@@ -91,6 +90,9 @@
 
     private Bitmap mDefaultIcon;
 
+    private static int mCellCountX;
+    private static int mCellCountY;
+
     public interface Callbacks {
         public int getCurrentWorkspaceScreen();
         public void startBinding();
@@ -102,6 +104,7 @@
         public void bindAppsAdded(ArrayList<ApplicationInfo> apps);
         public void bindAppsUpdated(ArrayList<ApplicationInfo> apps);
         public void bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent);
+        public void bindPackagesUpdated();
         public boolean isAllAppsVisible();
     }
 
@@ -146,6 +149,7 @@
      */
     static void moveItemInDatabase(Context context, ItemInfo item, long container, int screen,
             int cellX, int cellY) {
+
         item.container = container;
         item.screen = screen;
         item.cellX = cellX;
@@ -155,8 +159,8 @@
         final ContentResolver cr = context.getContentResolver();
 
         values.put(LauncherSettings.Favorites.CONTAINER, item.container);
-        values.put(LauncherSettings.Favorites.CELLX, item.cellX);
-        values.put(LauncherSettings.Favorites.CELLY, item.cellY);
+        values.put(LauncherSettings.Favorites.CELLX, cellX);
+        values.put(LauncherSettings.Favorites.CELLY, cellY);
         values.put(LauncherSettings.Favorites.SCREEN, item.screen);
 
         cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
@@ -181,6 +185,48 @@
     }
 
     /**
+     * Returns an ItemInfo array containing all the items in the LauncherModel.
+     * The ItemInfo.id is not set through this function.
+     */
+    static ArrayList<ItemInfo> getItemsInLocalCoordinates(Context context) {
+        ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
+        final ContentResolver cr = context.getContentResolver();
+        Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, new String[] {
+                LauncherSettings.Favorites.ITEM_TYPE, LauncherSettings.Favorites.CONTAINER,
+                LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY,
+                LauncherSettings.Favorites.SPANX, LauncherSettings.Favorites.SPANY }, null, null, null);
+
+        final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
+        final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
+        final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
+        final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
+        final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
+        final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
+        final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
+
+        try {
+            while (c.moveToNext()) {
+                ItemInfo item = new ItemInfo();
+                item.cellX = c.getInt(cellXIndex);
+                item.cellY = c.getInt(cellYIndex);
+                item.spanX = c.getInt(spanXIndex);
+                item.spanY = c.getInt(spanYIndex);
+                item.container = c.getInt(containerIndex);
+                item.itemType = c.getInt(itemTypeIndex);
+                item.screen = c.getInt(screenIndex);
+
+                items.add(item);
+            }
+        } catch (Exception e) {
+            items.clear();
+        } finally {
+            c.close();
+        }
+
+        return items;
+    }
+
+    /**
      * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList.
      */
     FolderInfo getFolderById(Context context, HashMap<Long,FolderInfo> folderList, long id) {
@@ -239,9 +285,10 @@
 
         final ContentValues values = new ContentValues();
         final ContentResolver cr = context.getContentResolver();
-
         item.onAddToDatabase(values);
 
+        item.updateValuesWithCoordinates(values, cellX, cellY);
+
         Uri result = cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
                 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
 
@@ -251,6 +298,30 @@
     }
 
     /**
+     * Creates a new unique child id, for a given cell span across all layouts.
+     */
+    static int getCellLayoutChildId(int cellId, int screen, int localCellX, int localCellY, int spanX, int spanY) {
+        return ((cellId & 0xFF) << 16) | (localCellX & 0xFF) << 8 | (localCellY & 0xFF);
+    }
+
+    static int getCellCountX() {
+        return mCellCountX;
+    }
+
+    static int getCellCountY() {
+        return mCellCountY;
+    }
+
+    /**
+     * Updates the model orientation helper to take into account the current layout dimensions
+     * when performing local/canonical coordinate transformations.
+     */
+    static void updateWorkspaceLayoutCells(int shortAxisCellCount, int longAxisCellCount) {
+        mCellCountX = shortAxisCellCount;
+        mCellCountY = longAxisCellCount;
+    }
+
+    /**
      * Update an item to the database in a specified container.
      */
     static void updateItemInDatabase(Context context, ItemInfo item) {
@@ -258,6 +329,7 @@
         final ContentResolver cr = context.getContentResolver();
 
         item.onAddToDatabase(values);
+        item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
 
         cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
     }
@@ -299,7 +371,7 @@
      */
     public void onReceive(Context context, Intent intent) {
         if (DEBUG_LOADERS) Log.d(TAG, "onReceive intent=" + intent);
-        
+
         final String action = intent.getAction();
 
         if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
@@ -343,7 +415,6 @@
             String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
             enqueuePackageUpdated(new PackageUpdatedTask(
                         PackageUpdatedTask.OP_UNAVAILABLE, packages));
-
         }
     }
 
@@ -449,7 +520,7 @@
                 }
                 if (DEBUG_LOADERS) {
                     Log.d(TAG, "waited "
-                            + (SystemClock.uptimeMillis()-workspaceWaitTime) 
+                            + (SystemClock.uptimeMillis()-workspaceWaitTime)
                             + "ms for previous step to finish binding");
                 }
             }
@@ -469,7 +540,6 @@
                     android.os.Process.setThreadPriority(mIsLaunching
                             ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
                 }
-
                 if (loadWorkspaceFirst) {
                     if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
                     loadAndBindWorkspace();
@@ -571,14 +641,13 @@
             if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
                 return true;
             }
-
             for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
                 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
                     if (occupied[item.screen][x][y] != null) {
                         Log.e(TAG, "Error loading shortcut " + item
-                            + " into cell (" + item.screen + ":" 
+                            + " into cell (" + item.screen + ":"
                             + x + "," + y
-                            + ") occupied by " 
+                            + ") occupied by "
                             + occupied[item.screen][x][y]);
                         return false;
                     }
@@ -610,7 +679,8 @@
             final Cursor c = contentResolver.query(
                     LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
 
-            final ItemInfo occupied[][][] = new ItemInfo[Launcher.SCREEN_COUNT][Launcher.NUMBER_CELLS_X][Launcher.NUMBER_CELLS_Y];
+            final ItemInfo occupied[][][] =
+                    new ItemInfo[Launcher.SCREEN_COUNT][mCellCountX][mCellCountY];
 
             try {
                 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
@@ -719,7 +789,6 @@
                             UserFolderInfo folderInfo = findOrMakeUserFolder(mFolders, id);
 
                             folderInfo.title = c.getString(titleIndex);
-
                             folderInfo.id = id;
                             container = c.getInt(containerIndex);
                             folderInfo.container = container;
@@ -731,7 +800,6 @@
                             if (!checkItemPlacement(occupied, folderInfo)) {
                                 break;
                             }
-
                             switch (container) {
                                 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
                                     mItems.add(folderInfo);
@@ -754,7 +822,6 @@
                                 itemsToRemove.add(id);
                             } else {
                                 LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(mFolders, id);
-
                                 intentDescription = c.getString(intentIndex);
                                 intent = null;
                                 if (intentDescription != null) {
@@ -800,7 +867,7 @@
 
                             final AppWidgetProviderInfo provider =
                                     widgets.getAppWidgetInfo(appWidgetId);
-                            
+
                             if (!isSafeMode && (provider == null || provider.provider == null ||
                                     provider.provider.getPackageName() == null)) {
                                 Log.e(TAG, "Deleting widget that isn't installed anymore: id="
@@ -861,13 +928,13 @@
             if (DEBUG_LOADERS) {
                 Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
                 Log.d(TAG, "workspace layout: ");
-                for (int y = 0; y < Launcher.NUMBER_CELLS_Y; y++) {
+                for (int y = 0; y < mCellCountY; y++) {
                     String line = "";
                     for (int s = 0; s < Launcher.SCREEN_COUNT; s++) {
                         if (s > 0) {
                             line += " | ";
                         }
-                        for (int x = 0; x < Launcher.NUMBER_CELLS_X; x++) {
+                        for (int x = 0; x < mCellCountX; x++) {
                             line += ((occupied[s][x][y] != null) ? "#" : ".");
                         }
                     }
@@ -1091,7 +1158,7 @@
                 startIndex = i;
                 for (int j=0; i<N && j<batchSize; j++) {
                     // This builds the icon bitmaps.
-                    mAllAppsList.add(new ApplicationInfo(apps.get(i), mIconCache));
+                    mAllAppsList.add(new ApplicationInfo(packageManager, apps.get(i), mIconCache));
                     i++;
                 }
 
@@ -1254,6 +1321,15 @@
                     }
                 });
             }
+
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    if (callbacks == mCallbacks.get()) {
+                        callbacks.bindPackagesUpdated();
+                    }
+                }
+            });
         }
     }
 
diff --git a/src/com/android/launcher2/LiveFolderInfo.java b/src/com/android/launcher2/LiveFolderInfo.java
index 7d0a0f5..74b0217 100644
--- a/src/com/android/launcher2/LiveFolderInfo.java
+++ b/src/com/android/launcher2/LiveFolderInfo.java
@@ -18,7 +18,6 @@
 
 import android.content.ContentValues;
 import android.content.Intent;
-import android.graphics.drawable.Drawable;
 import android.graphics.Bitmap;
 import android.net.Uri;
 
diff --git a/src/com/android/launcher2/PagedView.java b/src/com/android/launcher2/PagedView.java
new file mode 100644
index 0000000..5052a59
--- /dev/null
+++ b/src/com/android/launcher2/PagedView.java
@@ -0,0 +1,1017 @@
+/*
+ * 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.
+ */
+
+package com.android.launcher2;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.animation.Animation;
+import android.view.animation.Animation.AnimationListener;
+import android.view.animation.AnimationUtils;
+import android.widget.Scroller;
+
+import com.android.launcher.R;
+
+/**
+ * An abstraction of the original Workspace which supports browsing through a
+ * sequential list of "pages"
+ */
+public abstract class PagedView extends ViewGroup {
+    private static final String TAG = "PagedView";
+    protected static final int INVALID_PAGE = -1;
+
+    // the min drag distance for a fling to register, to prevent random page shifts
+    private static final int MIN_LENGTH_FOR_FLING = 50;
+
+    protected static final float NANOTIME_DIV = 1000000000.0f;
+
+    // the velocity at which a fling gesture will cause us to snap to the next page
+    protected int mSnapVelocity = 500;
+
+    protected float mSmoothingTime;
+    protected float mTouchX;
+
+    protected boolean mFirstLayout = true;
+
+    protected int mCurrentPage;
+    protected int mNextPage = INVALID_PAGE;
+    protected Scroller mScroller;
+    private VelocityTracker mVelocityTracker;
+
+    private float mDownMotionX;
+    private float mLastMotionX;
+    private float mLastMotionY;
+
+    protected final static int TOUCH_STATE_REST = 0;
+    protected final static int TOUCH_STATE_SCROLLING = 1;
+    protected final static int TOUCH_STATE_PREV_PAGE = 2;
+    protected final static int TOUCH_STATE_NEXT_PAGE = 3;
+
+    protected int mTouchState = TOUCH_STATE_REST;
+
+    protected OnLongClickListener mLongClickListener;
+
+    private boolean mAllowLongPress = true;
+
+    private int mTouchSlop;
+    private int mPagingTouchSlop;
+    private int mMaximumVelocity;
+
+    private static final int INVALID_POINTER = -1;
+
+    private int mActivePointerId = INVALID_POINTER;
+
+    private enum PageMovingState { PAGE_BEGIN_MOVING, PAGE_END_MOVING };
+    private PageSwitchListener mPageSwitchListener;
+    private PageMovingListener mPageMovingListener;
+
+    private ArrayList<Boolean> mDirtyPageContent;
+    private boolean mDirtyPageAlpha;
+
+    protected PagedViewIconCache mPageViewIconCache;
+
+    // If true, syncPages and syncPageItems will be called to refresh pages
+    protected boolean mContentIsRefreshable = true;
+
+    // If true, modify alpha of neighboring pages as user scrolls left/right
+    protected boolean mFadeInAdjacentScreens = true;
+
+    // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding
+    // to switch to a new page
+    protected boolean mUsePagingTouchSlop = true;
+
+    // If true, the subclass should directly update mScrollX itself in its computeScroll method
+    // (SmoothPagedView does this)
+    protected boolean mDeferScrollUpdate = false;
+
+    /**
+     * Simple cache mechanism for PagedViewIcon outlines.
+     */
+    class PagedViewIconCache {
+        private final HashMap<Object, Bitmap> iconOutlineCache = new HashMap<Object, Bitmap>();
+
+        public void clear() {
+            iconOutlineCache.clear();
+        }
+        public void addOutline(Object key, Bitmap b) {
+            iconOutlineCache.put(key, b);
+        }
+        public void removeOutline(Object key) {
+            if (iconOutlineCache.containsKey(key)) {
+                iconOutlineCache.remove(key);
+            }
+        }
+        public Bitmap getOutline(Object key) {
+            return iconOutlineCache.get(key);
+        }
+    }
+
+    public interface PageSwitchListener {
+        void onPageSwitch(View newPage, int newPageIndex);
+    }
+
+    public interface PageMovingListener {
+        void onPageBeginMoving();
+        void onPageEndMoving();
+    }
+
+    public PagedView(Context context) {
+        this(context, null);
+    }
+
+    public PagedView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public PagedView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        setHapticFeedbackEnabled(false);
+        init();
+    }
+
+    /**
+     * Initializes various states for this workspace.
+     */
+    protected void init() {
+        mDirtyPageContent = new ArrayList<Boolean>();
+        mDirtyPageContent.ensureCapacity(32);
+        mPageViewIconCache = new PagedViewIconCache();
+        mScroller = new Scroller(getContext());
+        mCurrentPage = 0;
+
+        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
+        mTouchSlop = configuration.getScaledTouchSlop();
+        mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
+        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+    }
+
+    public void setPageSwitchListener(PageSwitchListener pageSwitchListener) {
+        mPageSwitchListener = pageSwitchListener;
+        if (mPageSwitchListener != null) {
+            mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
+        }
+    }
+
+    /**
+     * Returns the index of the currently displayed page.
+     *
+     * @return The index of the currently displayed page.
+     */
+    int getCurrentPage() {
+        return mCurrentPage;
+    }
+
+    int getPageCount() {
+        return getChildCount();
+    }
+
+    View getPageAt(int index) {
+        return getChildAt(index);
+    }
+
+    int getScrollWidth() {
+        return getWidth();
+    }
+
+    /**
+     * Sets the current page.
+     */
+    void setCurrentPage(int currentPage) {
+        if (!mScroller.isFinished()) mScroller.abortAnimation();
+        if (getChildCount() == 0) return;
+
+        mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1));
+        scrollTo(getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage), 0);
+
+        invalidate();
+        notifyPageSwitchListener();
+    }
+
+    protected void notifyPageSwitchListener() {
+        if (mPageSwitchListener != null) {
+            mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
+        }
+    }
+
+    // a method that subclasses can override to add behavior
+    protected void pageBeginMoving() {
+    }
+
+    // a method that subclasses can override to add behavior
+    protected void pageEndMoving() {
+    }
+
+    /**
+     * Registers the specified listener on each page contained in this workspace.
+     *
+     * @param l The listener used to respond to long clicks.
+     */
+    @Override
+    public void setOnLongClickListener(OnLongClickListener l) {
+        mLongClickListener = l;
+        final int count = getPageCount();
+        for (int i = 0; i < count; i++) {
+            getPageAt(i).setOnLongClickListener(l);
+        }
+    }
+
+    @Override
+    public void scrollTo(int x, int y) {
+        super.scrollTo(x, y);
+        mTouchX = x;
+        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
+    }
+
+    // we moved this functionality to a helper function so SmoothPagedView can reuse it
+    protected boolean computeScrollHelper() {
+        if (mScroller.computeScrollOffset()) {
+            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
+            invalidate();
+            return true;
+        } else if (mNextPage != INVALID_PAGE) {
+            mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1));
+            mNextPage = INVALID_PAGE;
+            notifyPageSwitchListener();
+            pageEndMoving();
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void computeScroll() {
+        computeScrollHelper();
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+        if (widthMode != MeasureSpec.EXACTLY) {
+            throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
+        }
+
+        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+        if (heightMode != MeasureSpec.EXACTLY) {
+            throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
+        }
+
+        // The children are given the same width and height as the workspace
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
+        }
+
+        setMeasuredDimension(widthSize, heightSize);
+
+        if (mFirstLayout) {
+            setHorizontalScrollBarEnabled(false);
+            scrollTo(mCurrentPage * widthSize, 0);
+            mScroller.setFinalX(mCurrentPage * widthSize);
+            setHorizontalScrollBarEnabled(true);
+            mFirstLayout = false;
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        final int childCount = getChildCount();
+        int childLeft = 0;
+        if (childCount > 0) {
+            childLeft = (getMeasuredWidth() - getChildAt(0).getMeasuredWidth()) / 2;
+        }
+
+        for (int i = 0; i < childCount; i++) {
+            final View child = getChildAt(i);
+            if (child.getVisibility() != View.GONE) {
+                final int childWidth = child.getMeasuredWidth();
+                child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight());
+                childLeft += childWidth;
+            }
+        }
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        if (mFadeInAdjacentScreens) {
+            if (mDirtyPageAlpha || (mTouchState == TOUCH_STATE_SCROLLING) || !mScroller.isFinished()) {
+                int screenCenter = mScrollX + (getMeasuredWidth() / 2);
+                final int childCount = getChildCount();
+                for (int i = 0; i < childCount; ++i) {
+                    View layout = (View) getChildAt(i);
+                    int childWidth = layout.getMeasuredWidth();
+                    int halfChildWidth = (childWidth / 2);
+                    int childCenter = getChildOffset(i) + halfChildWidth;
+                    int distanceFromScreenCenter = Math.abs(childCenter - screenCenter);
+                    float alpha = 0.0f;
+                    if (distanceFromScreenCenter < halfChildWidth) {
+                        alpha = 1.0f;
+                    } else if (distanceFromScreenCenter > childWidth) {
+                        alpha = 0.0f;
+                    } else {
+                        float dimAlpha = (float) (distanceFromScreenCenter - halfChildWidth) / halfChildWidth;
+                        dimAlpha = Math.max(0.0f, Math.min(1.0f, (dimAlpha * dimAlpha)));
+                        alpha = 1.0f - dimAlpha;
+                    }
+                    if (Float.compare(alpha, layout.getAlpha()) != 0) {
+                        layout.setAlpha(alpha);
+                    }
+                }
+                mDirtyPageAlpha = false;
+            }
+        }
+
+        // Find out which screens are visible; as an optimization we only call draw on them
+
+        // As an optimization, this code assumes that all pages have the same width as the 0th
+        // page.
+        final int pageWidth = getChildAt(0).getMeasuredWidth();
+        final int pageCount = getChildCount();
+        final int screenWidth = getMeasuredWidth();
+        int x = getRelativeChildOffset(0) + pageWidth;
+        int leftScreen = 0;
+        int rightScreen = 0;
+        while (x <= mScrollX) {
+            leftScreen++;
+            x += pageWidth;
+            // replace above line with this if you don't assume all pages have same width as 0th
+            // page:
+            // x += getChildAt(leftScreen).getMeasuredWidth();
+        }
+        rightScreen = leftScreen;
+        while (x < mScrollX + screenWidth) {
+            rightScreen++;
+            x += pageWidth;
+            // replace above line with this if you don't assume all pages have same width as 0th
+            // page:
+            //if (rightScreen < pageCount) {
+            //    x += getChildAt(rightScreen).getMeasuredWidth();
+            //}
+        }
+        rightScreen = Math.min(getChildCount() - 1, rightScreen);
+
+        final long drawingTime = getDrawingTime();
+        for (int i = leftScreen; i <= rightScreen; i++) {
+            drawChild(canvas, getChildAt(i), drawingTime);
+        }
+    }
+
+    @Override
+    public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
+        int page = indexOfChild(child);
+        if (page != mCurrentPage || !mScroller.isFinished()) {
+            snapToPage(page);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
+        int focusablePage;
+        if (mNextPage != INVALID_PAGE) {
+            focusablePage = mNextPage;
+        } else {
+            focusablePage = mCurrentPage;
+        }
+        View v = getPageAt(focusablePage);
+        if (v != null) {
+            v.requestFocus(direction, previouslyFocusedRect);
+        }
+        return false;
+    }
+
+    @Override
+    public boolean dispatchUnhandledMove(View focused, int direction) {
+        if (direction == View.FOCUS_LEFT) {
+            if (getCurrentPage() > 0) {
+                snapToPage(getCurrentPage() - 1);
+                return true;
+            }
+        } else if (direction == View.FOCUS_RIGHT) {
+            if (getCurrentPage() < getPageCount() - 1) {
+                snapToPage(getCurrentPage() + 1);
+                return true;
+            }
+        }
+        return super.dispatchUnhandledMove(focused, direction);
+    }
+
+    @Override
+    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
+        if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) {
+            getPageAt(mCurrentPage).addFocusables(views, direction);
+        }
+        if (direction == View.FOCUS_LEFT) {
+            if (mCurrentPage > 0) {
+                getPageAt(mCurrentPage - 1).addFocusables(views, direction);
+            }
+        } else if (direction == View.FOCUS_RIGHT){
+            if (mCurrentPage < getPageCount() - 1) {
+                getPageAt(mCurrentPage + 1).addFocusables(views, direction);
+            }
+        }
+    }
+
+    /**
+     * If one of our descendant views decides that it could be focused now, only
+     * pass that along if it's on the current page.
+     *
+     * This happens when live folders requery, and if they're off page, they
+     * end up calling requestFocus, which pulls it on page.
+     */
+    @Override
+    public void focusableViewAvailable(View focused) {
+        View current = getPageAt(mCurrentPage);
+        View v = focused;
+        while (true) {
+            if (v == current) {
+                super.focusableViewAvailable(focused);
+                return;
+            }
+            if (v == this) {
+                return;
+            }
+            ViewParent parent = v.getParent();
+            if (parent instanceof View) {
+                v = (View)v.getParent();
+            } else {
+                return;
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+        if (disallowIntercept) {
+            // We need to make sure to cancel our long press if
+            // a scrollable widget takes over touch events
+            final View currentPage = getChildAt(mCurrentPage);
+            currentPage.cancelLongPress();
+        }
+        super.requestDisallowInterceptTouchEvent(disallowIntercept);
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        /*
+         * This method JUST determines whether we want to intercept the motion.
+         * If we return true, onTouchEvent will be called and we do the actual
+         * scrolling there.
+         */
+
+        /*
+         * Shortcut the most recurring case: the user is in the dragging
+         * state and he is moving his finger.  We want to intercept this
+         * motion.
+         */
+        final int action = ev.getAction();
+        if ((action == MotionEvent.ACTION_MOVE) &&
+                (mTouchState == TOUCH_STATE_SCROLLING)) {
+            return true;
+        }
+
+
+        switch (action & MotionEvent.ACTION_MASK) {
+            case MotionEvent.ACTION_MOVE: {
+                /*
+                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
+                 * whether the user has moved far enough from his original down touch.
+                 */
+                determineScrollingStart(ev);
+                break;
+            }
+
+            case MotionEvent.ACTION_DOWN: {
+                final float x = ev.getX();
+                final float y = ev.getY();
+                // Remember location of down touch
+                mDownMotionX = x;
+                mLastMotionX = x;
+                mLastMotionY = y;
+                mActivePointerId = ev.getPointerId(0);
+                mAllowLongPress = true;
+
+                /*
+                 * If being flinged and user touches the screen, initiate drag;
+                 * otherwise don't.  mScroller.isFinished should be false when
+                 * being flinged.
+                 */
+                mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;
+
+                // check if this can be the beginning of a tap on the side of the pages
+                // to scroll the current page
+                if ((mTouchState != TOUCH_STATE_PREV_PAGE) &&
+                        (mTouchState != TOUCH_STATE_NEXT_PAGE)) {
+                    if (getChildCount() > 0) {
+                        int relativeChildLeft = getChildOffset(0);
+                        int relativeChildRight = relativeChildLeft + getChildAt(0).getMeasuredWidth();
+                        if (x < relativeChildLeft) {
+                            mTouchState = TOUCH_STATE_PREV_PAGE;
+                        } else if (x > relativeChildRight) {
+                            mTouchState = TOUCH_STATE_NEXT_PAGE;
+                        }
+                    }
+                }
+                break;
+            }
+
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                // Release the drag
+                pageEndMoving();
+                mTouchState = TOUCH_STATE_REST;
+                mAllowLongPress = false;
+                mActivePointerId = INVALID_POINTER;
+
+                break;
+
+            case MotionEvent.ACTION_POINTER_UP:
+                onSecondaryPointerUp(ev);
+                break;
+        }
+
+        /*
+         * The only time we want to intercept motion events is if we are in the
+         * drag mode.
+         */
+        return mTouchState != TOUCH_STATE_REST;
+    }
+
+    protected void animateClickFeedback(View v, final Runnable r) {
+        // animate the view slightly to show click feedback running some logic after it is "pressed"
+        Animation anim = AnimationUtils.loadAnimation(getContext(), 
+                R.anim.paged_view_click_feedback);
+        anim.setAnimationListener(new AnimationListener() {
+            @Override
+            public void onAnimationStart(Animation animation) {}
+            @Override
+            public void onAnimationRepeat(Animation animation) {
+                r.run();
+            }
+            @Override
+            public void onAnimationEnd(Animation animation) {}
+        });
+        v.startAnimation(anim);
+    }
+
+    /*
+     * Determines if we should change the touch state to start scrolling after the
+     * user moves their touch point too far.
+     */
+    private void determineScrollingStart(MotionEvent ev) {
+        /*
+         * Locally do absolute value. mLastMotionX is set to the y value
+         * of the down event.
+         */
+        final int pointerIndex = ev.findPointerIndex(mActivePointerId);
+        final float x = ev.getX(pointerIndex);
+        final float y = ev.getY(pointerIndex);
+        final int xDiff = (int) Math.abs(x - mLastMotionX);
+        final int yDiff = (int) Math.abs(y - mLastMotionY);
+
+        final int touchSlop = mTouchSlop;
+        boolean xPaged = xDiff > mPagingTouchSlop;
+        boolean xMoved = xDiff > touchSlop;
+        boolean yMoved = yDiff > touchSlop;
+
+        if (xMoved || yMoved) {
+            if (mUsePagingTouchSlop ? xPaged : xMoved) {
+                // Scroll if the user moved far enough along the X axis
+                mTouchState = TOUCH_STATE_SCROLLING;
+                mLastMotionX = x;
+                mTouchX = mScrollX;
+                mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
+                pageBeginMoving();
+            }
+            // Either way, cancel any pending longpress
+            if (mAllowLongPress) {
+                mAllowLongPress = false;
+                // Try canceling the long press. It could also have been scheduled
+                // by a distant descendant, so use the mAllowLongPress flag to block
+                // everything
+                final View currentPage = getPageAt(mCurrentPage);
+                currentPage.cancelLongPress();
+            }
+        }
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        }
+        mVelocityTracker.addMovement(ev);
+
+        final int action = ev.getAction();
+
+        switch (action & MotionEvent.ACTION_MASK) {
+        case MotionEvent.ACTION_DOWN:
+            /*
+             * If being flinged and user touches, stop the fling. isFinished
+             * will be false if being flinged.
+             */
+            if (!mScroller.isFinished()) {
+                mScroller.abortAnimation();
+            }
+
+            // Remember where the motion event started
+            mDownMotionX = mLastMotionX = ev.getX();
+            mActivePointerId = ev.getPointerId(0);
+            if (mTouchState == TOUCH_STATE_SCROLLING) {
+                pageBeginMoving();
+            }
+            break;
+
+        case MotionEvent.ACTION_MOVE:
+            if (mTouchState == TOUCH_STATE_SCROLLING) {
+                // Scroll to follow the motion event
+                final int pointerIndex = ev.findPointerIndex(mActivePointerId);
+                final float x = ev.getX(pointerIndex);
+                final int deltaX = (int) (mLastMotionX - x);
+                mLastMotionX = x;
+
+                int sx = getScrollX();
+                if (deltaX < 0) {
+                    if (sx > 0) {
+                        mTouchX += Math.max(-mTouchX, deltaX);
+                        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
+                        if (!mDeferScrollUpdate) {
+                            scrollBy(Math.max(-sx, deltaX), 0);
+                        } else {
+                            // This will trigger a call to computeScroll() on next drawChild() call
+                            invalidate();
+                        }
+                    }
+                } else if (deltaX > 0) {
+                    final int lastChildIndex = getChildCount() - 1;
+                    final int availableToScroll = getChildOffset(lastChildIndex) -
+                        getRelativeChildOffset(lastChildIndex) - sx;
+                    if (availableToScroll > 0) {
+                        mTouchX += Math.min(availableToScroll, deltaX);
+                        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
+                        if (!mDeferScrollUpdate) {
+                            scrollBy(Math.min(availableToScroll, deltaX), 0);
+                        } else {
+                            // This will trigger a call to computeScroll() on next drawChild() call
+                            invalidate();
+                        }
+                    }
+                } else {
+                    awakenScrollBars();
+                }
+            } else if ((mTouchState == TOUCH_STATE_PREV_PAGE) ||
+                    (mTouchState == TOUCH_STATE_NEXT_PAGE)) {
+                determineScrollingStart(ev);
+            }
+            break;
+
+        case MotionEvent.ACTION_UP:
+            if (mTouchState == TOUCH_STATE_SCROLLING) {
+                final int activePointerId = mActivePointerId;
+                final int pointerIndex = ev.findPointerIndex(activePointerId);
+                final float x = ev.getX(pointerIndex);
+                final VelocityTracker velocityTracker = mVelocityTracker;
+                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+                int velocityX = (int) velocityTracker.getXVelocity(activePointerId);
+                boolean isfling = Math.abs(mDownMotionX - x) > MIN_LENGTH_FOR_FLING;
+
+                final int snapVelocity = mSnapVelocity;
+                if (isfling && velocityX > snapVelocity && mCurrentPage > 0) {
+                    snapToPageWithVelocity(mCurrentPage - 1, velocityX);
+                } else if (isfling && velocityX < -snapVelocity &&
+                        mCurrentPage < getChildCount() - 1) {
+                    snapToPageWithVelocity(mCurrentPage + 1, velocityX);
+                } else {
+                    snapToDestination();
+                }
+
+                if (mVelocityTracker != null) {
+                    mVelocityTracker.recycle();
+                    mVelocityTracker = null;
+                }
+            } else if (mTouchState == TOUCH_STATE_PREV_PAGE) {
+                // at this point we have not moved beyond the touch slop
+                // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
+                // we can just page
+                int nextPage = Math.max(0, mCurrentPage - 1);
+                if (nextPage != mCurrentPage) {
+                    snapToPage(nextPage);
+                } else {
+                    snapToDestination();
+                }
+            } else if (mTouchState == TOUCH_STATE_NEXT_PAGE) {
+                // at this point we have not moved beyond the touch slop
+                // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
+                // we can just page
+                int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1);
+                if (nextPage != mCurrentPage) {
+                    snapToPage(nextPage);
+                } else {
+                    snapToDestination();
+                }
+            }
+            mTouchState = TOUCH_STATE_REST;
+            mActivePointerId = INVALID_POINTER;
+            break;
+
+        case MotionEvent.ACTION_CANCEL:
+            mTouchState = TOUCH_STATE_REST;
+            mActivePointerId = INVALID_POINTER;
+            break;
+
+        case MotionEvent.ACTION_POINTER_UP:
+            onSecondaryPointerUp(ev);
+            break;
+        }
+
+        return true;
+    }
+
+    private void onSecondaryPointerUp(MotionEvent ev) {
+        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
+                MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+        final int pointerId = ev.getPointerId(pointerIndex);
+        if (pointerId == mActivePointerId) {
+            // This was our active pointer going up. Choose a new
+            // active pointer and adjust accordingly.
+            // TODO: Make this decision more intelligent.
+            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
+            mLastMotionX = mDownMotionX = ev.getX(newPointerIndex);
+            mLastMotionY = ev.getY(newPointerIndex);
+            mActivePointerId = ev.getPointerId(newPointerIndex);
+            if (mVelocityTracker != null) {
+                mVelocityTracker.clear();
+            }
+        }
+    }
+
+    @Override
+    public void requestChildFocus(View child, View focused) {
+        super.requestChildFocus(child, focused);
+        int page = indexOfChild(child);
+        if (page >= 0 && !isInTouchMode()) {
+            snapToPage(page);
+        }
+    }
+
+    protected int getRelativeChildOffset(int index) {
+        return (getMeasuredWidth() - getChildAt(index).getMeasuredWidth()) / 2;
+    }
+
+    protected int getChildOffset(int index) {
+        if (getChildCount() == 0)
+            return 0;
+
+        int offset = getRelativeChildOffset(0);
+        for (int i = 0; i < index; ++i) {
+            offset += getChildAt(i).getMeasuredWidth();
+        }
+        return offset;
+    }
+
+    protected void snapToDestination() {
+        int minDistanceFromScreenCenter = getMeasuredWidth();
+        int minDistanceFromScreenCenterIndex = -1;
+        int screenCenter = mScrollX + (getMeasuredWidth() / 2);
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; ++i) {
+            View layout = (View) getChildAt(i);
+            int childWidth = layout.getMeasuredWidth();
+            int halfChildWidth = (childWidth / 2);
+            int childCenter = getChildOffset(i) + halfChildWidth;
+            int distanceFromScreenCenter = Math.abs(childCenter - screenCenter);
+            if (distanceFromScreenCenter < minDistanceFromScreenCenter) {
+                minDistanceFromScreenCenter = distanceFromScreenCenter;
+                minDistanceFromScreenCenterIndex = i;
+            }
+        }
+        snapToPage(minDistanceFromScreenCenterIndex, 1000);
+    }
+
+    protected void snapToPageWithVelocity(int whichPage, int velocity) {
+        // We ignore velocity in this implementation, but children (e.g. SmoothPagedView)
+        // can use it
+        snapToPage(whichPage);
+    }
+
+    protected void snapToPage(int whichPage) {
+        snapToPage(whichPage, 1000);
+    }
+
+    protected void snapToPage(int whichPage, int duration) {
+        whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1));
+
+
+        int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
+        final int sX = getScrollX();
+        final int delta = newX - sX;
+        snapToPage(whichPage, delta, duration);
+    }
+
+    protected void snapToPage(int whichPage, int delta, int duration) {
+        mNextPage = whichPage;
+
+        View focusedChild = getFocusedChild();
+        if (focusedChild != null && whichPage != mCurrentPage &&
+                focusedChild == getChildAt(mCurrentPage)) {
+            focusedChild.clearFocus();
+        }
+
+        pageBeginMoving();
+        awakenScrollBars(duration);
+        if (duration == 0) {
+            duration = Math.abs(delta);
+        }
+
+        if (!mScroller.isFinished()) mScroller.abortAnimation();
+        mScroller.startScroll(getScrollX(), 0, delta, 0, duration);
+
+        // only load some associated pages
+        loadAssociatedPages(mNextPage);
+        notifyPageSwitchListener();
+        invalidate();
+    }
+
+    @Override
+    protected Parcelable onSaveInstanceState() {
+        final SavedState state = new SavedState(super.onSaveInstanceState());
+        state.currentPage = mCurrentPage;
+        return state;
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Parcelable state) {
+        SavedState savedState = (SavedState) state;
+        super.onRestoreInstanceState(savedState.getSuperState());
+        if (savedState.currentPage != -1) {
+            mCurrentPage = savedState.currentPage;
+        }
+    }
+
+    public void scrollLeft() {
+        if (mScroller.isFinished()) {
+            if (mCurrentPage > 0) snapToPage(mCurrentPage - 1);
+        } else {
+            if (mNextPage > 0) snapToPage(mNextPage - 1);
+        }
+    }
+
+    public void scrollRight() {
+        if (mScroller.isFinished()) {
+            if (mCurrentPage < getChildCount() -1) snapToPage(mCurrentPage + 1);
+        } else {
+            if (mNextPage < getChildCount() -1) snapToPage(mNextPage + 1);
+        }
+    }
+
+    public int getPageForView(View v) {
+        int result = -1;
+        if (v != null) {
+            ViewParent vp = v.getParent();
+            int count = getChildCount();
+            for (int i = 0; i < count; i++) {
+                if (vp == getChildAt(i)) {
+                    return i;
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @return True is long presses are still allowed for the current touch
+     */
+    public boolean allowLongPress() {
+        return mAllowLongPress;
+    }
+
+    /**
+     * Set true to allow long-press events to be triggered, usually checked by
+     * {@link Launcher} to accept or block dpad-initiated long-presses.
+     */
+    public void setAllowLongPress(boolean allowLongPress) {
+        mAllowLongPress = allowLongPress;
+    }
+
+    public static class SavedState extends BaseSavedState {
+        int currentPage = -1;
+
+        SavedState(Parcelable superState) {
+            super(superState);
+        }
+
+        private SavedState(Parcel in) {
+            super(in);
+            currentPage = in.readInt();
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            super.writeToParcel(out, flags);
+            out.writeInt(currentPage);
+        }
+
+        public static final Parcelable.Creator<SavedState> CREATOR =
+                new Parcelable.Creator<SavedState>() {
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in);
+            }
+
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
+    }
+
+    public void loadAssociatedPages(int page) {
+        if (mContentIsRefreshable) {
+            final int count = getChildCount();
+            if (page < count) {
+                int lowerPageBound = Math.max(0, page - 1);
+                int upperPageBound = Math.min(page + 1, count - 1);
+                for (int i = 0; i < count; ++i) {
+                    final ViewGroup layout = (ViewGroup) getChildAt(i);
+                    final int childCount = layout.getChildCount();
+                    if (lowerPageBound <= i && i <= upperPageBound) {
+                        if (mDirtyPageContent.get(i)) {
+                            syncPageItems(i);
+                            mDirtyPageContent.set(i, false);
+                        }
+                    } else {
+                        if (childCount > 0) {
+                            layout.removeAllViews();
+                        }
+                        mDirtyPageContent.set(i, true);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * This method is called ONLY to synchronize the number of pages that the paged view has.
+     * To actually fill the pages with information, implement syncPageItems() below.  It is
+     * guaranteed that syncPageItems() will be called for a particular page before it is shown,
+     * and therefore, individual page items do not need to be updated in this method.
+     */
+    public abstract void syncPages();
+
+    /**
+     * This method is called to synchronize the items that are on a particular page.  If views on
+     * the page can be reused, then they should be updated within this method.
+     */
+    public abstract void syncPageItems(int page);
+
+    public void invalidatePageData() {
+        if (mContentIsRefreshable) {
+            // Update all the pages
+            syncPages();
+
+            // Mark each of the pages as dirty
+            final int count = getChildCount();
+            mDirtyPageContent.clear();
+            for (int i = 0; i < count; ++i) {
+                mDirtyPageContent.add(true);
+            }
+
+            // Load any pages that are necessary for the current window of views
+            loadAssociatedPages(mCurrentPage);
+            mDirtyPageAlpha = true;
+            requestLayout();
+        }
+    }
+}
diff --git a/src/com/android/launcher2/PagedViewCellLayout.java b/src/com/android/launcher2/PagedViewCellLayout.java
new file mode 100644
index 0000000..219a362
--- /dev/null
+++ b/src/com/android/launcher2/PagedViewCellLayout.java
@@ -0,0 +1,436 @@
+/*
+ * 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.
+ */
+
+package com.android.launcher2;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewGroup;
+
+/**
+ * An abstraction of the original CellLayout which supports laying out items
+ * which span multiple cells into a grid-like layout.  Also supports dimming
+ * to give a preview of its contents.
+ */
+public class PagedViewCellLayout extends ViewGroup {
+    static final String TAG = "PagedViewCellLayout";
+
+    private float mHolographicAlpha;
+
+    private boolean mCenterContent;
+
+    private int mCellCountX;
+    private int mCellCountY;
+    private int mCellWidth;
+    private int mCellHeight;
+    private static int sDefaultCellDimensions = 96;
+
+    public PagedViewCellLayout(Context context) {
+        this(context, null);
+    }
+
+    public PagedViewCellLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public PagedViewCellLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        setAlwaysDrawnWithCacheEnabled(false);
+
+        // setup default cell parameters
+        mCellWidth = mCellHeight = sDefaultCellDimensions;
+        mCellCountX = LauncherModel.getCellCountX();
+        mCellCountY = LauncherModel.getCellCountY();
+        mHolographicAlpha = 0.0f;
+    }
+
+    @Override
+    protected boolean onSetAlpha(int alpha) {
+        return true;
+    }
+
+    @Override
+    public void setAlpha(float alpha) {
+        mHolographicAlpha = 1.0f - alpha;
+        setChildrenAlpha(alpha);
+        super.setAlpha(alpha);
+    }
+
+    @Override
+    public void cancelLongPress() {
+        super.cancelLongPress();
+
+        // Cancel long press for all children
+        final int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+            child.cancelLongPress();
+        }
+    }
+
+    public boolean addViewToCellLayout(View child, int index, int childId,
+            PagedViewCellLayout.LayoutParams params) {
+        final PagedViewCellLayout.LayoutParams lp = params;
+
+        // Generate an id for each view, this assumes we have at most 256x256 cells
+        // per workspace screen
+        if (lp.cellX >= 0 && lp.cellX <= (mCellCountX - 1) &&
+                lp.cellY >= 0 && (lp.cellY <= mCellCountY - 1)) {
+            // If the horizontal or vertical span is set to -1, it is taken to
+            // mean that it spans the extent of the CellLayout
+            if (lp.cellHSpan < 0) lp.cellHSpan = mCellCountX;
+            if (lp.cellVSpan < 0) lp.cellVSpan = mCellCountY;
+
+            child.setId(childId);
+
+            // We might be in the middle or end of shrinking/fading to a dimmed view
+            // Make sure this view's alpha is set the same as all the rest of the views
+            child.setAlpha(1.0f - mHolographicAlpha);
+
+            addView(child, index, lp);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void requestChildFocus(View child, View focused) {
+        super.requestChildFocus(child, focused);
+        if (child != null) {
+            Rect r = new Rect();
+            child.getDrawingRect(r);
+            requestRectangleOnScreen(r);
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        // TODO: currently ignoring padding
+
+        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
+        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
+
+        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
+        int heightSpecSize =  MeasureSpec.getSize(heightMeasureSpec);
+
+        if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
+            throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions");
+        }
+
+        final int cellWidth = mCellWidth;
+        final int cellHeight = mCellHeight;
+
+        int numWidthGaps = mCellCountX - 1;
+        int numHeightGaps = mCellCountY - 1;
+
+        int vSpaceLeft = heightSpecSize - mPaddingTop
+                - mPaddingBottom - (cellHeight * mCellCountY);
+        int heightGap = vSpaceLeft / numHeightGaps;
+
+        int hSpaceLeft = widthSpecSize - mPaddingLeft
+                - mPaddingRight - (cellWidth * mCellCountX);
+        int widthGap = hSpaceLeft / numWidthGaps;
+
+        // center it around the min gaps
+        int minGap = Math.min(widthGap, heightGap);
+        int paddingLeft = mPaddingLeft;
+        int paddingTop = mPaddingTop;
+        /*
+        if (minGap < heightGap) {
+            // vertical space has shrunken, so change padding accordingly
+            paddingTop += ((heightGap - minGap) * (mCellCountY - 1)) / 2;
+        } else if (minGap < widthGap) {
+            // horizontal space has shrunken, so change padding accordingly
+            paddingLeft += ((widthGap - minGap) * (mCellCountX - 1)) / 2;
+        }
+        */
+        widthGap = heightGap = minGap;
+
+        int newWidth = mPaddingLeft + mPaddingRight + (mCellCountX * cellWidth) +
+            ((mCellCountX - 1) * minGap);
+        int newHeight = mPaddingTop + mPaddingBottom + (mCellCountY * cellHeight) +
+            ((mCellCountY - 1) * minGap);
+
+        final int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            View child = getChildAt(i);
+            PagedViewCellLayout.LayoutParams lp =
+                (PagedViewCellLayout.LayoutParams) child.getLayoutParams();
+            lp.setup(cellWidth, cellHeight, widthGap, heightGap,
+                    paddingLeft, paddingTop);
+
+            int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width,
+                    MeasureSpec.EXACTLY);
+            int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height,
+                    MeasureSpec.EXACTLY);
+
+            child.measure(childWidthMeasureSpec, childheightMeasureSpec);
+        }
+
+        setMeasuredDimension(newWidth, newHeight);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        int count = getChildCount();
+
+        int offsetX = 0;
+        if (mCenterContent) {
+            // determine the max width of all the rows and center accordingly
+            int maxRowWidth = 0;
+            for (int i = 0; i < count; i++) {
+                View child = getChildAt(i);
+                if (child.getVisibility() != GONE) {
+                    PagedViewCellLayout.LayoutParams lp =
+                        (PagedViewCellLayout.LayoutParams) child.getLayoutParams();
+                    maxRowWidth = Math.max(maxRowWidth, lp.x + lp.width);
+                }
+            }
+            offsetX = (getMeasuredWidth() / 2) - (maxRowWidth / 2);
+        }
+
+        for (int i = 0; i < count; i++) {
+            View child = getChildAt(i);
+            if (child.getVisibility() != GONE) {
+                PagedViewCellLayout.LayoutParams lp =
+                    (PagedViewCellLayout.LayoutParams) child.getLayoutParams();
+
+                int childLeft = offsetX + lp.x;
+                int childTop = lp.y;
+                child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
+            }
+        }
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        return super.onTouchEvent(event) || true;
+    }
+
+    public void enableCenteredContent(boolean enabled) {
+        mCenterContent = enabled;
+    }
+
+    @Override
+    protected void setChildrenDrawingCacheEnabled(boolean enabled) {
+        final int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            final View view = getChildAt(i);
+            view.setDrawingCacheEnabled(enabled);
+            // Update the drawing caches
+            view.buildDrawingCache(true);
+        }
+    }
+
+    public void setCellCount(int xCount, int yCount) {
+        mCellCountX = xCount;
+        mCellCountY = yCount;
+        requestLayout();
+    }
+
+    private void setChildrenAlpha(float alpha) {
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            getChildAt(i).setAlpha(alpha);
+        }
+    }
+
+    public int[] getCellCountForDimensions(int width, int height) {
+        // Always assume we're working with the smallest span to make sure we
+        // reserve enough space in both orientations
+        int smallerSize = Math.min(mCellWidth, mCellHeight);
+
+        // Always round up to next largest cell
+        int spanX = (width + smallerSize) / smallerSize;
+        int spanY = (height + smallerSize) / smallerSize;
+
+        return new int[] { spanX, spanY };
+    }
+
+    /**
+     * Start dragging the specified child
+     *
+     * @param child The child that is being dragged
+     */
+    void onDragChild(View child) {
+        PagedViewCellLayout.LayoutParams lp = (PagedViewCellLayout.LayoutParams) child.getLayoutParams();
+        lp.isDragging = true;
+    }
+
+    public int estimateCellHSpan(int width) {
+        return (width + mCellWidth) / mCellWidth;
+    }
+    public int estimateCellVSpan(int height) {
+        return (height + mCellHeight) / mCellHeight;
+    }
+    public int[] estimateCellDimensions(int approxWidth, int approxHeight, 
+            int cellHSpan, int cellVSpan) {
+        // NOTE: we are disabling this until we find a good way to approximate this without fully
+        // measuring
+        /*
+        // we may want to use this before any measuring/layout happens, so we pass in an approximate
+        // size for the layout
+        int numWidthGaps = mCellCountX - 1;
+        int numHeightGaps = mCellCountY - 1;
+        int hSpaceLeft = approxWidth - mPaddingLeft
+            - mPaddingRight - (mCellWidth * mCellCountX);
+        int vSpaceLeft = approxHeight - mPaddingTop
+            - mPaddingBottom - (mCellHeight * mCellCountY);
+        int widthGap = hSpaceLeft / numWidthGaps;
+        int heightGap = vSpaceLeft / numHeightGaps;
+        int minGap = Math.min(widthGap, heightGap);
+        return new int[] {
+            (cellHSpan * mCellWidth) + ((cellHSpan - 1) * minGap),
+            (cellVSpan * mCellHeight) + ((cellVSpan - 1) * minGap)
+        };
+        */
+        return new int[] {
+            (cellHSpan * mCellWidth),
+            (cellVSpan * mCellHeight)
+        };
+    }
+
+    @Override
+    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
+        return new PagedViewCellLayout.LayoutParams(getContext(), attrs);
+    }
+
+    @Override
+    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+        return p instanceof PagedViewCellLayout.LayoutParams;
+    }
+
+    @Override
+    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+        return new PagedViewCellLayout.LayoutParams(p);
+    }
+
+    public static class LayoutParams extends ViewGroup.MarginLayoutParams {
+        /**
+         * Horizontal location of the item in the grid.
+         */
+        @ViewDebug.ExportedProperty
+        public int cellX;
+
+        /**
+         * Vertical location of the item in the grid.
+         */
+        @ViewDebug.ExportedProperty
+        public int cellY;
+
+        /**
+         * Number of cells spanned horizontally by the item.
+         */
+        @ViewDebug.ExportedProperty
+        public int cellHSpan;
+
+        /**
+         * Number of cells spanned vertically by the item.
+         */
+        @ViewDebug.ExportedProperty
+        public int cellVSpan;
+
+        /**
+         * Is this item currently being dragged
+         */
+        public boolean isDragging;
+
+        // a data object that you can bind to this layout params
+        private Object mTag;
+
+        // X coordinate of the view in the layout.
+        @ViewDebug.ExportedProperty
+        int x;
+        // Y coordinate of the view in the layout.
+        @ViewDebug.ExportedProperty
+        int y;
+
+        public LayoutParams() {
+            super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+            cellHSpan = 1;
+            cellVSpan = 1;
+        }
+
+        public LayoutParams(Context c, AttributeSet attrs) {
+            super(c, attrs);
+            cellHSpan = 1;
+            cellVSpan = 1;
+        }
+
+        public LayoutParams(ViewGroup.LayoutParams source) {
+            super(source);
+            cellHSpan = 1;
+            cellVSpan = 1;
+        }
+
+        public LayoutParams(LayoutParams source) {
+            super(source);
+            this.cellX = source.cellX;
+            this.cellY = source.cellY;
+            this.cellHSpan = source.cellHSpan;
+            this.cellVSpan = source.cellVSpan;
+        }
+
+        public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
+            super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+            this.cellX = cellX;
+            this.cellY = cellY;
+            this.cellHSpan = cellHSpan;
+            this.cellVSpan = cellVSpan;
+        }
+
+        public void setup(int cellWidth, int cellHeight, int widthGap, int heightGap,
+                int hStartPadding, int vStartPadding) {
+
+            final int myCellHSpan = cellHSpan;
+            final int myCellVSpan = cellVSpan;
+            final int myCellX = cellX;
+            final int myCellY = cellY;
+
+            width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) -
+                    leftMargin - rightMargin;
+            height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) -
+                    topMargin - bottomMargin;
+
+            x = hStartPadding + myCellX * (cellWidth + widthGap) + leftMargin;
+            y = vStartPadding + myCellY * (cellHeight + heightGap) + topMargin;
+        }
+
+        public Object getTag() {
+            return mTag;
+        }
+
+        public void setTag(Object tag) {
+            mTag = tag;
+        }
+
+        public String toString() {
+            return "(" + this.cellX + ", " + this.cellY + ", " +
+                this.cellHSpan + ", " + this.cellVSpan + ")";
+        }
+    }
+}
diff --git a/src/com/android/launcher2/PagedViewIcon.java b/src/com/android/launcher2/PagedViewIcon.java
new file mode 100644
index 0000000..e227569
--- /dev/null
+++ b/src/com/android/launcher2/PagedViewIcon.java
@@ -0,0 +1,278 @@
+/*
+ * 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.
+ */
+
+package com.android.launcher2;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BlurMaskFilter;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PointF;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.Region.Op;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.widget.Checkable;
+import android.widget.TextView;
+
+import com.android.launcher2.PagedView.PagedViewIconCache;
+
+class HolographicOutlineHelper {
+    private final Paint mHolographicPaint = new Paint();
+    private final Paint mBlurPaint = new Paint();
+    private final Paint mErasePaint = new Paint();
+    private static final Matrix mIdentity = new Matrix();
+    private static final float STROKE_WIDTH = 6.0f;
+    private static final float BLUR_FACTOR = 3.5f;
+
+    public static final int HOLOGRAPHIC_BLUE = 0xFF6699FF;
+    public static final int HOLOGRAPHIC_GREEN = 0xFF66FF66;
+
+    HolographicOutlineHelper(float density) {
+        mHolographicPaint.setColor(HOLOGRAPHIC_BLUE);
+        mHolographicPaint.setFilterBitmap(true);
+        mHolographicPaint.setAntiAlias(true);
+        mBlurPaint.setMaskFilter(new BlurMaskFilter(BLUR_FACTOR * density, 
+                BlurMaskFilter.Blur.OUTER));
+        mBlurPaint.setFilterBitmap(true);
+        mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
+        mErasePaint.setFilterBitmap(true);
+        mErasePaint.setAntiAlias(true);
+    }
+
+    private float cubic(float r) {
+        return (float) (Math.pow(r-1, 3) + 1);
+    }
+
+    /**
+     * Returns the interpolated holographic highlight alpha for the effect we want when scrolling 
+     * pages.
+     */
+    public float highlightAlphaInterpolator(float r) {
+        final float pivot = 0.3f;
+        if (r < pivot) {
+            return Math.max(0.5f, 0.65f*cubic(r/pivot));
+        } else {
+            return Math.min(1.0f, 0.65f*cubic(1 - (r-pivot)/(1-pivot)));
+        }
+    }
+
+    /**
+     * Returns the interpolated view alpha for the effect we want when scrolling pages.
+     */
+    public float viewAlphaInterpolator(float r) {
+        final float pivot = 0.6f;
+        if (r < pivot) {
+            return r/pivot;
+        } else {
+            return 1.0f;
+        }
+    }
+
+    /**
+     * Sets the color of the holographic paint to be used when applying the outline/blur.
+     */
+    void setColor(int color) {
+        mHolographicPaint.setColor(color);
+    }
+
+    /**
+     * Applies an outline to whatever is currently drawn in the specified bitmap.
+     */
+    void applyOutline(Bitmap srcDst, Canvas srcDstCanvas, PointF offset) {
+        Bitmap mask = srcDst.extractAlpha();
+        Matrix m = new Matrix();
+        final int width = srcDst.getWidth();
+        final int height = srcDst.getHeight();
+        float xScale = STROKE_WIDTH*2.0f/width;
+        float yScale = STROKE_WIDTH*2.0f/height;
+        m.preScale(1+xScale, 1+yScale, (width / 2.0f) + offset.x,
+                (height / 2.0f) + offset.y);
+
+        srcDstCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
+        srcDstCanvas.drawBitmap(mask, m, mHolographicPaint);
+        srcDstCanvas.drawBitmap(mask, mIdentity, mErasePaint);
+        mask.recycle();
+    }
+
+    /**
+     * Applies an blur to whatever is currently drawn in the specified bitmap.
+     */
+    void applyBlur(Bitmap srcDst, Canvas srcDstCanvas) {
+        int[] xy = new int[2];
+        Bitmap mask = srcDst.extractAlpha(mBlurPaint, xy);
+        srcDstCanvas.drawBitmap(mask, xy[0], xy[1], mHolographicPaint);
+        mask.recycle();
+    }
+}
+
+/**
+ * An icon on a PagedView, specifically for items in the launcher's paged view (with compound
+ * drawables on the top).
+ */
+public class PagedViewIcon extends TextView implements Checkable {
+    private static final String TAG = "PagedViewIcon";
+
+    // holographic outline
+    private final Paint mPaint = new Paint();
+    private static HolographicOutlineHelper sHolographicOutlineHelper;
+    private Bitmap mHolographicOutline;
+    private Canvas mHolographicOutlineCanvas;
+    private boolean mIsHolographicUpdatePass;
+    private Rect mDrawableClipRect;
+
+    private Object mIconCacheKey;
+    private PagedViewIconCache mIconCache;
+
+    private int mAlpha;
+    private int mHolographicAlpha;
+
+    private boolean mIsChecked;
+
+    public PagedViewIcon(Context context) {
+        this(context, null);
+    }
+
+    public PagedViewIcon(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public PagedViewIcon(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        if (sHolographicOutlineHelper == null) {
+            final Resources resources = context.getResources();
+            final DisplayMetrics metrics = resources.getDisplayMetrics();
+            final float density = metrics.density;
+            sHolographicOutlineHelper = new HolographicOutlineHelper(density);
+        }
+        mDrawableClipRect = new Rect();
+
+        setFocusable(true);
+        setBackgroundDrawable(null);
+    }
+
+    public void applyFromApplicationInfo(ApplicationInfo info, PagedViewIconCache cache) {
+        mIconCache = cache;
+        mIconCacheKey = info;
+        mHolographicOutline = mIconCache.getOutline(mIconCacheKey);
+
+        setCompoundDrawablesWithIntrinsicBounds(null,
+                new FastBitmapDrawable(info.iconBitmap), null, null);
+        setText(info.title);
+        setTag(info);
+    }
+
+    public void applyFromResolveInfo(ResolveInfo info, PackageManager packageManager,
+            PagedViewIconCache cache) {
+        mIconCache = cache;
+        mIconCacheKey = info;
+        mHolographicOutline = mIconCache.getOutline(mIconCacheKey);
+
+        Drawable image = info.loadIcon(packageManager);
+        image.setBounds(0, 0, image.getIntrinsicWidth(), image.getIntrinsicHeight());
+        setCompoundDrawablesWithIntrinsicBounds(null, image, null, null);
+        setText(info.loadLabel(packageManager));
+        setTag(info);
+    }
+
+    @Override
+    public void setAlpha(float alpha) {
+        final float viewAlpha = sHolographicOutlineHelper.viewAlphaInterpolator(alpha);
+        final float holographicAlpha = sHolographicOutlineHelper.highlightAlphaInterpolator(alpha);
+        mAlpha = (int) (viewAlpha * 255);
+        mHolographicAlpha = (int) (holographicAlpha * 255);
+        super.setAlpha(viewAlpha);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+
+        if (mHolographicOutline == null) {
+            final PointF offset = new PointF(0,
+                    -(getCompoundPaddingBottom() + getCompoundPaddingTop())/2.0f);
+
+            // update the clipping rect to be used in the holographic pass below
+            getDrawingRect(mDrawableClipRect);
+            mDrawableClipRect.bottom = getPaddingTop() + getCompoundPaddingTop();
+
+            // set a flag to indicate that we are going to draw the view at full alpha with the text
+            // clipped for the generation of the holographic icon
+            mIsHolographicUpdatePass = true;
+            mHolographicOutline = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(),
+                    Bitmap.Config.ARGB_8888);
+            mHolographicOutlineCanvas = new Canvas(mHolographicOutline);
+            mHolographicOutlineCanvas.concat(getMatrix());
+            draw(mHolographicOutlineCanvas);
+            sHolographicOutlineHelper.setColor(HolographicOutlineHelper.HOLOGRAPHIC_BLUE);
+            sHolographicOutlineHelper.applyOutline(mHolographicOutline, mHolographicOutlineCanvas,
+                    offset);
+            sHolographicOutlineHelper.applyBlur(mHolographicOutline, mHolographicOutlineCanvas);
+            mIsHolographicUpdatePass = false;
+            mIconCache.addOutline(mIconCacheKey, mHolographicOutline);
+        }
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        // draw the view itself
+        if (mIsHolographicUpdatePass) {
+            // only clip to the text view (restore its alpha so that we get a proper outline)
+            canvas.save();
+            canvas.clipRect(mDrawableClipRect, Op.REPLACE);
+            final float alpha = getAlpha();
+            super.setAlpha(1.0f);
+            super.onDraw(canvas);
+            super.setAlpha(alpha);
+            canvas.restore();
+        } else {
+            if (mAlpha > 0) {
+                super.onDraw(canvas);
+            }
+        }
+
+        if (!mIsHolographicUpdatePass && mHolographicOutline != null && mHolographicAlpha > 0) {
+            mPaint.setAlpha(mHolographicAlpha);
+            canvas.drawBitmap(mHolographicOutline, 0, 0, mPaint);
+        }
+    }
+
+    @Override
+    public boolean isChecked() {
+        return mIsChecked;
+    }
+
+    @Override
+    public void setChecked(boolean checked) {
+        mIsChecked = checked;
+        invalidate();
+    }
+
+    @Override
+    public void toggle() {
+        setChecked(!mIsChecked);
+    }
+}
diff --git a/src/com/android/launcher2/ShortcutInfo.java b/src/com/android/launcher2/ShortcutInfo.java
index 2c5aec4..72f2d51 100644
--- a/src/com/android/launcher2/ShortcutInfo.java
+++ b/src/com/android/launcher2/ShortcutInfo.java
@@ -16,16 +16,14 @@
 
 package com.android.launcher2;
 
+import java.util.ArrayList;
+
 import android.content.ComponentName;
 import android.content.ContentValues;
-import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
-import android.graphics.drawable.Drawable;
 import android.util.Log;
 
-import java.util.ArrayList;
-
 /**
  * Represents a launchable icon on the workspaces and in folders.
  */
diff --git a/src/com/android/launcher2/ShortcutsAdapter.java b/src/com/android/launcher2/ShortcutsAdapter.java
index 19c3af0..93c500a 100644
--- a/src/com/android/launcher2/ShortcutsAdapter.java
+++ b/src/com/android/launcher2/ShortcutsAdapter.java
@@ -16,16 +16,15 @@
 
 package com.android.launcher2;
 
+import java.util.ArrayList;
+
 import android.content.Context;
-import android.content.pm.PackageManager;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ArrayAdapter;
 import android.widget.TextView;
 
-import java.util.ArrayList;
-
 import com.android.launcher.R;
 
 /**
diff --git a/src/com/android/launcher2/SmoothPagedView.java b/src/com/android/launcher2/SmoothPagedView.java
new file mode 100644
index 0000000..3218349
--- /dev/null
+++ b/src/com/android/launcher2/SmoothPagedView.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher2;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.animation.Interpolator;
+import android.widget.Scroller;
+
+
+public abstract class SmoothPagedView extends PagedView {
+    private static final float SMOOTHING_SPEED = 0.75f;
+    private static final float SMOOTHING_CONSTANT = (float) (0.016 / Math.log(SMOOTHING_SPEED));
+
+
+    private static final float BASELINE_FLING_VELOCITY = 2500.f;
+    private static final float FLING_VELOCITY_INFLUENCE = 0.4f;
+
+    private WorkspaceOvershootInterpolator mScrollInterpolator;
+
+    private static class WorkspaceOvershootInterpolator implements Interpolator {
+        private static final float DEFAULT_TENSION = 1.3f;
+        private float mTension;
+
+        public WorkspaceOvershootInterpolator() {
+            mTension = DEFAULT_TENSION;
+        }
+
+        public void setDistance(int distance) {
+            mTension = distance > 0 ? DEFAULT_TENSION / distance : DEFAULT_TENSION;
+        }
+
+        public void disableSettle() {
+            mTension = 0.f;
+        }
+
+        public float getInterpolation(float t) {
+            // _o(t) = t * t * ((tension + 1) * t + tension)
+            // o(t) = _o(t - 1) + 1
+            t -= 1.0f;
+            return t * t * ((mTension + 1) * t + mTension) + 1.0f;
+        }
+    }
+
+    /**
+     * Used to inflate the Workspace from XML.
+     *
+     * @param context The application's context.
+     * @param attrs The attributes set containing the Workspace's customization values.
+     */
+    public SmoothPagedView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    /**
+     * Used to inflate the Workspace from XML.
+     *
+     * @param context The application's context.
+     * @param attrs The attributes set containing the Workspace's customization values.
+     * @param defStyle Unused.
+     */
+    public SmoothPagedView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        mUsePagingTouchSlop = false;
+
+        // This means that we'll take care of updating the scroll parameter ourselves (we do it
+        // in computeScroll)
+        mDeferScrollUpdate = true;
+    }
+
+    /**
+     * Initializes various states for this workspace.
+     */
+    @Override
+    protected void init() {
+        super.init();
+        mScrollInterpolator = new WorkspaceOvershootInterpolator();
+        // overwrite the previous mScroller
+        mScroller = new Scroller(getContext(), mScrollInterpolator);
+    }
+
+    @Override
+    protected void snapToDestination() {
+        snapToPageWithVelocity(mCurrentPage, 0);
+    }
+
+    @Override
+    protected void snapToPageWithVelocity(int whichPage, int velocity) {
+        snapToPageWithVelocity(whichPage, 0, true);
+    }
+
+    void snapToPageWithVelocity(int whichPage, int velocity, boolean settle) {
+            // if (!mScroller.isFinished()) return;
+
+        whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1));
+
+        final int screenDelta = Math.max(1, Math.abs(whichPage - mCurrentPage));
+        final int newX = whichPage * getWidth();
+        final int delta = newX - mScrollX;
+        int duration = (screenDelta + 1) * 100;
+
+        if (!mScroller.isFinished()) {
+            mScroller.abortAnimation();
+        }
+
+        if (settle) {
+            mScrollInterpolator.setDistance(screenDelta);
+        } else {
+            mScrollInterpolator.disableSettle();
+        }
+
+        velocity = Math.abs(velocity);
+        if (velocity > 0) {
+            duration += (duration / (velocity / BASELINE_FLING_VELOCITY))
+                    * FLING_VELOCITY_INFLUENCE;
+        } else {
+            duration += 100;
+        }
+        snapToPage(whichPage, delta, duration);
+    }
+
+    @Override
+    protected void snapToPage(int whichPage) {
+        snapToPageWithVelocity(whichPage, 0, false);
+    }
+
+    @Override
+    public void computeScroll() {
+        boolean scrollComputed = computeScrollHelper();
+
+        if (!scrollComputed && mTouchState == TOUCH_STATE_SCROLLING) {
+            final float now = System.nanoTime() / NANOTIME_DIV;
+            final float e = (float) Math.exp((now - mSmoothingTime) / SMOOTHING_CONSTANT);
+            final float dx = mTouchX - mScrollX;
+            mScrollX += dx * e;
+            mSmoothingTime = now;
+
+            // Keep generating points as long as we're more than 1px away from the target
+            if (dx > 1.f || dx < -1.f) {
+                invalidate();
+            }
+        }
+    }
+}
diff --git a/src/com/android/launcher2/SymmetricalLinearTween.java b/src/com/android/launcher2/SymmetricalLinearTween.java
index 2e0ed8f..da02242 100644
--- a/src/com/android/launcher2/SymmetricalLinearTween.java
+++ b/src/com/android/launcher2/SymmetricalLinearTween.java
@@ -17,9 +17,7 @@
 package com.android.launcher2;
 
 import android.os.Handler;
-import android.os.Message;
 import android.os.SystemClock;
-import android.util.Log;
 
 /**
  * Provides an animation between 0.0f and 1.0f over a given duration.
diff --git a/src/com/android/launcher2/UserFolder.java b/src/com/android/launcher2/UserFolder.java
index d49c27a..b46e924 100644
--- a/src/com/android/launcher2/UserFolder.java
+++ b/src/com/android/launcher2/UserFolder.java
@@ -3,10 +3,8 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.widget.ArrayAdapter;
 
 import com.android.launcher.R;
 
@@ -91,4 +89,10 @@
         super.onOpen();
         requestFocus();
     }
+
+    @Override
+    public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset,
+            DragView dragView, Object dragInfo) {
+        return null;
+    }
 }
diff --git a/src/com/android/launcher2/Utilities.java b/src/com/android/launcher2/Utilities.java
index 757e48e..c67ff99 100644
--- a/src/com/android/launcher2/Utilities.java
+++ b/src/com/android/launcher2/Utilities.java
@@ -16,9 +16,8 @@
 
 package com.android.launcher2;
 
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.PaintDrawable;
+import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BlurMaskFilter;
 import android.graphics.Canvas;
@@ -26,19 +25,19 @@
 import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Paint;
 import android.graphics.PaintFlagsDrawFilter;
-import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.TableMaskFilter;
 import android.graphics.Typeface;
-import android.text.Layout.Alignment;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.PaintDrawable;
 import android.text.StaticLayout;
 import android.text.TextPaint;
+import android.text.Layout.Alignment;
 import android.util.DisplayMetrics;
 import android.util.Log;
-import android.content.res.Resources;
-import android.content.Context;
 
 import com.android.launcher.R;
 
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index 30b1494..7551699 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -16,9 +16,13 @@
 
 package com.android.launcher2;
 
-import java.util.ArrayList;
-import java.util.HashSet;
+import com.android.launcher.R;
 
+import android.animation.Animatable;
+import android.animation.PropertyAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.Sequencer;
+import android.animation.Animatable.AnimatableListener;
 import android.app.WallpaperManager;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
@@ -27,138 +31,89 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ProviderInfo;
+import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
+import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.IBinder;
-import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.MotionEvent;
-import android.view.VelocityTracker;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.animation.Interpolator;
-import android.widget.Scroller;
 import android.widget.TextView;
+import android.widget.Toast;
 
-import com.android.launcher.R;
+import java.util.ArrayList;
+import java.util.HashSet;
 
 /**
- * The workspace is a wide area with a wallpaper and a finite number of screens. Each
- * screen contains a number of icons, folders or widgets the user can interact with.
- * A workspace is meant to be used with a fixed width only.
+ * The workspace is a wide area with a wallpaper and a finite number of pages.
+ * Each page contains a number of icons, folders or widgets the user can
+ * interact with. A workspace is meant to be used with a fixed width only.
  */
-public class Workspace extends ViewGroup implements DropTarget, DragSource, DragScroller {
+public class Workspace extends SmoothPagedView
+        implements DropTarget, DragSource, DragScroller, View.OnTouchListener {
     @SuppressWarnings({"UnusedDeclaration"})
     private static final String TAG = "Launcher.Workspace";
-    private static final int INVALID_SCREEN = -1;
-    
-    /**
-     * The velocity at which a fling gesture will cause us to snap to the next screen
-     */
-    private static final int SNAP_VELOCITY = 600;
+
+    // This is how much the workspace shrinks when we enter all apps or
+    // customization mode
+    private static final float SHRINK_FACTOR = 0.16f;
+    private enum ShrinkPosition { SHRINK_TO_TOP, SHRINK_TO_MIDDLE, SHRINK_TO_BOTTOM };
 
     private final WallpaperManager mWallpaperManager;
-    
-    private int mDefaultScreen;
 
-    private boolean mFirstLayout = true;
+    private int mDefaultPage;
 
-    private int mCurrentScreen;
-    private int mNextScreen = INVALID_SCREEN;
-    private Scroller mScroller;
-    private VelocityTracker mVelocityTracker;
+    private boolean mWaitingToShrinkToBottom = false;
 
     /**
      * CellInfo for the cell that is currently being dragged
      */
     private CellLayout.CellInfo mDragInfo;
-    
+
     /**
      * Target drop area calculated during last acceptDrop call.
      */
     private int[] mTargetCell = null;
 
-    private float mLastMotionX;
-    private float mLastMotionY;
-    
-    private final static int TOUCH_STATE_REST = 0;
-    private final static int TOUCH_STATE_SCROLLING = 1;
-
-    private int mTouchState = TOUCH_STATE_REST;
-
-    private OnLongClickListener mLongClickListener;
+    /**
+     * The CellLayout that is currently being dragged over
+     */
+    private CellLayout mDragTargetLayout = null;
 
     private Launcher mLauncher;
     private IconCache mIconCache;
     private DragController mDragController;
-    
-    /**
-     * Cache of vacant cells, used during drag events and invalidated as needed.
-     */
-    private CellLayout.CellInfo mVacantCache = null;
-    
+
+
     private int[] mTempCell = new int[2];
     private int[] mTempEstimate = new int[2];
+    private float[] mTempDragCoordinates = new float[2];
+    private float[] mTempDragBottomRightCoordinates = new float[2];
 
-    private boolean mAllowLongPress = true;
+    private static final int DEFAULT_CELL_COUNT_X = 4;
+    private static final int DEFAULT_CELL_COUNT_Y = 4;
 
-    private int mTouchSlop;
-    private int mMaximumVelocity;
-    
-    private static final int INVALID_POINTER = -1;
-
-    private int mActivePointerId = INVALID_POINTER;
-    
     private Drawable mPreviousIndicator;
     private Drawable mNextIndicator;
-    
-    private static final float NANOTIME_DIV = 1000000000.0f;
-    private static final float SMOOTHING_SPEED = 0.75f;
-    private static final float SMOOTHING_CONSTANT = (float) (0.016 / Math.log(SMOOTHING_SPEED));
-    private float mSmoothingTime;
-    private float mTouchX;
 
-    private WorkspaceOvershootInterpolator mScrollInterpolator;
+    // State variable that indicated whether the pages are small (ie when you're
+    // in all apps or customize mode)
+    private boolean mIsSmall;
+    private AnimatableListener mUnshrinkAnimationListener;
 
-    private static final float BASELINE_FLING_VELOCITY = 2500.f;
-    private static final float FLING_VELOCITY_INFLUENCE = 0.4f;
-    
-    private static class WorkspaceOvershootInterpolator implements Interpolator {
-        private static final float DEFAULT_TENSION = 1.3f;
-        private float mTension;
 
-        public WorkspaceOvershootInterpolator() {
-            mTension = DEFAULT_TENSION;
-        }
-        
-        public void setDistance(int distance) {
-            mTension = distance > 0 ? DEFAULT_TENSION / distance : DEFAULT_TENSION;
-        }
-
-        public void disableSettle() {
-            mTension = 0.f;
-        }
-
-        public float getInterpolation(float t) {
-            // _o(t) = t * t * ((tension + 1) * t + tension)
-            // o(t) = _o(t - 1) + 1
-            t -= 1.0f;
-            return t * t * ((mTension + 1) * t + mTension) + 1.0f;
-        }
-    }
-    
     /**
      * Used to inflate the Workspace from XML.
      *
      * @param context The application's context.
-     * @param attrs The attribtues set containing the Workspace's customization values.
+     * @param attrs The attributes set containing the Workspace's customization values.
      */
     public Workspace(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
@@ -168,37 +123,49 @@
      * Used to inflate the Workspace from XML.
      *
      * @param context The application's context.
-     * @param attrs The attribtues set containing the Workspace's customization values.
+     * @param attrs The attributes set containing the Workspace's customization values.
      * @param defStyle Unused.
      */
     public Workspace(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
+        mContentIsRefreshable = false;
+        mFadeInAdjacentScreens = false;
 
         mWallpaperManager = WallpaperManager.getInstance(context);
-        
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Workspace, defStyle, 0);
-        mDefaultScreen = a.getInt(R.styleable.Workspace_defaultScreen, 1);
+
+        TypedArray a = context.obtainStyledAttributes(attrs,
+                R.styleable.Workspace, defStyle, 0);
+        int cellCountX = a.getInt(R.styleable.Workspace_cellCountX, DEFAULT_CELL_COUNT_X);
+        int cellCountY = a.getInt(R.styleable.Workspace_cellCountY, DEFAULT_CELL_COUNT_Y);
+        mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1);
         a.recycle();
 
+        LauncherModel.updateWorkspaceLayoutCells(cellCountX, cellCountY);
         setHapticFeedbackEnabled(false);
+
         initWorkspace();
     }
 
     /**
      * Initializes various states for this workspace.
      */
-    private void initWorkspace() {
+    protected void initWorkspace() {
         Context context = getContext();
-        mScrollInterpolator = new WorkspaceOvershootInterpolator();
-        mScroller = new Scroller(context, mScrollInterpolator);
-        mCurrentScreen = mDefaultScreen;
-        Launcher.setScreen(mCurrentScreen);
+        mCurrentPage = mDefaultPage;
+        Launcher.setScreen(mCurrentPage);
         LauncherApplication app = (LauncherApplication)context.getApplicationContext();
         mIconCache = app.getIconCache();
 
-        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
-        mTouchSlop = configuration.getScaledTouchSlop();
-        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+        mUnshrinkAnimationListener = new AnimatableListener() {
+            public void onAnimationStart(Animatable animation) {}
+            public void onAnimationEnd(Animatable animation) {
+                mIsSmall = false;
+            }
+            public void onAnimationCancel(Animatable animation) {}
+            public void onAnimationRepeat(Animatable animation) {}
+        };
+
+        mSnapVelocity = 600;
     }
 
     @Override
@@ -245,30 +212,32 @@
      * @return The open folder on the current screen, or null if there is none
      */
     Folder getOpenFolder() {
-        CellLayout currentScreen = (CellLayout) getChildAt(mCurrentScreen);
-        int count = currentScreen.getChildCount();
+        CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage);
+        int count = currentPage.getChildCount();
         for (int i = 0; i < count; i++) {
-            View child = currentScreen.getChildAt(i);
-            CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
-            if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
-                return (Folder) child;
+            View child = currentPage.getChildAt(i);
+            if (child instanceof Folder) {
+                Folder folder = (Folder) child;
+                if (folder.getInfo().opened)
+                    return folder;
             }
         }
         return null;
     }
 
     ArrayList<Folder> getOpenFolders() {
-        final int screens = getChildCount();
-        ArrayList<Folder> folders = new ArrayList<Folder>(screens);
+        final int screenCount = getChildCount();
+        ArrayList<Folder> folders = new ArrayList<Folder>(screenCount);
 
-        for (int screen = 0; screen < screens; screen++) {
-            CellLayout currentScreen = (CellLayout) getChildAt(screen);
-            int count = currentScreen.getChildCount();
+        for (int screen = 0; screen < screenCount; screen++) {
+            CellLayout currentPage = (CellLayout) getChildAt(screen);
+            int count = currentPage.getChildCount();
             for (int i = 0; i < count; i++) {
-                View child = currentScreen.getChildAt(i);
-                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
-                if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
-                    folders.add((Folder) child);
+                View child = currentPage.getChildAt(i);
+                if (child instanceof Folder) {
+                    Folder folder = (Folder) child;
+                    if (folder.getInfo().opened)
+                        folders.add(folder);
                     break;
                 }
             }
@@ -277,33 +246,19 @@
         return folders;
     }
 
-    boolean isDefaultScreenShowing() {
-        return mCurrentScreen == mDefaultScreen;
-    }
-
-    /**
-     * Returns the index of the currently displayed screen.
-     *
-     * @return The index of the currently displayed screen.
-     */
-    int getCurrentScreen() {
-        return mCurrentScreen;
+    boolean isDefaultPageShowing() {
+        return mCurrentPage == mDefaultPage;
     }
 
     /**
      * Sets the current screen.
      *
-     * @param currentScreen
+     * @param currentPage
      */
-    void setCurrentScreen(int currentScreen) {
-        if (!mScroller.isFinished()) mScroller.abortAnimation();
-        clearVacantCache();
-        mCurrentScreen = Math.max(0, Math.min(currentScreen, getChildCount() - 1));
-        mPreviousIndicator.setLevel(mCurrentScreen);
-        mNextIndicator.setLevel(mCurrentScreen);
-        scrollTo(mCurrentScreen * getWidth(), 0);
-        updateWallpaperOffset();
-        invalidate();
+    @Override
+    void setCurrentPage(int currentPage) {
+        super.setCurrentPage(currentPage);
+        updateWallpaperOffset(mScrollX);
     }
 
     /**
@@ -317,7 +272,7 @@
      * @param spanY The number of cells spanned vertically by the child.
      */
     void addInCurrentScreen(View child, int x, int y, int spanX, int spanY) {
-        addInScreen(child, mCurrentScreen, x, y, spanX, spanY, false);
+        addInScreen(child, mCurrentPage, x, y, spanX, spanY, false);
     }
 
     /**
@@ -332,7 +287,7 @@
      * @param insert When true, the child is inserted at the beginning of the children list.
      */
     void addInCurrentScreen(View child, int x, int y, int spanX, int spanY, boolean insert) {
-        addInScreen(child, mCurrentScreen, x, y, spanX, spanY, insert);
+        addInScreen(child, mCurrentPage, x, y, spanX, spanY, insert);
     }
 
     /**
@@ -350,6 +305,10 @@
         addInScreen(child, screen, x, y, spanX, spanY, false);
     }
 
+    void addInFullScreen(View child, int screen) {
+        addInScreen(child, screen, 0, 0, -1, -1);
+    }
+
     /**
      * Adds the specified child in the specified screen. The position and dimension of
      * the child are defined by x, y, spanX and spanY.
@@ -369,8 +328,6 @@
             return;
         }
 
-        clearVacantCache();
-
         final CellLayout group = (CellLayout) getChildAt(screen);
         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
         if (lp == null) {
@@ -381,127 +338,92 @@
             lp.cellHSpan = spanX;
             lp.cellVSpan = spanY;
         }
-        group.addView(child, insert ? 0 : -1, lp);
+
+        // Get the canonical child id to uniquely represent this view in this screen
+        int childId = LauncherModel.getCellLayoutChildId(child.getId(), screen, x, y, spanX, spanY);
+        if (!group.addViewToCellLayout(child, insert ? 0 : -1, childId, lp)) {
+            // TODO: This branch occurs when the workspace is adding views
+            // outside of the defined grid
+            // maybe we should be deleting these items from the LauncherModel?
+            Log.w(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
+        }
+
         if (!(child instanceof Folder)) {
             child.setHapticFeedbackEnabled(false);
             child.setOnLongClickListener(mLongClickListener);
         }
         if (child instanceof DropTarget) {
-            mDragController.addDropTarget((DropTarget)child);
+            mDragController.addDropTarget((DropTarget) child);
         }
     }
 
-    CellLayout.CellInfo findAllVacantCells(boolean[] occupied) {
-        CellLayout group = (CellLayout) getChildAt(mCurrentScreen);
+    CellLayout.CellInfo updateOccupiedCellsForCurrentScreen(boolean[] occupied) {
+        CellLayout group = (CellLayout) getChildAt(mCurrentPage);
         if (group != null) {
-            return group.findAllVacantCells(occupied, null);
+            return group.updateOccupiedCells(occupied, null);
         }
         return null;
     }
 
-    private void clearVacantCache() {
-        if (mVacantCache != null) {
-            mVacantCache.clearVacantCells();
-            mVacantCache = null;
+    public boolean onTouch(View v, MotionEvent event) {
+        // this is an intercepted event being forwarded from a cell layout
+        if (mIsSmall) {
+            unshrink((CellLayout)v);
+            mLauncher.onWorkspaceUnshrink();
+            return true;
+        }
+        return false;
+    }
+
+    protected void pageBeginMoving() {
+        if (mNextPage != INVALID_PAGE) {
+            // we're snapping to a particular screen
+            enableChildrenCache(mCurrentPage, mNextPage);
+        } else {
+            // this is when user is actively dragging a particular screen, they might
+            // swipe it either left or right (but we won't advance by more than one screen)
+            enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1);
         }
     }
 
-    /**
-     * Registers the specified listener on each screen contained in this workspace.
-     *
-     * @param l The listener used to respond to long clicks.
-     */
-    @Override
-    public void setOnLongClickListener(OnLongClickListener l) {
-        mLongClickListener = l;
-        final int count = getChildCount();
-        for (int i = 0; i < count; i++) {
-            getChildAt(i).setOnLongClickListener(l);
-        }
+    protected void pageEndMoving() {
+        clearChildrenCache();
     }
 
+    @Override
+    protected void notifyPageSwitchListener() {
+        super.notifyPageSwitchListener();
+
+        if (mPreviousIndicator != null) {
+            // if we know the next page, we show the indication for it right away; it looks
+            // weird if the indicators are lagging
+            int page = mNextPage;
+            if (page == INVALID_PAGE) {
+                page = mCurrentPage;
+            }
+            mPreviousIndicator.setLevel(page);
+            mNextIndicator.setLevel(page);
+        }
+        Launcher.setScreen(mCurrentPage);
+    };
+
     private void updateWallpaperOffset() {
         updateWallpaperOffset(getChildAt(getChildCount() - 1).getRight() - (mRight - mLeft));
     }
 
     private void updateWallpaperOffset(int scrollRange) {
-        IBinder token = getWindowToken();
-        if (token != null) {
-            mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 0 );
-            mWallpaperManager.setWallpaperOffsets(getWindowToken(),
-                    Math.max(0.f, Math.min(mScrollX/(float)scrollRange, 1.f)), 0);
-        }
-    }
-    
-    @Override
-    public void scrollTo(int x, int y) {
-        super.scrollTo(x, y);
-        mTouchX = x;
-        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
-    }
-    
-    @Override
-    public void computeScroll() {
-        if (mScroller.computeScrollOffset()) {
-            mTouchX = mScrollX = mScroller.getCurrX();
-            mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
-            mScrollY = mScroller.getCurrY();
-            updateWallpaperOffset();
-            postInvalidate();
-        } else if (mNextScreen != INVALID_SCREEN) {
-            mCurrentScreen = Math.max(0, Math.min(mNextScreen, getChildCount() - 1));
-            mPreviousIndicator.setLevel(mCurrentScreen);
-            mNextIndicator.setLevel(mCurrentScreen);
-            Launcher.setScreen(mCurrentScreen);
-            mNextScreen = INVALID_SCREEN;
-            clearChildrenCache();
-        } else if (mTouchState == TOUCH_STATE_SCROLLING) {
-            final float now = System.nanoTime() / NANOTIME_DIV;
-            final float e = (float) Math.exp((now - mSmoothingTime) / SMOOTHING_CONSTANT);
-            final float dx = mTouchX - mScrollX;
-            mScrollX += dx * e;
-            mSmoothingTime = now;
-
-            // Keep generating points as long as we're more than 1px away from the target
-            if (dx > 1.f || dx < -1.f) {
-                updateWallpaperOffset();
-                postInvalidate();
+        final boolean isStaticWallpaper = (mWallpaperManager != null) &&
+                (mWallpaperManager.getWallpaperInfo() == null);
+        if (LauncherApplication.isScreenXLarge() && !isStaticWallpaper) {
+            IBinder token = getWindowToken();
+            if (token != null) {
+                mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 0 );
+                mWallpaperManager.setWallpaperOffsets(getWindowToken(),
+                        Math.max(0.f, Math.min(mScrollX/(float)scrollRange, 1.f)), 0);
             }
         }
     }
 
-    @Override
-    protected void dispatchDraw(Canvas canvas) {
-        boolean restore = false;
-        int restoreCount = 0;
-
-        // ViewGroup.dispatchDraw() supports many features we don't need:
-        // clip to padding, layout animation, animation listener, disappearing
-        // children, etc. The following implementation attempts to fast-track
-        // the drawing dispatch by drawing only what we know needs to be drawn.
-
-        boolean fastDraw = mTouchState != TOUCH_STATE_SCROLLING && mNextScreen == INVALID_SCREEN;
-        // If we are not scrolling or flinging, draw only the current screen
-        if (fastDraw) {
-            drawChild(canvas, getChildAt(mCurrentScreen), getDrawingTime());
-        } else {
-            final long drawingTime = getDrawingTime();
-            final float scrollPos = (float) mScrollX / getWidth();
-            final int leftScreen = (int) scrollPos;
-            final int rightScreen = leftScreen + 1;
-            if (leftScreen >= 0) {
-                drawChild(canvas, getChildAt(leftScreen), drawingTime);
-            }
-            if (scrollPos != leftScreen && rightScreen < getChildCount()) {
-                drawChild(canvas, getChildAt(rightScreen), drawingTime);
-            }
-        }
-
-        if (restore) {
-            canvas.restoreToCount(restoreCount);
-        }
-    }
-
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         computeScroll();
@@ -509,59 +431,40 @@
     }
 
     @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-        final int width = MeasureSpec.getSize(widthMeasureSpec);
-        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
-        if (widthMode != MeasureSpec.EXACTLY) {
-            throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
-        }
-
-        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-        if (heightMode != MeasureSpec.EXACTLY) {
-            throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
-        }
-
-        // The children are given the same width and height as the workspace
-        final int count = getChildCount();
-        for (int i = 0; i < count; i++) {
-            getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
-        }
-
-
-        if (mFirstLayout) {
-            setHorizontalScrollBarEnabled(false);
-            scrollTo(mCurrentScreen * width, 0);
-            setHorizontalScrollBarEnabled(true);
-            updateWallpaperOffset(width * (getChildCount() - 1));
-            mFirstLayout = false;
-        }
-    }
-
-    @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        int childLeft = 0;
+        super.onLayout(changed, left, top, right, bottom);
 
-        final int count = getChildCount();
-        for (int i = 0; i < count; i++) {
-            final View child = getChildAt(i);
-            if (child.getVisibility() != View.GONE) {
-                final int childWidth = child.getMeasuredWidth();
-                child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight());
-                childLeft += childWidth;
-            }
+        // if shrinkToBottom() is called on initialization, it has to be deferred
+        // until after the first call to onLayout so that it has the correct width
+        if (mWaitingToShrinkToBottom) {
+            shrinkToBottom(false);
+            mWaitingToShrinkToBottom = false;
+        }
+
+        if (LauncherApplication.isInPlaceRotationEnabled()) {
+            // When the device is rotated, the scroll position of the current screen
+            // needs to be refreshed
+            setCurrentPage(getCurrentPage());
         }
     }
 
     @Override
-    public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
-        int screen = indexOfChild(child);
-        if (screen != mCurrentScreen || !mScroller.isFinished()) {
-            snapToScreen(screen);
-            return true;
+    protected void dispatchDraw(Canvas canvas) {
+        if (mIsSmall) {
+            // Draw all the workspaces if we're small
+            final int pageCount = getChildCount();
+            final long drawingTime = getDrawingTime();
+            for (int i = 0; i < pageCount; i++) {
+                final View page = (View) getChildAt(i);
+
+                if (page.getAlpha() != 1.0f) {
+                    page.setAlpha(1.0f);
+                }
+                drawChild(canvas, page, drawingTime);
+            }
+        } else {
+            super.dispatchDraw(canvas);
         }
-        return false;
     }
 
     @Override
@@ -571,51 +474,20 @@
             if (openFolder != null) {
                 return openFolder.requestFocus(direction, previouslyFocusedRect);
             } else {
-                int focusableScreen;
-                if (mNextScreen != INVALID_SCREEN) {
-                    focusableScreen = mNextScreen;
-                } else {
-                    focusableScreen = mCurrentScreen;
-                }
-                getChildAt(focusableScreen).requestFocus(direction, previouslyFocusedRect);
+                return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
             }
         }
         return false;
     }
 
     @Override
-    public boolean dispatchUnhandledMove(View focused, int direction) {
-        if (direction == View.FOCUS_LEFT) {
-            if (getCurrentScreen() > 0) {
-                snapToScreen(getCurrentScreen() - 1);
-                return true;
-            }
-        } else if (direction == View.FOCUS_RIGHT) {
-            if (getCurrentScreen() < getChildCount() - 1) {
-                snapToScreen(getCurrentScreen() + 1);
-                return true;
-            }
-        }
-        return super.dispatchUnhandledMove(focused, direction);
-    }
-
-    @Override
     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
         if (!mLauncher.isAllAppsVisible()) {
             final Folder openFolder = getOpenFolder();
-            if (openFolder == null) {
-                getChildAt(mCurrentScreen).addFocusables(views, direction);
-                if (direction == View.FOCUS_LEFT) {
-                    if (mCurrentScreen > 0) {
-                        getChildAt(mCurrentScreen - 1).addFocusables(views, direction);
-                    }
-                } else if (direction == View.FOCUS_RIGHT){
-                    if (mCurrentScreen < getChildCount() - 1) {
-                        getChildAt(mCurrentScreen + 1).addFocusables(views, direction);
-                    }
-                }
-            } else {
+            if (openFolder != null) {
                 openFolder.addFocusables(views, direction);
+            } else {
+                super.addFocusables(views, direction, focusableMode);
             }
         }
     }
@@ -623,203 +495,28 @@
     @Override
     public boolean dispatchTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            if (mLauncher.isAllAppsVisible()) {
+            // (In XLarge mode, the workspace is shrunken below all apps, and responds to taps
+            // ie when you click on a mini-screen, it zooms back to that screen)
+            if (!LauncherApplication.isScreenXLarge() && mLauncher.isAllAppsVisible()) {
                 return false;
             }
         }
         return super.dispatchTouchEvent(ev);
     }
 
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        final boolean allAppsVisible = mLauncher.isAllAppsVisible();
-        if (allAppsVisible) {
-            return false; // We don't want the events.  Let them fall through to the all apps view.
+    void enableChildrenCache(int fromPage, int toPage) {
+        if (fromPage > toPage) {
+            final int temp = fromPage;
+            fromPage = toPage;
+            toPage = temp;
         }
 
-        /*
-         * This method JUST determines whether we want to intercept the motion.
-         * If we return true, onTouchEvent will be called and we do the actual
-         * scrolling there.
-         */
+        final int screenCount = getChildCount();
 
-        /*
-         * Shortcut the most recurring case: the user is in the dragging
-         * state and he is moving his finger.  We want to intercept this
-         * motion.
-         */
-        final int action = ev.getAction();
-        if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {
-            return true;
-        }
+        fromPage = Math.max(fromPage, 0);
+        toPage = Math.min(toPage, screenCount - 1);
 
-        if (mVelocityTracker == null) {
-            mVelocityTracker = VelocityTracker.obtain();
-        }
-        mVelocityTracker.addMovement(ev);
-        
-        switch (action & MotionEvent.ACTION_MASK) {
-            case MotionEvent.ACTION_MOVE: {
-                /*
-                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
-                 * whether the user has moved far enough from his original down touch.
-                 */
-
-                /*
-                 * Locally do absolute value. mLastMotionX is set to the y value
-                 * of the down event.
-                 */
-                final int pointerIndex = ev.findPointerIndex(mActivePointerId);
-                final float x = ev.getX(pointerIndex);
-                final float y = ev.getY(pointerIndex);
-                final int xDiff = (int) Math.abs(x - mLastMotionX);
-                final int yDiff = (int) Math.abs(y - mLastMotionY);
-
-                final int touchSlop = mTouchSlop;
-                boolean xMoved = xDiff > touchSlop;
-                boolean yMoved = yDiff > touchSlop;
-                
-                if (xMoved || yMoved) {
-                    
-                    if (xMoved) {
-                        // Scroll if the user moved far enough along the X axis
-                        mTouchState = TOUCH_STATE_SCROLLING;
-                        mLastMotionX = x;
-                        mTouchX = mScrollX;
-                        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
-                        enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1);
-                    }
-                    // Either way, cancel any pending longpress
-                    if (mAllowLongPress) {
-                        mAllowLongPress = false;
-                        // Try canceling the long press. It could also have been scheduled
-                        // by a distant descendant, so use the mAllowLongPress flag to block
-                        // everything
-                        final View currentScreen = getChildAt(mCurrentScreen);
-                        currentScreen.cancelLongPress();
-                    }
-                }
-                break;
-            }
-
-            case MotionEvent.ACTION_DOWN: {
-                final float x = ev.getX();
-                final float y = ev.getY();
-                // Remember location of down touch
-                mLastMotionX = x;
-                mLastMotionY = y;
-                mActivePointerId = ev.getPointerId(0);
-                mAllowLongPress = true;
-
-                /*
-                 * If being flinged and user touches the screen, initiate drag;
-                 * otherwise don't.  mScroller.isFinished should be false when
-                 * being flinged.
-                 */
-                mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;
-                break;
-            }
-
-            case MotionEvent.ACTION_CANCEL:
-            case MotionEvent.ACTION_UP:
-                
-                if (mTouchState != TOUCH_STATE_SCROLLING) {
-                    final CellLayout currentScreen = (CellLayout)getChildAt(mCurrentScreen);
-                    if (!currentScreen.lastDownOnOccupiedCell()) {
-                        getLocationOnScreen(mTempCell);
-                        // Send a tap to the wallpaper if the last down was on empty space
-                        final int pointerIndex = ev.findPointerIndex(mActivePointerId);
-                        mWallpaperManager.sendWallpaperCommand(getWindowToken(), 
-                                "android.wallpaper.tap",
-                                mTempCell[0] + (int) ev.getX(pointerIndex),
-                                mTempCell[1] + (int) ev.getY(pointerIndex), 0, null);
-                    }
-                }
-                
-                // Release the drag
-                clearChildrenCache();
-                mTouchState = TOUCH_STATE_REST;
-                mActivePointerId = INVALID_POINTER;
-                mAllowLongPress = false;
-                
-                if (mVelocityTracker != null) {
-                    mVelocityTracker.recycle();
-                    mVelocityTracker = null;
-                }
-
-                break;
-                
-            case MotionEvent.ACTION_POINTER_UP:
-                onSecondaryPointerUp(ev);
-                break;
-        }
-
-        /*
-         * The only time we want to intercept motion events is if we are in the
-         * drag mode.
-         */
-        return mTouchState != TOUCH_STATE_REST;
-    }
-    
-    private void onSecondaryPointerUp(MotionEvent ev) {
-        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
-                MotionEvent.ACTION_POINTER_INDEX_SHIFT;
-        final int pointerId = ev.getPointerId(pointerIndex);
-        if (pointerId == mActivePointerId) {
-            // This was our active pointer going up. Choose a new
-            // active pointer and adjust accordingly.
-            // TODO: Make this decision more intelligent.
-            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
-            mLastMotionX = ev.getX(newPointerIndex);
-            mLastMotionY = ev.getY(newPointerIndex);
-            mActivePointerId = ev.getPointerId(newPointerIndex);
-            if (mVelocityTracker != null) {
-                mVelocityTracker.clear();
-            }
-        }
-    }
-
-    /**
-     * If one of our descendant views decides that it could be focused now, only
-     * pass that along if it's on the current screen.
-     *
-     * This happens when live folders requery, and if they're off screen, they
-     * end up calling requestFocus, which pulls it on screen.
-     */
-    @Override
-    public void focusableViewAvailable(View focused) {
-        View current = getChildAt(mCurrentScreen);
-        View v = focused;
-        while (true) {
-            if (v == current) {
-                super.focusableViewAvailable(focused);
-                return;
-            }
-            if (v == this) {
-                return;
-            }
-            ViewParent parent = v.getParent();
-            if (parent instanceof View) {
-                v = (View)v.getParent();
-            } else {
-                return;
-            }
-        }
-    }
-
-    void enableChildrenCache(int fromScreen, int toScreen) {
-        if (fromScreen > toScreen) {
-            final int temp = fromScreen;
-            fromScreen = toScreen;
-            toScreen = temp;
-        }
-        
-        final int count = getChildCount();
-
-        fromScreen = Math.max(fromScreen, 0);
-        toScreen = Math.min(toScreen, count - 1);
-
-        for (int i = fromScreen; i <= toScreen; i++) {
+        for (int i = fromPage; i <= toPage; i++) {
             final CellLayout layout = (CellLayout) getChildAt(i);
             layout.setChildrenDrawnWithCacheEnabled(true);
             layout.setChildrenDrawingCacheEnabled(true);
@@ -827,8 +524,8 @@
     }
 
     void clearChildrenCache() {
-        final int count = getChildCount();
-        for (int i = 0; i < count; i++) {
+        final int screenCount = getChildCount();
+        for (int i = 0; i < screenCount; i++) {
             final CellLayout layout = (CellLayout) getChildAt(i);
             layout.setChildrenDrawnWithCacheEnabled(false);
         }
@@ -836,199 +533,182 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
-        
         if (mLauncher.isAllAppsVisible()) {
             // Cancel any scrolling that is in progress.
             if (!mScroller.isFinished()) {
                 mScroller.abortAnimation();
             }
-            snapToScreen(mCurrentScreen);
+            snapToPage(mCurrentPage);
             return false; // We don't want the events.  Let them fall through to the all apps view.
         }
 
-        if (mVelocityTracker == null) {
-            mVelocityTracker = VelocityTracker.obtain();
-        }
-        mVelocityTracker.addMovement(ev);
-
-        final int action = ev.getAction();
-
-        switch (action & MotionEvent.ACTION_MASK) {
-        case MotionEvent.ACTION_DOWN:
-            /*
-             * If being flinged and user touches, stop the fling. isFinished
-             * will be false if being flinged.
-             */
-            if (!mScroller.isFinished()) {
-                mScroller.abortAnimation();
-            }
-
-            // Remember where the motion event started
-            mLastMotionX = ev.getX();
-            mActivePointerId = ev.getPointerId(0);
-            if (mTouchState == TOUCH_STATE_SCROLLING) {
-                enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1);
-            }
-            break;
-        case MotionEvent.ACTION_MOVE:
-            if (mTouchState == TOUCH_STATE_SCROLLING) {
-                // Scroll to follow the motion event
-                final int pointerIndex = ev.findPointerIndex(mActivePointerId);
-                final float x = ev.getX(pointerIndex);
-                final float deltaX = mLastMotionX - x;
-                mLastMotionX = x;
-
-                if (deltaX < 0) {
-                    if (mTouchX > 0) {
-                        mTouchX += Math.max(-mTouchX, deltaX);
-                        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
-                        invalidate();
-                    }
-                } else if (deltaX > 0) {
-                    final float availableToScroll = getChildAt(getChildCount() - 1).getRight() -
-                            mTouchX - getWidth();
-                    if (availableToScroll > 0) {
-                        mTouchX += Math.min(availableToScroll, deltaX);
-                        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
-                        invalidate();
-                    }
-                } else {
-                    awakenScrollBars();
-                }
-            }
-            break;
-        case MotionEvent.ACTION_UP:
-            if (mTouchState == TOUCH_STATE_SCROLLING) {
-                final VelocityTracker velocityTracker = mVelocityTracker;
-                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
-                final int velocityX = (int) velocityTracker.getXVelocity(mActivePointerId);
-                
-                final int screenWidth = getWidth();
-                final int whichScreen = (mScrollX + (screenWidth / 2)) / screenWidth;
-                final float scrolledPos = (float) mScrollX / screenWidth;
-                
-                if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {
-                    // Fling hard enough to move left.
-                    // Don't fling across more than one screen at a time.
-                    final int bound = scrolledPos < whichScreen ?
-                            mCurrentScreen - 1 : mCurrentScreen;
-                    snapToScreen(Math.min(whichScreen, bound), velocityX, true);
-                } else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) {
-                    // Fling hard enough to move right
-                    // Don't fling across more than one screen at a time.
-                    final int bound = scrolledPos > whichScreen ?
-                            mCurrentScreen + 1 : mCurrentScreen;
-                    snapToScreen(Math.max(whichScreen, bound), velocityX, true);
-                } else {
-                    snapToScreen(whichScreen, 0, true);
-                }
-
-                if (mVelocityTracker != null) {
-                    mVelocityTracker.recycle();
-                    mVelocityTracker = null;
-                }
-            }
-            mTouchState = TOUCH_STATE_REST;
-            mActivePointerId = INVALID_POINTER;
-            break;
-        case MotionEvent.ACTION_CANCEL:
-            mTouchState = TOUCH_STATE_REST;
-            mActivePointerId = INVALID_POINTER;
-            break;
-        case MotionEvent.ACTION_POINTER_UP:
-            onSecondaryPointerUp(ev);
-            break;
-        }
-
-        return true;
-    }
-    
-    void snapToScreen(int whichScreen) {
-        snapToScreen(whichScreen, 0, false);
+        return super.onTouchEvent(ev);
     }
 
-    private void snapToScreen(int whichScreen, int velocity, boolean settle) {
-        //if (!mScroller.isFinished()) return;
+    public boolean isSmall() {
+        return mIsSmall;
+    }
 
-        whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
-        
-        clearVacantCache();
-        enableChildrenCache(mCurrentScreen, whichScreen);
+    void shrinkToTop(boolean animated) {
+        shrink(ShrinkPosition.SHRINK_TO_TOP, animated);
+    }
 
-        mNextScreen = whichScreen;
+    void shrinkToMiddle() {
+        shrink(ShrinkPosition.SHRINK_TO_MIDDLE, true);
+    }
 
-        mPreviousIndicator.setLevel(mNextScreen);
-        mNextIndicator.setLevel(mNextScreen);
+    void shrinkToBottom() {
+        shrinkToBottom(true);
+    }
 
-        View focusedChild = getFocusedChild();
-        if (focusedChild != null && whichScreen != mCurrentScreen &&
-                focusedChild == getChildAt(mCurrentScreen)) {
-            focusedChild.clearFocus();
-        }
-        
-        final int screenDelta = Math.max(1, Math.abs(whichScreen - mCurrentScreen));
-        final int newX = whichScreen * getWidth();
-        final int delta = newX - mScrollX;
-        int duration = (screenDelta + 1) * 100;
-
-        if (!mScroller.isFinished()) {
-            mScroller.abortAnimation();
-        }
-        
-        if (settle) {
-            mScrollInterpolator.setDistance(screenDelta);
+    void shrinkToBottom(boolean animated) {
+        if (mFirstLayout) {
+            // (mFirstLayout == "first layout has not happened yet")
+            // if we get a call to shrink() as part of our initialization (for example, if
+            // Launcher is started in All Apps mode) then we need to wait for a layout call
+            // to get our width so we can layout the mini-screen views correctly
+            mWaitingToShrinkToBottom = true;
         } else {
-            mScrollInterpolator.disableSettle();
+            shrink(ShrinkPosition.SHRINK_TO_BOTTOM, animated);
         }
-        
-        velocity = Math.abs(velocity);
-        if (velocity > 0) {
-            duration += (duration / (velocity / BASELINE_FLING_VELOCITY))
-                    * FLING_VELOCITY_INFLUENCE;
-        } else {
-            duration += 100;
+    }
+
+    // we use this to shrink the workspace for the all apps view and the customize view
+    private void shrink(ShrinkPosition shrinkPosition, boolean animated) {
+        mIsSmall = true;
+        final Resources res = getResources();
+        final int screenWidth = getWidth();
+        final int screenHeight = getHeight();
+
+        // Making the assumption that all pages have the same width as the 0th
+        final int pageWidth = getChildAt(0).getMeasuredWidth();
+        final int pageHeight = getChildAt(0).getMeasuredHeight();
+
+        final int scaledPageWidth = (int) (SHRINK_FACTOR * pageWidth);
+        final int scaledPageHeight = (int) (SHRINK_FACTOR * pageHeight);
+        final float scaledSpacing = res.getDimension(R.dimen.smallScreenSpacing);
+
+        final int screenCount = getChildCount();
+        float totalWidth = screenCount * scaledPageWidth + (screenCount - 1) * scaledSpacing;
+
+        float newY = getResources().getDimension(R.dimen.smallScreenVerticalMargin);
+        if (shrinkPosition == ShrinkPosition.SHRINK_TO_BOTTOM) {
+            newY = screenHeight - newY - scaledPageHeight;
+        } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_MIDDLE) {
+            newY = screenHeight / 2 - scaledPageHeight / 2;
         }
 
-        awakenScrollBars(duration);
-        mScroller.startScroll(mScrollX, 0, delta, 0, duration);
-        invalidate();
+        // We animate all the screens to the centered position in workspace
+        // At the same time, the screens become greyed/dimmed
+
+        // newX is initialized to the left-most position of the centered screens
+        float newX = mScroller.getFinalX() + screenWidth / 2 - totalWidth / 2;
+        for (int i = 0; i < screenCount; i++) {
+            CellLayout cl = (CellLayout) getChildAt(i);
+            cl.setPivotX(0.0f);
+            cl.setPivotY(0.0f);
+            if (animated) {
+                final int duration = res.getInteger(R.integer.config_workspaceShrinkTime);
+                new PropertyAnimator(duration, cl,
+                        new PropertyValuesHolder("x", newX),
+                        new PropertyValuesHolder("y", newY),
+                        new PropertyValuesHolder("scaleX", SHRINK_FACTOR),
+                        new PropertyValuesHolder("scaleY", SHRINK_FACTOR),
+                        new PropertyValuesHolder("dimmedBitmapAlpha", 1.0f)).start();
+            } else {
+                cl.setX((int)newX);
+                cl.setY((int)newY);
+                cl.setScaleX(SHRINK_FACTOR);
+                cl.setScaleY(SHRINK_FACTOR);
+                cl.setDimmedBitmapAlpha(1.0f);
+            }
+            // increment newX for the next screen
+            newX += scaledPageWidth + scaledSpacing;
+            cl.setOnInterceptTouchListener(this);
+        }
+        setChildrenDrawnWithCacheEnabled(true);
+    }
+
+    // We call this when we trigger an unshrink by clicking on the CellLayout cl
+    private void unshrink(CellLayout clThatWasClicked) {
+        int newCurrentPage = mCurrentPage;
+        final int screenCount = getChildCount();
+        for (int i = 0; i < screenCount; i++) {
+            if (getChildAt(i) == clThatWasClicked) {
+                newCurrentPage = i;
+            }
+        }
+        unshrink(newCurrentPage);
+    }
+
+    private void unshrink(int newCurrentPage) {
+        if (mIsSmall) {
+            int delta = (newCurrentPage - mCurrentPage)*getWidth();
+
+            final int screenCount = getChildCount();
+            for (int i = 0; i < screenCount; i++) {
+                CellLayout cl = (CellLayout) getChildAt(i);
+                cl.setX(cl.getX() + delta);
+            }
+            snapToPage(newCurrentPage);
+            unshrink();
+
+            setCurrentPage(newCurrentPage);
+        }
+    }
+
+    void unshrink() {
+        unshrink(true);
+    }
+
+    void unshrink(boolean animated) {
+        if (mIsSmall) {
+            Sequencer s = new Sequencer();
+            final int screenCount = getChildCount();
+
+            final int duration = getResources().getInteger(R.integer.config_workspaceUnshrinkTime);
+            for (int i = 0; i < screenCount; i++) {
+                final CellLayout cl = (CellLayout)getChildAt(i);
+                cl.setPivotX(0.0f);
+                cl.setPivotY(0.0f);
+                if (animated) {
+                    s.playTogether(
+                            new PropertyAnimator<Float>(duration, cl, "translationX", 0.0f),
+                            new PropertyAnimator<Float>(duration, cl, "translationY", 0.0f),
+                            new PropertyAnimator<Float>(duration, cl, "scaleX", 1.0f),
+                            new PropertyAnimator<Float>(duration, cl, "scaleY", 1.0f),
+                            new PropertyAnimator<Float>(duration, cl, "dimmedBitmapAlpha", 0.0f));
+                } else {
+                    cl.setTranslationX(0.0f);
+                    cl.setTranslationY(0.0f);
+                    cl.setScaleX(1.0f);
+                    cl.setScaleY(1.0f);
+                    cl.setDimmedBitmapAlpha(0.0f);
+                }
+            }
+            s.addListener(mUnshrinkAnimationListener);
+            s.start();
+        }
     }
 
     void startDrag(CellLayout.CellInfo cellInfo) {
         View child = cellInfo.cell;
-        
+
         // Make sure the drag was started by a long press as opposed to a long click.
         if (!child.isInTouchMode()) {
             return;
         }
-        
+
         mDragInfo = cellInfo;
-        mDragInfo.screen = mCurrentScreen;
-        
-        CellLayout current = ((CellLayout) getChildAt(mCurrentScreen));
+        mDragInfo.screen = mCurrentPage;
+
+        CellLayout current = ((CellLayout) getChildAt(mCurrentPage));
 
         current.onDragChild(child);
         mDragController.startDrag(child, this, child.getTag(), DragController.DRAG_ACTION_MOVE);
         invalidate();
     }
 
-    @Override
-    protected Parcelable onSaveInstanceState() {
-        final SavedState state = new SavedState(super.onSaveInstanceState());
-        state.currentScreen = mCurrentScreen;
-        return state;
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        SavedState savedState = (SavedState) state;
-        super.onRestoreInstanceState(savedState.getSuperState());
-        if (savedState.currentScreen != -1) {
-            mCurrentScreen = savedState.currentScreen;
-            Launcher.setScreen(mCurrentScreen);
-        }
-    }
-
     void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo) {
         addApplicationShortcut(info, cellInfo, false);
     }
@@ -1044,94 +724,313 @@
 
     public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
             DragView dragView, Object dragInfo) {
-        final CellLayout cellLayout = getCurrentDropLayout();
+        CellLayout cellLayout = getCurrentDropLayout();
+        int originX = x - xOffset;
+        int originY = y - yOffset;
+        if (mIsSmall) {
+            // find out which target layout is over
+            final float[] localXY = mTempDragCoordinates;
+            localXY[0] = originX;
+            localXY[1] = originY;
+            final float[] localBottomRightXY = mTempDragBottomRightCoordinates;
+            // we need to subtract left/top here because DragController already adds
+            // dragRegionLeft/Top to xOffset and yOffset
+            localBottomRightXY[0] = originX + dragView.getDragRegionWidth();
+            localBottomRightXY[1] = originY + dragView.getDragRegionHeight();
+            cellLayout =  findMatchingPageForDragOver(localXY, localBottomRightXY);
+            if (cellLayout == null) {
+                // cancel the drag if we're not over a mini-screen at time of drop
+                // TODO: maybe add a nice fade here?
+                return;
+            }
+            // localXY will be transformed into the local screen's coordinate space; save that info
+            originX = (int)localXY[0];
+            originY = (int)localXY[1];
+        }
         if (source != this) {
-            onDropExternal(x - xOffset, y - yOffset, dragInfo, cellLayout);
+            onDropExternal(originX, originY, dragInfo, cellLayout);
         } else {
             // Move internally
             if (mDragInfo != null) {
                 final View cell = mDragInfo.cell;
-                int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen;                
+                int index = mScroller.isFinished() ? mCurrentPage : mNextPage;
                 if (index != mDragInfo.screen) {
                     final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen);
                     originalCellLayout.removeView(cell);
-                    cellLayout.addView(cell);
+                    addInScreen(cell, index, mDragInfo.cellX, mDragInfo.cellY,
+                            mDragInfo.spanX, mDragInfo.spanY);
                 }
-                mTargetCell = estimateDropCell(x - xOffset, y - yOffset,
-                        mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout, mTargetCell);
-                cellLayout.onDropChild(cell, mTargetCell);
 
+                mTargetCell = estimateDropCell(originX, originY,
+                        mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout,
+                        mTargetCell);
+                cellLayout.onDropChild(cell);
+
+                // update the item's position after drop
                 final ItemInfo info = (ItemInfo) cell.getTag();
-                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
+                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell
+                        .getLayoutParams();
+                lp.cellX = mTargetCell[0];
+                lp.cellY = mTargetCell[1];
+
                 LauncherModel.moveItemInDatabase(mLauncher, info,
-                        LauncherSettings.Favorites.CONTAINER_DESKTOP, index, lp.cellX, lp.cellY);
+                        LauncherSettings.Favorites.CONTAINER_DESKTOP, index,
+                        lp.cellX, lp.cellY);
             }
         }
     }
 
-    public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset,
+    public void onDragEnter(DragSource source, int x, int y, int xOffset,
+            int yOffset, DragView dragView, Object dragInfo) {
+    }
+
+    public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset,
             DragView dragView, Object dragInfo) {
-        clearVacantCache();
+
+        // We may need to delegate the drag to a child view. If a 1x1 item
+        // would land in a cell occupied by a DragTarget (e.g. a Folder),
+        // then drag events should be handled by that child.
+
+        ItemInfo item = (ItemInfo)dragInfo;
+        CellLayout currentLayout = getCurrentDropLayout();
+
+        int dragPointX, dragPointY;
+        if (item.spanX == 1 && item.spanY == 1) {
+            // For a 1x1, calculate the drop cell exactly as in onDragOver
+            dragPointX = x - xOffset;
+            dragPointY = y - yOffset;
+        } else {
+            // Otherwise, use the exact drag coordinates
+            dragPointX = x;
+            dragPointY = y;
+        }
+
+        // If we are dragging over a cell that contains a DropTarget that will
+        // accept the drop, delegate to that DropTarget.
+        final int[] cellXY = mTempCell;
+        currentLayout.estimateDropCell(dragPointX, dragPointY, item.spanX, item.spanY, cellXY);
+        View child = currentLayout.getChildAt(cellXY[0], cellXY[1]);
+        if (child instanceof DropTarget) {
+            DropTarget target = (DropTarget)child;
+            if (target.acceptDrop(source, x, y, xOffset, yOffset, dragView, dragInfo)) {
+                return target;
+            }
+        }
+        return null;
+    }
+
+    // xy = upper left corner of item being dragged
+    // bottomRightXy = lower right corner of item being dragged
+    // This method will see which mini-screen is most overlapped by the item being dragged, and
+    // return it. It will also transform the parameters xy and bottomRightXy into the local
+    // coordinate space of the returned screen
+    private CellLayout findMatchingPageForDragOver(float[] xy, float[] bottomRightXy) {
+        float x = xy[0];
+        float y = xy[1];
+        float right = bottomRightXy[0];
+        float bottom = bottomRightXy[1];
+
+        float bestX = 0;
+        float bestY = 0;
+        float bestRight = 0;
+        float bestBottom = 0;
+
+        Matrix inverseMatrix = new Matrix();
+
+        // We loop through all the screens (ie CellLayouts) and see which one overlaps the most
+        // with the item being dragged.
+        final int screenCount = getChildCount();
+        CellLayout bestMatchingScreen = null;
+        float bestOverlapSoFar = 0;
+        for (int i = 0; i < screenCount; i++) {
+            CellLayout cl = (CellLayout)getChildAt(i);
+            // Transform the coordinates of the item being dragged to the CellLayout's coordinates
+            float left = cl.getLeft();
+            float top = cl.getTop();
+            xy[0] = x + mScrollX - left;
+            xy[1] = y + mScrollY - top;
+            cl.getMatrix().invert(inverseMatrix);
+
+            bottomRightXy[0] = right + mScrollX - left;
+            bottomRightXy[1] = bottom + mScrollY - top;
+
+            inverseMatrix.mapPoints(xy);
+            inverseMatrix.mapPoints(bottomRightXy);
+
+            float dragRegionX = xy[0];
+            float dragRegionY = xy[1];
+            float dragRegionRight = bottomRightXy[0];
+            float dragRegionBottom = bottomRightXy[1];
+
+            // Find the overlapping region
+            float overlapLeft = Math.max(0f, dragRegionX);
+            float overlapTop = Math.max(0f, dragRegionY);
+            float overlapBottom = Math.min(cl.getHeight(), dragRegionBottom);
+            float overlapRight = Math.min(cl.getWidth(), dragRegionRight);
+
+            if (overlapRight >= 0 && overlapLeft <= cl.getWidth() &&
+                    overlapTop >= 0 && overlapBottom <= cl.getHeight()) {
+                // Calculate the size of the overlapping region
+                float overlap = (overlapRight - overlapLeft) * (overlapBottom - overlapTop);
+                if (overlap > bestOverlapSoFar) {
+                    bestOverlapSoFar = overlap;
+                    bestMatchingScreen = cl;
+                    bestX = xy[0];
+                    bestY = xy[1];
+                    bestRight = bottomRightXy[0];
+                    bestBottom = bottomRightXy[1];
+                }
+             }
+        }
+        if (bestMatchingScreen != null && bestMatchingScreen != mDragTargetLayout) {
+            if (mDragTargetLayout != null) {
+                mDragTargetLayout.onDragComplete();
+            }
+            mDragTargetLayout = bestMatchingScreen;
+        }
+        xy[0] = bestX;
+        xy[1] = bestY;
+        bottomRightXy[0] = bestRight;
+        bottomRightXy[1] = bestBottom;
+        return bestMatchingScreen;
     }
 
     public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
             DragView dragView, Object dragInfo) {
+
+        final ItemInfo item = (ItemInfo)dragInfo;
+        CellLayout currentLayout = getCurrentDropLayout();
+
+        if (dragInfo instanceof LauncherAppWidgetInfo) {
+            LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo)dragInfo;
+
+            if (widgetInfo.spanX == -1) {
+                // Calculate the grid spans needed to fit this widget
+                int[] spans = currentLayout.rectToCell(widgetInfo.minWidth, widgetInfo.minHeight, null);
+                item.spanX = spans[0];
+                item.spanY = spans[1];
+            }
+        }
+        int originX = x - xOffset;
+        int originY = y - yOffset;
+        if (mIsSmall) {
+            // find out which mini screen the dragged item is over
+            final float[] localXY = mTempDragCoordinates;
+            localXY[0] = originX;
+            localXY[1] = originY;
+            final float[] localBottomRightXY = mTempDragBottomRightCoordinates;
+
+            localBottomRightXY[0] = originX + dragView.getDragRegionWidth();
+            localBottomRightXY[1] = originY + dragView.getDragRegionHeight();
+            currentLayout = findMatchingPageForDragOver(localXY, localBottomRightXY);
+            if (currentLayout != null) {
+                currentLayout.setHover(true);
+            }
+
+            originX = (int)localXY[0];
+            originY = (int)localXY[1];
+        }
+
+        if (source != this) {
+            // This is a hack to fix the point used to determine which cell an icon from the all
+            // apps screen is over
+            if (item != null && item.spanX == 1 && currentLayout != null) {
+                int dragRegionLeft = (dragView.getWidth() - currentLayout.getCellWidth()) / 2;
+
+                originX += dragRegionLeft - dragView.getDragRegionLeft();
+                if (dragView.getDragRegionWidth() != currentLayout.getCellWidth()) {
+                    dragView.setDragRegion(dragView.getDragRegionLeft(), dragView.getDragRegionTop(),
+                            currentLayout.getCellWidth(), dragView.getDragRegionHeight());
+                }
+            }
+        }
+        if (currentLayout != mDragTargetLayout) {
+            if (mDragTargetLayout != null) {
+                mDragTargetLayout.onDragComplete();
+            }
+            mDragTargetLayout = currentLayout;
+        }
+
+        // only visualize the drop locations for moving icons within the home screen on tablet
+        // on phone, we also visualize icons dragged in from All Apps
+        if ((!LauncherApplication.isScreenXLarge() || source == this)
+                && mDragTargetLayout != null) {
+            final View child = (mDragInfo == null) ? null : mDragInfo.cell;
+            mDragTargetLayout.visualizeDropLocation(
+                    child, originX, originY, item.spanX, item.spanY);
+        }
     }
 
-    public void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset,
-            DragView dragView, Object dragInfo) {
-        clearVacantCache();
+    public void onDragExit(DragSource source, int x, int y, int xOffset,
+            int yOffset, DragView dragView, Object dragInfo) {
+        if (mDragTargetLayout != null) {
+            mDragTargetLayout.onDragComplete();
+            mDragTargetLayout = null;
+        }
     }
 
-    private void onDropExternal(int x, int y, Object dragInfo, CellLayout cellLayout) {
+    private void onDropExternal(int x, int y, Object dragInfo,
+            CellLayout cellLayout) {
         onDropExternal(x, y, dragInfo, cellLayout, false);
     }
-    
-    private void onDropExternal(int x, int y, Object dragInfo, CellLayout cellLayout,
-            boolean insertAtFirst) {
+
+    private void onDropExternal(int x, int y, Object dragInfo,
+            CellLayout cellLayout, boolean insertAtFirst) {
         // Drag from somewhere else
         ItemInfo info = (ItemInfo) dragInfo;
 
-        View view;
+        View view = null;
 
         switch (info.itemType) {
         case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
         case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
             if (info.container == NO_ID && info instanceof ApplicationInfo) {
                 // Came from all apps -- make a copy
-                info = new ShortcutInfo((ApplicationInfo)info);
+                info = new ShortcutInfo((ApplicationInfo) info);
             }
-            view = mLauncher.createShortcut(R.layout.application, cellLayout, (ShortcutInfo)info);
+            view = mLauncher.createShortcut(R.layout.application, cellLayout,
+                    (ShortcutInfo) info);
             break;
         case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
             view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher,
-                    (ViewGroup) getChildAt(mCurrentScreen), ((UserFolderInfo) info));
+                    (ViewGroup) getChildAt(mCurrentPage),
+                    ((UserFolderInfo) info));
+            break;
+        case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+            cellLayout.setTagToCellInfoForPoint(x, y);
+            int[] position = new int[2];
+            position[0] = x;
+            position[1] = y;
+            mLauncher.addAppWidgetFromDrop(((LauncherAppWidgetInfo)dragInfo).providerName,
+                    cellLayout.getTag(), position);
             break;
         default:
-            throw new IllegalStateException("Unknown item type: " + info.itemType);
+            throw new IllegalStateException("Unknown item type: "
+                    + info.itemType);
         }
 
-        cellLayout.addView(view, insertAtFirst ? 0 : -1);
-        view.setHapticFeedbackEnabled(false);
-        view.setOnLongClickListener(mLongClickListener);
-        if (view instanceof DropTarget) {
-            mDragController.addDropTarget((DropTarget) view);
+        // If the view is null, it has already been added.
+        if (view == null) {
+            cellLayout.onDragComplete();
+        } else {
+            mTargetCell = estimateDropCell(x, y, 1, 1, view, cellLayout, mTargetCell);
+            addInScreen(view, indexOfChild(cellLayout), mTargetCell[0],
+                    mTargetCell[1], info.spanX, info.spanY, insertAtFirst);
+            cellLayout.onDropChild(view);
+            CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
+
+            LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
+                    LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentPage,
+                    lp.cellX, lp.cellY);
         }
-
-        mTargetCell = estimateDropCell(x, y, 1, 1, view, cellLayout, mTargetCell);
-        cellLayout.onDropChild(view, mTargetCell);
-        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
-
-        LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
-                LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentScreen, lp.cellX, lp.cellY);
     }
-    
+
     /**
      * Return the current {@link CellLayout}, correctly picking the destination
      * screen while a scroll is in progress.
      */
     private CellLayout getCurrentDropLayout() {
-        int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen;
+        int index = mScroller.isFinished() ? mCurrentPage : mNextPage;
         return (CellLayout) getChildAt(index);
     }
 
@@ -1141,48 +1040,51 @@
     public boolean acceptDrop(DragSource source, int x, int y,
             int xOffset, int yOffset, DragView dragView, Object dragInfo) {
         final CellLayout layout = getCurrentDropLayout();
-        final CellLayout.CellInfo cellInfo = mDragInfo;
-        final int spanX = cellInfo == null ? 1 : cellInfo.spanX;
-        final int spanY = cellInfo == null ? 1 : cellInfo.spanY;
+        final CellLayout.CellInfo dragCellInfo = mDragInfo;
+        final int spanX = dragCellInfo == null ? 1 : dragCellInfo.spanX;
+        final int spanY = dragCellInfo == null ? 1 : dragCellInfo.spanY;
 
-        if (mVacantCache == null) {
-            final View ignoreView = cellInfo == null ? null : cellInfo.cell;
-            mVacantCache = layout.findAllVacantCells(null, ignoreView);
+        final View ignoreView = dragCellInfo == null ? null : dragCellInfo.cell;
+        final CellLayout.CellInfo cellInfo = layout.updateOccupiedCells(null, ignoreView);
+
+        if (cellInfo.findCellForSpan(mTempEstimate, spanX, spanY)) {
+            return true;
+        } else {
+            Toast.makeText(getContext(), getContext().getString(R.string.out_of_space), Toast.LENGTH_SHORT).show();
+            return false;
         }
-
-        return mVacantCache.findCellForSpan(mTempEstimate, spanX, spanY, false);
     }
-    
+
     /**
      * {@inheritDoc}
      */
     public Rect estimateDropLocation(DragSource source, int x, int y,
             int xOffset, int yOffset, DragView dragView, Object dragInfo, Rect recycle) {
         final CellLayout layout = getCurrentDropLayout();
-        
+
         final CellLayout.CellInfo cellInfo = mDragInfo;
         final int spanX = cellInfo == null ? 1 : cellInfo.spanX;
         final int spanY = cellInfo == null ? 1 : cellInfo.spanY;
         final View ignoreView = cellInfo == null ? null : cellInfo.cell;
-        
+
         final Rect location = recycle != null ? recycle : new Rect();
-        
+
         // Find drop cell and convert into rectangle
-        int[] dropCell = estimateDropCell(x - xOffset, y - yOffset,
-                spanX, spanY, ignoreView, layout, mTempCell);
-        
+        int[] dropCell = estimateDropCell(x - xOffset, y - yOffset, spanX,
+                spanY, ignoreView, layout, mTempCell);
+
         if (dropCell == null) {
             return null;
         }
-        
+
         layout.cellToPoint(dropCell[0], dropCell[1], mTempEstimate);
         location.left = mTempEstimate[0];
         location.top = mTempEstimate[1];
-        
+
         layout.cellToPoint(dropCell[0] + spanX, dropCell[1] + spanY, mTempEstimate);
         location.right = mTempEstimate[0];
         location.bottom = mTempEstimate[1];
-        
+
         return location;
     }
 
@@ -1191,16 +1093,23 @@
      */
     private int[] estimateDropCell(int pixelX, int pixelY,
             int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) {
-        // Create vacant cell cache if none exists
-        if (mVacantCache == null) {
-            mVacantCache = layout.findAllVacantCells(null, ignoreView);
-        }
 
+        final int[] cellXY = mTempCell;
+        layout.estimateDropCell(pixelX, pixelY, spanX, spanY, cellXY);
+        layout.cellToPoint(cellXY[0], cellXY[1], mTempEstimate);
+
+        final CellLayout.CellInfo cellInfo = layout.updateOccupiedCells(null, ignoreView);
         // Find the best target drop location
-        return layout.findNearestVacantArea(pixelX, pixelY,
-                spanX, spanY, mVacantCache, recycle);
+        return layout.findNearestVacantArea(mTempEstimate[0], mTempEstimate[1], spanX, spanY, cellInfo, recycle);
     }
-    
+
+    /**
+     * Estimate the size that a child with the given dimensions will take in the current screen.
+     */
+    void estimateChildSize(int minWidth, int minHeight, int[] result) {
+        ((CellLayout)getChildAt(mCurrentPage)).estimateChildSize(minWidth, minHeight, result);
+    }
+
     void setLauncher(Launcher launcher) {
         mLauncher = launcher;
     }
@@ -1210,16 +1119,14 @@
     }
 
     public void onDropCompleted(View target, boolean success) {
-        clearVacantCache();
-
-        if (success){
+        if (success) {
             if (target != this && mDragInfo != null) {
                 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
                 cellLayout.removeView(mDragInfo.cell);
                 if (mDragInfo.cell instanceof DropTarget) {
                     mDragController.removeDropTarget((DropTarget)mDragInfo.cell);
                 }
-                //final Object tag = mDragInfo.cell.getTag();
+                // final Object tag = mDragInfo.cell.getTag();
             }
         } else {
             if (mDragInfo != null) {
@@ -1231,40 +1138,28 @@
         mDragInfo = null;
     }
 
+    @Override
+    protected void onRestoreInstanceState(Parcelable state) {
+        super.onRestoreInstanceState(state);
+        Launcher.setScreen(mCurrentPage);
+    }
+
+    @Override
     public void scrollLeft() {
-        clearVacantCache();
-        if (mScroller.isFinished()) {
-            if (mCurrentScreen > 0) snapToScreen(mCurrentScreen - 1);
-        } else {
-            if (mNextScreen > 0) snapToScreen(mNextScreen - 1);            
+        if (!mIsSmall) {
+            super.scrollLeft();
         }
     }
 
+    @Override
     public void scrollRight() {
-        clearVacantCache();
-        if (mScroller.isFinished()) {
-            if (mCurrentScreen < getChildCount() -1) snapToScreen(mCurrentScreen + 1);
-        } else {
-            if (mNextScreen < getChildCount() -1) snapToScreen(mNextScreen + 1);            
+        if (!mIsSmall) {
+            super.scrollRight();
         }
     }
 
-    public int getScreenForView(View v) {
-        int result = -1;
-        if (v != null) {
-            ViewParent vp = v.getParent();
-            int count = getChildCount();
-            for (int i = 0; i < count; i++) {
-                if (vp == getChildAt(i)) {
-                    return i;
-                }
-            }
-        }
-        return result;
-    }
-
     public Folder getFolderForTag(Object tag) {
-        int screenCount = getChildCount();
+        final int screenCount = getChildCount();
         for (int screen = 0; screen < screenCount; screen++) {
             CellLayout currentScreen = ((CellLayout) getChildAt(screen));
             int count = currentScreen.getChildCount();
@@ -1273,7 +1168,7 @@
                 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
                 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
                     Folder f = (Folder) child;
-                    if (f.getInfo() == tag) {
+                    if (f.getInfo() == tag && f.getInfo().opened) {
                         return f;
                     }
                 }
@@ -1297,23 +1192,9 @@
         return null;
     }
 
-    /**
-     * @return True is long presses are still allowed for the current touch
-     */
-    public boolean allowLongPress() {
-        return mAllowLongPress;
-    }
-    
-    /**
-     * Set true to allow long-press events to be triggered, usually checked by
-     * {@link Launcher} to accept or block dpad-initiated long-presses.
-     */
-    public void setAllowLongPress(boolean allowLongPress) {
-        mAllowLongPress = allowLongPress;
-    }
 
     void removeItems(final ArrayList<ApplicationInfo> apps) {
-        final int count = getChildCount();
+        final int screenCount = getChildCount();
         final PackageManager manager = getContext().getPackageManager();
         final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext());
 
@@ -1323,7 +1204,7 @@
             packageNames.add(apps.get(i).componentName.getPackageName());
         }
 
-        for (int i = 0; i < count; i++) {
+        for (int i = 0; i < screenCount; i++) {
             final CellLayout layout = (CellLayout) getChildAt(i);
 
             // Avoid ANRs by treating each screen separately
@@ -1331,17 +1212,17 @@
                 public void run() {
                     final ArrayList<View> childrenToRemove = new ArrayList<View>();
                     childrenToRemove.clear();
-        
+
                     int childCount = layout.getChildCount();
                     for (int j = 0; j < childCount; j++) {
                         final View view = layout.getChildAt(j);
                         Object tag = view.getTag();
-        
+
                         if (tag instanceof ShortcutInfo) {
                             final ShortcutInfo info = (ShortcutInfo) tag;
                             final Intent intent = info.intent;
                             final ComponentName name = intent.getComponent();
-        
+
                             if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
                                 for (String packageName: packageNames) {
                                     if (packageName.equals(name.getPackageName())) {
@@ -1357,12 +1238,12 @@
                             final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1);
                             final int contentsCount = contents.size();
                             boolean removedFromFolder = false;
-        
+
                             for (int k = 0; k < contentsCount; k++) {
                                 final ShortcutInfo appInfo = contents.get(k);
                                 final Intent intent = appInfo.intent;
                                 final ComponentName name = intent.getComponent();
-        
+
                                 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
                                     for (String packageName: packageNames) {
                                         if (packageName.equals(name.getPackageName())) {
@@ -1375,11 +1256,12 @@
                                     }
                                 }
                             }
-        
+
                             contents.removeAll(toRemove);
                             if (removedFromFolder) {
                                 final Folder folder = getOpenFolder();
-                                if (folder != null) folder.notifyDataSetChanged();
+                                if (folder != null)
+                                    folder.notifyDataSetChanged();
                             }
                         } else if (tag instanceof LiveFolderInfo) {
                             final LiveFolderInfo info = (LiveFolderInfo) tag;
@@ -1392,7 +1274,7 @@
                                     if (packageName.equals(providerInfo.packageName)) {
                                         // TODO: This should probably be done on a worker thread
                                         LauncherModel.deleteItemFromDatabase(mLauncher, info);
-                                        childrenToRemove.add(view);                        
+                                        childrenToRemove.add(view);
                                     }
                                 }
                             }
@@ -1405,13 +1287,13 @@
                                     if (packageName.equals(provider.provider.getPackageName())) {
                                         // TODO: This should probably be done on a worker thread
                                         LauncherModel.deleteItemFromDatabase(mLauncher, info);
-                                        childrenToRemove.add(view);                                
+                                        childrenToRemove.add(view);
                                     }
                                 }
                             }
                         }
                     }
-        
+
                     childCount = childrenToRemove.size();
                     for (int j = 0; j < childCount; j++) {
                         View child = childrenToRemove.get(j);
@@ -1420,7 +1302,7 @@
                             mDragController.removeDropTarget((DropTarget)child);
                         }
                     }
-        
+
                     if (childCount > 0) {
                         layout.requestLayout();
                         layout.invalidate();
@@ -1433,8 +1315,8 @@
     void updateShortcuts(ArrayList<ApplicationInfo> apps) {
         final PackageManager pm = mLauncher.getPackageManager();
 
-        final int count = getChildCount();
-        for (int i = 0; i < count; i++) {
+        final int screenCount = getChildCount();
+        for (int i = 0; i < screenCount; i++) {
             final CellLayout layout = (CellLayout) getChildAt(i);
             int childCount = layout.getChildCount();
             for (int j = 0; j < childCount; j++) {
@@ -1450,7 +1332,7 @@
                     if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
                             Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
                         final int appCount = apps.size();
-                        for (int k=0; k<appCount; k++) {
+                        for (int k = 0; k < appCount; k++) {
                             ApplicationInfo app = apps.get(k);
                             if (app.componentName.equals(name)) {
                                 info.setIcon(mIconCache.getIcon(info.intent));
@@ -1467,47 +1349,30 @@
 
     void moveToDefaultScreen(boolean animate) {
         if (animate) {
-            snapToScreen(mDefaultScreen);
+            if (mIsSmall) {
+                unshrink(mDefaultPage);
+            } else {
+                snapToPage(mDefaultPage);
+            }
         } else {
-            setCurrentScreen(mDefaultScreen);
+            setCurrentPage(mDefaultPage);
         }
-        getChildAt(mDefaultScreen).requestFocus();
+        getChildAt(mDefaultPage).requestFocus();
     }
 
     void setIndicators(Drawable previous, Drawable next) {
         mPreviousIndicator = previous;
         mNextIndicator = next;
-        previous.setLevel(mCurrentScreen);
-        next.setLevel(mCurrentScreen);
+        previous.setLevel(mCurrentPage);
+        next.setLevel(mCurrentPage);
     }
 
-    public static class SavedState extends BaseSavedState {
-        int currentScreen = -1;
-
-        SavedState(Parcelable superState) {
-            super(superState);
-        }
-
-        private SavedState(Parcel in) {
-            super(in);
-            currentScreen = in.readInt();
-        }
-
-        @Override
-        public void writeToParcel(Parcel out, int flags) {
-            super.writeToParcel(out, flags);
-            out.writeInt(currentScreen);
-        }
-
-        public static final Parcelable.Creator<SavedState> CREATOR =
-                new Parcelable.Creator<SavedState>() {
-            public SavedState createFromParcel(Parcel in) {
-                return new SavedState(in);
-            }
-
-            public SavedState[] newArray(int size) {
-                return new SavedState[size];
-            }
-        };
+    @Override
+    public void syncPages() {
     }
+
+    @Override
+    public void syncPageItems(int page) {
+    }
+
 }
diff --git a/src/com/android/launcher2/allapps.rs b/src/com/android/launcher2/allapps.rs
new file mode 100644
index 0000000..b07e1fe
--- /dev/null
+++ b/src/com/android/launcher2/allapps.rs
@@ -0,0 +1,383 @@
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.launcher2)
+
+#include "rs_graphics.rsh"
+
+#define PI 3.14159f
+
+// Constants from Java
+int COLUMNS_PER_PAGE_PORTRAIT;
+int ROWS_PER_PAGE_PORTRAIT;
+int COLUMNS_PER_PAGE_LANDSCAPE;
+int ROWS_PER_PAGE_LANDSCAPE;
+
+int gIconCount;
+int gSelectedIconIndex = -1;
+rs_allocation gSelectedIconTexture;
+rs_allocation gHomeButton;
+
+rs_program_fragment gPFTexNearest;
+rs_program_fragment gPFTexMip;
+rs_program_fragment gPFTexMipAlpha;
+rs_program_vertex gPVCurve;
+rs_program_store gPS;
+rs_mesh gSMCell;
+
+rs_allocation *gIconIDs;
+rs_allocation *gLabelIDs;
+
+typedef struct VpConsts {
+    float4 Position;
+    float4 ScaleOffset;
+    float2 BendPos;
+    float2 ImgSize;
+} VpConsts_t;
+VpConsts_t *vpConstants;
+
+
+#pragma rs export_var(COLUMNS_PER_PAGE_PORTRAIT, ROWS_PER_PAGE_PORTRAIT, COLUMNS_PER_PAGE_LANDSCAPE, ROWS_PER_PAGE_LANDSCAPE, gIconCount, gSelectedIconIndex, gSelectedIconTexture, gHomeButton, gTargetPos, gPFTexNearest, gPFTexMip, gPFTexMipAlpha, gPVCurve, gPS, gSMCell, gIconIDs, gLabelIDs, vpConstants)
+#pragma rs export_func(move, moveTo, setZoom, fling)
+
+
+// Attraction to center values from page edge to page center.
+static float g_AttractionTable[9] = {20.f, 20.f, 20.f, 10.f, -10.f, -20.f, -20.f, -20.f, -20.f};
+static float g_FrictionTable[9] = {10.f, 10.f, 11.f, 15.f, 15.f, 11.f, 10.f, 10.f, 10.f};
+static float g_PhysicsTableSize = 7;
+
+static float gZoomTarget;
+static float gTargetPos;
+static float g_PosPage = 0.f;
+static float g_PosVelocity = 0.f;
+static float g_LastPositionX = 0.f;
+static bool g_LastTouchDown = false;
+static float g_DT;
+static int g_PosMax;
+static float g_Zoom = 0.f;
+static float g_Animation = 1.f;
+static float g_OldPosPage;
+static float g_OldPosVelocity;
+static float g_OldZoom;
+static float g_MoveToTotalTime = 0.2f;
+static float g_MoveToTime = 0.f;
+static float g_MoveToOldPos = 0.f;
+
+static int g_Cols;
+static int g_Rows;
+
+// Drawing constants, should be parameters ======
+#define VIEW_ANGLE 1.28700222f
+
+
+static void updateReadback() {
+    if ((g_OldPosPage != g_PosPage) ||
+        (g_OldPosVelocity != g_PosVelocity) ||
+        (g_OldZoom != g_Zoom)) {
+
+        g_OldPosPage = g_PosPage;
+        g_OldPosVelocity = g_PosVelocity;
+        g_OldZoom = g_Zoom;
+
+        int i[3];
+        i[0] = g_PosPage * (1 << 16);
+        i[1] = g_PosVelocity * (1 << 16);
+        i[2] = g_OldZoom * (1 << 16);
+        rsSendToClientBlocking(1, &i[0], sizeof(i));
+    }
+}
+
+void init() {
+}
+
+void move(float newPos) {
+    if (g_LastTouchDown) {
+        float dx = -(newPos - g_LastPositionX);
+        g_PosVelocity = 0;
+        g_PosPage += dx * 5.2f;
+
+        float pmin = -0.49f;
+        float pmax = g_PosMax + 0.49f;
+        g_PosPage = clamp(g_PosPage, pmin, pmax);
+    }
+    g_LastTouchDown = true;
+    g_LastPositionX = newPos;
+    g_MoveToTime = 0;
+}
+
+void moveTo(float targetPos) {
+    gTargetPos = targetPos;
+    g_MoveToTime = g_MoveToTotalTime;
+    g_PosVelocity = 0;
+    g_MoveToOldPos = g_PosPage;
+}
+
+void setZoom(float z, /*bool*/ int animate) {
+    gZoomTarget = z;
+    if (gZoomTarget < 0.001f) {
+        gZoomTarget = 0;
+    }
+    if (!animate) {
+        g_Zoom = gZoomTarget;
+    }
+    updateReadback();
+}
+
+void fling(float newPos, float vel) {
+    move(newPos);
+
+    g_LastTouchDown = false;
+    g_PosVelocity = -vel * 4;
+    float av = fabs(g_PosVelocity);
+    float minVel = 3.5f;
+
+    minVel *= 1.f - (fabs(rsFrac(g_PosPage + 0.5f) - 0.5f) * 0.45f);
+
+    if (av < minVel && av > 0.2f) {
+        if (g_PosVelocity > 0) {
+            g_PosVelocity = minVel;
+        } else {
+            g_PosVelocity = -minVel;
+        }
+    }
+
+    if (g_PosPage <= 0) {
+        g_PosVelocity = max(0.f, g_PosVelocity);
+    }
+    if (g_PosPage > g_PosMax) {
+        g_PosVelocity = min(0.f, g_PosVelocity);
+    }
+}
+
+// Interpolates values in the range 0..1 to a curve that eases in
+// and out.
+static float getInterpolation(float input) {
+    return (cos((input + 1) * PI) * 0.5f) + 0.5f;
+}
+
+
+static void updatePos() {
+    if (g_LastTouchDown) {
+        return;
+    }
+
+    float tablePosNorm = rsFrac(g_PosPage + 0.5f);
+    float tablePosF = tablePosNorm * g_PhysicsTableSize;
+    int tablePosI = tablePosF;
+    float tablePosFrac = tablePosF - tablePosI;
+    float accel = mix(g_AttractionTable[tablePosI],
+                        g_AttractionTable[tablePosI + 1],
+                        tablePosFrac) * g_DT;
+    float friction = mix(g_FrictionTable[tablePosI],
+                        g_FrictionTable[tablePosI + 1],
+                        tablePosFrac) * g_DT;
+
+    if (g_MoveToTime) {
+        // New position is old posiition + (total distance) * (interpolated time)
+        g_PosPage = g_MoveToOldPos + (gTargetPos - g_MoveToOldPos) * getInterpolation((g_MoveToTotalTime - g_MoveToTime) / g_MoveToTotalTime);
+        g_MoveToTime -= g_DT;
+        if (g_MoveToTime <= 0) {
+            g_MoveToTime = 0;
+            g_PosPage = gTargetPos;
+        }
+        return;
+    }
+
+    // If our velocity is low OR acceleration is opposing it, apply it.
+    if (fabs(g_PosVelocity) < 4.0f || (g_PosVelocity * accel) < 0) {
+        g_PosVelocity += accel;
+    }
+    //RS_DEBUG(g_PosPage);
+    //RS_DEBUG(g_PosVelocity);
+    //RS_DEBUG(friction);
+    //RS_DEBUG(accel);
+
+    // Normal physics
+    if (g_PosVelocity > 0) {
+        g_PosVelocity -= friction;
+        g_PosVelocity = max(g_PosVelocity, 0.f);
+    } else {
+        g_PosVelocity += friction;
+        g_PosVelocity = min(g_PosVelocity, 0.f);
+    }
+
+    if ((friction > fabs(g_PosVelocity)) && (friction > fabs(accel))) {
+        // Special get back to center and overcome friction physics.
+        float t = tablePosNorm - 0.5f;
+        if (fabs(t) < (friction * g_DT)) {
+            // really close, just snap
+            g_PosPage = round(g_PosPage);
+            g_PosVelocity = 0;
+        } else {
+            if (t > 0) {
+                g_PosVelocity = -friction;
+            } else {
+                g_PosVelocity = friction;
+            }
+        }
+    }
+
+    // Check for out of boundry conditions.
+    if (g_PosPage < 0 && g_PosVelocity < 0) {
+        float damp = 1.0f + (g_PosPage * 4);
+        damp = clamp(damp, 0.f, 0.9f);
+        g_PosVelocity *= damp;
+    }
+    if (g_PosPage > g_PosMax && g_PosVelocity > 0) {
+        float damp = 1.0f - ((g_PosPage - g_PosMax) * 4);
+        damp = clamp(damp, 0.f, 0.9f);
+        g_PosVelocity *= damp;
+    }
+
+    g_PosPage += g_PosVelocity * g_DT;
+    g_PosPage = clamp(g_PosPage, -0.49f, g_PosMax + 0.49f);
+}
+
+static void
+draw_home_button()
+{
+    rsgBindTexture(gPFTexNearest, 0, gHomeButton);
+
+    float w = rsgGetWidth();
+    float h = rsgGetHeight();
+    float tw = rsAllocationGetDimX(gHomeButton);
+    float th = rsAllocationGetDimY(gHomeButton);
+
+    float x;
+    float y;
+    if (w > h) {
+        x = w - (tw * (1 - g_Animation)) + 20;
+        y = (h - th) * 0.5f;
+    } else {
+        x = (w - tw) / 2;
+        y = -g_Animation * th;
+        y -= 30; // move the house to the edge of the screen as it doesn't fill the texture.
+    }
+
+    rsgDrawSpriteScreenspace(x, y, 0, tw, th);
+}
+
+static void drawFrontGrid(float rowOffset, float p)
+{
+    float h = rsgGetHeight();
+    float w = rsgGetWidth();
+
+    int intRowOffset = rowOffset;
+    float rowFrac = rowOffset - intRowOffset;
+    float colWidth = 120.f;//w / 4;
+    float rowHeight = colWidth + 25.f;
+    float yoff = 0.5f * h + 1.5f * rowHeight;
+
+    int row, col;
+    int colCount = 4;
+    if (w > h) {
+        colCount = 6;
+        rowHeight -= 12.f;
+        yoff = 0.47f * h + 1.0f * rowHeight;
+    }
+
+    int iconNum = (intRowOffset - 5) * colCount;
+
+    rsgBindProgramVertex(gPVCurve);
+
+    vpConstants->Position.z = p;
+
+    for (row = -5; row < 15; row++) {
+        float y = yoff - ((-rowFrac + row) * rowHeight);
+
+        for (col=0; col < colCount; col++) {
+            if (iconNum >= gIconCount) {
+                return;
+            }
+
+            if (iconNum >= 0) {
+                float x = colWidth * col + (colWidth / 2);
+                vpConstants->Position.x = x + 0.2f;
+
+                if (gSelectedIconIndex == iconNum && !p && rsIsObject(gSelectedIconTexture)) {
+                    rsgBindProgramFragment(gPFTexNearest);
+                    rsgBindTexture(gPFTexNearest, 0, gSelectedIconTexture);
+                    vpConstants->ImgSize.x = rsAllocationGetDimX(gSelectedIconTexture);
+                    vpConstants->ImgSize.y = rsAllocationGetDimY(gSelectedIconTexture);
+                    vpConstants->Position.y = y - (rsAllocationGetDimY(gSelectedIconTexture)
+                                                - rsAllocationGetDimY(gIconIDs[iconNum])) * 0.5f;
+                    rsgDrawMesh(gSMCell);
+                }
+
+                rsgBindProgramFragment(gPFTexMip);
+                vpConstants->ImgSize.x = rsAllocationGetDimX(gIconIDs[iconNum]);
+                vpConstants->ImgSize.y = rsAllocationGetDimY(gIconIDs[iconNum]);
+                vpConstants->Position.y = y - 0.2f;
+                rsgBindTexture(gPFTexMip, 0, gIconIDs[iconNum]);
+                rsgDrawMesh(gSMCell);
+
+                rsgBindProgramFragment(gPFTexMipAlpha);
+                vpConstants->ImgSize.x = rsAllocationGetDimX(gLabelIDs[iconNum]);
+                vpConstants->ImgSize.y = rsAllocationGetDimY(gLabelIDs[iconNum]);
+                vpConstants->Position.y = y - 64.f - 0.2f;
+                rsgBindTexture(gPFTexMipAlpha, 0, gLabelIDs[iconNum]);
+                rsgDrawMesh(gSMCell);
+            }
+            iconNum++;
+        }
+    }
+}
+
+
+int root()
+{
+    // Compute dt in seconds.
+    // physics may break if DT is large.
+    g_DT = min(rsGetDt(), 0.1f);
+
+    if (g_Zoom != gZoomTarget) {
+        float dz = g_DT * 1.7f;
+        if (gZoomTarget < 0.5f) {
+            dz = -dz;
+        }
+        if (fabs(g_Zoom - gZoomTarget) < fabs(dz)) {
+            g_Zoom = gZoomTarget;
+        } else {
+            g_Zoom += dz;
+        }
+        updateReadback();
+    }
+    g_Animation = pow(1.f - g_Zoom, 3.f);
+
+    // Set clear value to dim the background based on the zoom position.
+    if ((g_Zoom < 0.001f) && (gZoomTarget < 0.001f)) {
+        rsgClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+        // When we're zoomed out and not tracking motion events, reset the pos to 0.
+        if (!g_LastTouchDown) {
+            g_PosPage = 0;
+        }
+        return 0;
+    } else {
+        rsgClearColor(0.0f, 0.0f, 0.0f, g_Zoom);
+    }
+
+    rsgBindProgramStore(gPS);
+
+    // icons & labels
+    if (rsgGetWidth() > rsgGetHeight()) {
+        g_Cols = COLUMNS_PER_PAGE_LANDSCAPE;
+        g_Rows = ROWS_PER_PAGE_LANDSCAPE;
+    } else {
+        g_Cols = COLUMNS_PER_PAGE_PORTRAIT;
+        g_Rows = ROWS_PER_PAGE_PORTRAIT;
+    }
+
+    g_PosMax = ((gIconCount + (g_Cols-1)) / g_Cols) - g_Rows;
+    if (g_PosMax < 0) g_PosMax = 0;
+
+    updatePos();
+    updateReadback();
+
+    // Draw the icons ========================================
+    drawFrontGrid(g_PosPage, g_Animation);
+
+    rsgBindProgramFragment(gPFTexNearest);
+    draw_home_button();
+    return (g_PosVelocity != 0) || rsFrac(g_PosPage) || g_Zoom != gZoomTarget || (g_MoveToTime != 0);
+}
+
+