Merge commit 'remotes/korg/cupcake' into merge
diff --git a/Android.mk b/Android.mk
index 61ca3a3..b95e3d8 100644
--- a/Android.mk
+++ b/Android.mk
@@ -17,7 +17,7 @@
 LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_MODULE_TAGS := user eng development
+LOCAL_MODULE_TAGS := user
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9668483..272f7c1 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -20,7 +20,8 @@
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.launcher"
-    android:sharedUserId="android.uid.shared">
+    android:sharedUserId="android.uid.shared"
+    android:sharedUserLabel="@string/application_name">
 
     <permission
         android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
@@ -69,7 +70,8 @@
             android:launchMode="singleTask"
             android:clearTaskOnLaunch="true"
             android:stateNotNeeded="true"
-            android:theme="@style/Theme">
+            android:theme="@style/Theme"
+            android:windowSoftInputMode="stateUnspecified|adjustPan">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.HOME"/>
diff --git a/res/drawable/bg_appwidget_error.9.png b/res/drawable/bg_appwidget_error.9.png
new file mode 100644
index 0000000..5077424
--- /dev/null
+++ b/res/drawable/bg_appwidget_error.9.png
Binary files differ
diff --git a/res/drawable/clock_dial.png b/res/drawable/clock_dial.png
deleted file mode 100644
index eda3d17..0000000
--- a/res/drawable/clock_dial.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/clock_hour.png b/res/drawable/clock_hour.png
deleted file mode 100644
index fcfd948..0000000
--- a/res/drawable/clock_hour.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/clock_minute.png b/res/drawable/clock_minute.png
deleted file mode 100644
index afc0a3f..0000000
--- a/res/drawable/clock_minute.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/ic_launcher_alarmclock.png b/res/drawable/ic_launcher_alarmclock.png
deleted file mode 100755
index 30ff267..0000000
--- a/res/drawable/ic_launcher_alarmclock.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/ic_launcher_application.png b/res/drawable/ic_launcher_application.png
new file mode 100644
index 0000000..7502484
--- /dev/null
+++ b/res/drawable/ic_launcher_application.png
Binary files differ
diff --git a/res/drawable/ic_launcher_appwidget.png b/res/drawable/ic_launcher_appwidget.png
new file mode 100644
index 0000000..cb40a19
--- /dev/null
+++ b/res/drawable/ic_launcher_appwidget.png
Binary files differ
diff --git a/res/drawable/ic_launcher_empty.png b/res/drawable/ic_launcher_empty.png
new file mode 100644
index 0000000..59bb6c5
--- /dev/null
+++ b/res/drawable/ic_launcher_empty.png
Binary files differ
diff --git a/res/drawable/ic_search_gadget.png b/res/drawable/ic_search_widget.png
similarity index 100%
rename from res/drawable/ic_search_gadget.png
rename to res/drawable/ic_search_widget.png
Binary files differ
diff --git a/res/drawable/picture_frame.9.png b/res/drawable/picture_frame.9.png
deleted file mode 100644
index b153260..0000000
--- a/res/drawable/picture_frame.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/texture_brushed_steel.png b/res/drawable/texture_brushed_steel.png
new file mode 100644
index 0000000..73b3dfe
--- /dev/null
+++ b/res/drawable/texture_brushed_steel.png
Binary files differ
diff --git a/res/drawable/wallpaper.jpg b/res/drawable/wallpaper.jpg
deleted file mode 100644
index d19b642..0000000
--- a/res/drawable/wallpaper.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable/wallpaper_beach.jpg b/res/drawable/wallpaper_beach.jpg
index c059cc3..b502092 100644
--- a/res/drawable/wallpaper_beach.jpg
+++ b/res/drawable/wallpaper_beach.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_beach_small.jpg b/res/drawable/wallpaper_beach_small.jpg
index 3508509..46051cc 100644
--- a/res/drawable/wallpaper_beach_small.jpg
+++ b/res/drawable/wallpaper_beach_small.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_blue.jpg b/res/drawable/wallpaper_blue.jpg
index 032f598..0edd6bd 100644
--- a/res/drawable/wallpaper_blue.jpg
+++ b/res/drawable/wallpaper_blue.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_blue_small.jpg b/res/drawable/wallpaper_blue_small.jpg
index e987c69..5cf585a 100644
--- a/res/drawable/wallpaper_blue_small.jpg
+++ b/res/drawable/wallpaper_blue_small.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_green.jpg b/res/drawable/wallpaper_green.jpg
index 89b308b..36c9e08 100644
--- a/res/drawable/wallpaper_green.jpg
+++ b/res/drawable/wallpaper_green.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_green_small.jpg b/res/drawable/wallpaper_green_small.jpg
index 16790d2..d7c28ec 100644
--- a/res/drawable/wallpaper_green_small.jpg
+++ b/res/drawable/wallpaper_green_small.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_grey.jpg b/res/drawable/wallpaper_grey.jpg
index 7898568..ca48e6a 100644
--- a/res/drawable/wallpaper_grey.jpg
+++ b/res/drawable/wallpaper_grey.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_grey_small.jpg b/res/drawable/wallpaper_grey_small.jpg
index 12ca3e2..1734d2e 100644
--- a/res/drawable/wallpaper_grey_small.jpg
+++ b/res/drawable/wallpaper_grey_small.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_jellyfish_small.jpg b/res/drawable/wallpaper_jellyfish_small.jpg
index e140597..4e5d134 100644
--- a/res/drawable/wallpaper_jellyfish_small.jpg
+++ b/res/drawable/wallpaper_jellyfish_small.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_lake.jpg b/res/drawable/wallpaper_lake.jpg
deleted file mode 100644
index 5ba522f..0000000
--- a/res/drawable/wallpaper_lake.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable/wallpaper_lake_small.jpg b/res/drawable/wallpaper_lake_small.jpg
index 179b26a..36d56cc 100644
--- a/res/drawable/wallpaper_lake_small.jpg
+++ b/res/drawable/wallpaper_lake_small.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_mountain_small.jpg b/res/drawable/wallpaper_mountain_small.jpg
index 2416474..e2d5ac2 100644
--- a/res/drawable/wallpaper_mountain_small.jpg
+++ b/res/drawable/wallpaper_mountain_small.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_path.jpg b/res/drawable/wallpaper_path.jpg
index f4cb02c..e1c98c0 100644
--- a/res/drawable/wallpaper_path.jpg
+++ b/res/drawable/wallpaper_path.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_path_small.jpg b/res/drawable/wallpaper_path_small.jpg
index a1af4cd..f47d7a7 100644
--- a/res/drawable/wallpaper_path_small.jpg
+++ b/res/drawable/wallpaper_path_small.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_pink.jpg b/res/drawable/wallpaper_pink.jpg
index 1b9f226..74403e5 100644
--- a/res/drawable/wallpaper_pink.jpg
+++ b/res/drawable/wallpaper_pink.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_pink_small.jpg b/res/drawable/wallpaper_pink_small.jpg
index 7b7ad58..42b1770 100644
--- a/res/drawable/wallpaper_pink_small.jpg
+++ b/res/drawable/wallpaper_pink_small.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_ripples.jpg b/res/drawable/wallpaper_ripples.jpg
deleted file mode 100644
index 8624c86..0000000
--- a/res/drawable/wallpaper_ripples.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable/wallpaper_ripples_small.jpg b/res/drawable/wallpaper_ripples_small.jpg
deleted file mode 100644
index ce07d38..0000000
--- a/res/drawable/wallpaper_ripples_small.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable/wallpaper_road.jpg b/res/drawable/wallpaper_road.jpg
index 88611a4..3aaa639 100644
--- a/res/drawable/wallpaper_road.jpg
+++ b/res/drawable/wallpaper_road.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_snow_leopard.jpg b/res/drawable/wallpaper_snow_leopard.jpg
index 0d33157..bcbc51e 100644
--- a/res/drawable/wallpaper_snow_leopard.jpg
+++ b/res/drawable/wallpaper_snow_leopard.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_snow_leopard_small.jpg b/res/drawable/wallpaper_snow_leopard_small.jpg
index ec976aa..99db5c6 100644
--- a/res/drawable/wallpaper_snow_leopard_small.jpg
+++ b/res/drawable/wallpaper_snow_leopard_small.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_sunrise_small.jpg b/res/drawable/wallpaper_sunrise_small.jpg
index aadd980..248b7e7 100644
--- a/res/drawable/wallpaper_sunrise_small.jpg
+++ b/res/drawable/wallpaper_sunrise_small.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_sunset_small.jpg b/res/drawable/wallpaper_sunset_small.jpg
index eff8f0d..d5e5add 100644
--- a/res/drawable/wallpaper_sunset_small.jpg
+++ b/res/drawable/wallpaper_sunset_small.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_zanzibar_small.jpg b/res/drawable/wallpaper_zanzibar_small.jpg
index 7307a73..700a7fe 100644
--- a/res/drawable/wallpaper_zanzibar_small.jpg
+++ b/res/drawable/wallpaper_zanzibar_small.jpg
Binary files differ
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index 11a81fd..2b262c3 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -16,7 +16,6 @@
 
 <com.android.launcher.DragLayer
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
 
     android:id="@+id/drag_layer"
@@ -37,15 +36,15 @@
 
     </com.android.launcher.Workspace>
 
-    <com.android.internal.widget.SlidingDrawer
+    <SlidingDrawer
         android:id="@+id/drawer"
         android:layout_width="fill_parent"
         android:layout_height="fill_parent"
 
         android:orientation="horizontal"
-        androidprv:bottomOffset="7px"
-        androidprv:handle="@+id/all_apps"
-        androidprv:content="@+id/content">
+        android:bottomOffset="7dip"
+        android:handle="@+id/all_apps"
+        android:content="@+id/content">
         
         <com.android.launcher.HandleView
             android:id="@id/all_apps"
@@ -67,7 +66,7 @@
             android:layout_width="fill_parent"
             android:layout_height="fill_parent"
 
-            android:background="@color/grid_dark_background"
+            launcher:texture="@drawable/texture_brushed_steel"
 
             android:scrollbarStyle="outsideInset"
             android:drawSelectorOnTop="false"
@@ -81,7 +80,7 @@
             android:verticalSpacing="10dip"
             android:numColumns="5" />
 
-    </com.android.internal.widget.SlidingDrawer>
+    </SlidingDrawer>
 
     <com.android.launcher.DeleteZone
         android:id="@+id/delete_zone"
diff --git a/res/layout-port/application_boxed.xml b/res/layout-port/application_boxed.xml
index 63d2254..e71a2e2 100644
--- a/res/layout-port/application_boxed.xml
+++ b/res/layout-port/application_boxed.xml
@@ -22,7 +22,7 @@
     android:paddingTop="5dip"
     android:paddingBottom="2dip"
     android:drawablePadding="0dip"
-
+        
     android:textSize="13dip"
     android:maxLines="2"
     android:ellipsize="marquee"
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index 167331e..0c249a3 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -16,7 +16,6 @@
 
 <com.android.launcher.DragLayer
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
 
     android:id="@+id/drag_layer"
@@ -37,15 +36,15 @@
 
     </com.android.launcher.Workspace>
 
-    <com.android.internal.widget.SlidingDrawer
+    <SlidingDrawer
         android:id="@+id/drawer"
         android:layout_width="fill_parent"
         android:layout_height="fill_parent"
 
-        androidprv:topOffset="5px"
-        androidprv:bottomOffset="7px"
-        androidprv:handle="@+id/all_apps"
-        androidprv:content="@+id/content">
+        android:topOffset="5dip"
+        android:bottomOffset="7dip"
+        android:handle="@+id/all_apps"
+        android:content="@+id/content">
 
         <com.android.launcher.HandleView
             android:id="@id/all_apps"
@@ -67,7 +66,7 @@
             android:layout_width="fill_parent"
             android:layout_height="fill_parent"
 
-            android:background="@color/grid_dark_background"
+            launcher:texture="@drawable/texture_brushed_steel"
 
             android:scrollbarStyle="outsideInset"
             android:drawSelectorOnTop="false"
@@ -81,7 +80,7 @@
             android:verticalSpacing="10dip"
             android:numColumns="4" />
 
-    </com.android.internal.widget.SlidingDrawer>
+    </SlidingDrawer>
 
     <com.android.launcher.DeleteZone
         android:id="@+id/delete_zone"
diff --git a/res/layout/add_list_item.xml b/res/layout/add_list_item.xml
new file mode 100644
index 0000000..f276511
--- /dev/null
+++ b/res/layout/add_list_item.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:textAppearance="?android:attr/textAppearanceLargeInverse"
+    android:gravity="center_vertical"
+    android:drawablePadding="14dip"
+    android:paddingLeft="15dip"
+    android:paddingRight="15dip" />
diff --git a/res/layout/appwidget_error.xml b/res/layout/appwidget_error.xml
new file mode 100644
index 0000000..03d4ae4
--- /dev/null
+++ b/res/layout/appwidget_error.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:paddingTop="10dip"
+    android:paddingBottom="10dip"
+    android:paddingLeft="20dip"
+    android:paddingRight="20dip"
+    android:gravity="center"
+    android:background="@drawable/bg_appwidget_error"
+    android:textAppearance="?android:attr/textAppearanceMediumInverse"
+    android:textColor="@color/appwidget_error_color"
+    android:text="@string/gadget_error_text"
+    />
diff --git a/res/layout/create_shortcut_list.xml b/res/layout/create_shortcut_list.xml
index 63ca2b8..aa16733 100644
--- a/res/layout/create_shortcut_list.xml
+++ b/res/layout/create_shortcut_list.xml
@@ -17,11 +17,10 @@
 ** limitations under the License.
 */
 -->
-<ExpandableListView xmlns:android="http://schemas.android.com/apk/res/android"
+<ListView xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
-    android:layout_marginTop="5px"
+    android:layout_marginTop="5dip"
     android:cacheColorHint="@null"
-    android:childDivider="@android:drawable/divider_horizontal_bright"
     android:divider="@android:drawable/divider_horizontal_bright"
     android:scrollbars="vertical" />
diff --git a/res/layout/widget_clock.xml b/res/layout/widget_clock.xml
deleted file mode 100644
index b934e90..0000000
--- a/res/layout/widget_clock.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?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.
--->
-
-<AnalogClock xmlns:android="http://schemas.android.com/apk/res/android"
-    android:dial="@drawable/clock_dial"
-    android:hand_hour="@drawable/clock_hour"
-    android:hand_minute="@drawable/clock_minute"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent" />
diff --git a/res/layout/widget_photo_frame.xml b/res/layout/widget_photo_frame.xml
deleted file mode 100644
index 9fe3b80..0000000
--- a/res/layout/widget_photo_frame.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?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.launcher.PhotoFrame xmlns:android="http://schemas.android.com/apk/res/android"
-	android:scaleType="center"
-	android:cropToPadding="true"
-    android:background="@drawable/picture_frame"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent" />
diff --git a/res/layout/widget_search.xml b/res/layout/widget_search.xml
index 27e591d..209716d 100644
--- a/res/layout/widget_search.xml
+++ b/res/layout/widget_search.xml
@@ -26,7 +26,7 @@
         android:layout_height="wrap_content"
         android:src="@drawable/google_logo" />
         
-    <AutoCompleteTextView
+    <com.android.launcher.SearchAutoCompleteTextView
         android:id="@+id/input"
         android:layout_width="0dip"
         android:layout_weight="1"
@@ -36,7 +36,8 @@
         android:singleLine="true"
         android:selectAllOnFocus="true"
         android:completionThreshold="1"
-        android:inputType="textAutoComplete|textSearchString"
+        android:inputType="textAutoComplete"
+        android:imeOptions="actionSearch"
         />
         
      <ImageButton android:id="@+id/search_go_btn"
@@ -46,4 +47,10 @@
          android:src="@*android:drawable/ic_btn_search"
          />
     
+     <ImageButton android:id="@+id/search_voice_btn"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:src="@android:drawable/ic_btn_speak_now"
+         />
+
 </com.android.launcher.Search>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index c5afda2..dea039f 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -28,6 +28,8 @@
     <string name="menu_item_add_item">"Přidat na plochu"</string>
     <string name="group_applications">"Aplikace"</string>
     <string name="group_shortcuts">"Zástupce"</string>
+    <string name="group_search">"Hledat"</string>
+    <string name="group_folder">"Složka"</string>
     <string name="group_live_folders">"Složka Live"</string>
     <string name="group_widgets">"Miniaplikace"</string>
     <string name="group_wallpapers">"Tapeta"</string>
@@ -36,6 +38,8 @@
     <string name="add_photo_frame">"Rámeček fotografie"</string>
     <string name="add_search">"Vyhledávání"</string>
     <string name="out_of_space">"Na této ploše již není místo."</string>
+    <string name="title_select_shortcut">"Vyberte zástupce"</string>
+    <string name="title_select_live_folder">"Vyberte složku Live"</string>
     <string name="menu_add">"Přidat"</string>
     <string name="menu_wallpaper">"Tapeta"</string>
     <string name="menu_search">"Hledat"</string>
@@ -50,4 +54,6 @@
     <string name="permlab_write_settings">"zápis nastavení a odkazů plochy"</string>
     <string name="permdesc_write_settings">"Povoluje aplikaci změnit nastavení a odkazy plochy."</string>
     <string name="search_hint">"Vyhledávání Google"</string>
+    <!-- no translation found for gadget_error_text (8359351016167075858) -->
+    <skip />
 </resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 3f0c42a..c405852 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -28,6 +28,8 @@
     <string name="menu_item_add_item">"Zur Startseite hinzufügen"</string>
     <string name="group_applications">"Anwendung"</string>
     <string name="group_shortcuts">"Verknüpfung"</string>
+    <string name="group_search">"Suchen"</string>
+    <string name="group_folder">"Ordner"</string>
     <string name="group_live_folders">"Live-Ordner"</string>
     <string name="group_widgets">"Widget"</string>
     <string name="group_wallpapers">"Hintergrund"</string>
@@ -36,6 +38,8 @@
     <string name="add_photo_frame">"Bildrahmen"</string>
     <string name="add_search">"Suchen"</string>
     <string name="out_of_space">"Auf der Startseite ist kein Platz mehr vorhanden."</string>
+    <string name="title_select_shortcut">"Tastenkürzel auswählen"</string>
+    <string name="title_select_live_folder">"Live-Ordner auswählen"</string>
     <string name="menu_add">"Hinzufügen"</string>
     <string name="menu_wallpaper">"Hintergrund"</string>
     <string name="menu_search">"Suchen"</string>
@@ -50,4 +54,6 @@
     <string name="permlab_write_settings">"Einstellungen und Shortcuts für Startseite schreiben"</string>
     <string name="permdesc_write_settings">"Ermöglicht einer Anwendung, die Einstellungen und Shortcuts auf der Startseite zu ändern."</string>
     <string name="search_hint">"Google-Suche"</string>
+    <!-- no translation found for gadget_error_text (8359351016167075858) -->
+    <skip />
 </resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 161616d..20d2605 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -17,9 +17,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="application_name">"Página principal"</string>
     <string name="folder_name">"Carpeta"</string>
-    <string name="chooser_wallpaper">"Seleccionar papel tapiz de"</string>
-    <string name="wallpaper_instructions">"Establecer papel tapiz"</string>
-    <string name="pick_wallpaper">"Galería de papel tapiz"</string>
+    <string name="chooser_wallpaper">"Seleccionar fondo de pantalla de"</string>
+    <string name="wallpaper_instructions">"Establecer fondo de pantalla"</string>
+    <string name="pick_wallpaper">"Galería de fondo de pantalla"</string>
     <string name="activity_not_found">"La aplicación no está instalada en el teléfono."</string>
     <string name="rename_folder_label">"Nombre de carpeta"</string>
     <string name="rename_folder_title">"Cambiar nombre de carpeta"</string>
@@ -28,19 +28,23 @@
     <string name="menu_item_add_item">"Añadir a pantalla de página principal"</string>
     <string name="group_applications">"Aplicación"</string>
     <string name="group_shortcuts">"Acceso directo"</string>
+    <string name="group_search">"Búsqueda"</string>
+    <string name="group_folder">"Carpeta"</string>
     <string name="group_live_folders">"Carpeta activa"</string>
     <string name="group_widgets">"Widget"</string>
-    <string name="group_wallpapers">"Papel tapiz"</string>
+    <string name="group_wallpapers">"Fondo de pantalla"</string>
     <string name="add_folder">"Carpeta"</string>
     <string name="add_clock">"Reloj"</string>
     <string name="add_photo_frame">"Picture frame"</string>
     <string name="add_search">"Búsqueda de Google"</string>
     <string name="out_of_space">"No queda espacio en esta pantalla de página principal."</string>
+    <string name="title_select_shortcut">"Seleccionar acceso directo"</string>
+    <string name="title_select_live_folder">"Seleccionar carpeta activa"</string>
     <string name="menu_add">"Añadir"</string>
-    <string name="menu_wallpaper">"Papel tapiz"</string>
-    <string name="menu_search">"Búsqueda de Google"</string>
+    <string name="menu_wallpaper">"Fondo de pantalla"</string>
+    <string name="menu_search">"Buscar con Google"</string>
     <string name="menu_notifications">"Notificaciones"</string>
-    <string name="menu_settings">"Configuración"</string>
+    <string name="menu_settings">"Ajustes"</string>
     <string name="permlab_install_shortcut">"instalar accesos directos"</string>
     <string name="permdesc_install_shortcut">"Permite que una aplicación añada accesos directos sin intervención del usuario."</string>
     <string name="permlab_uninstall_shortcut">"desinstalar accesos directos"</string>
@@ -50,4 +54,6 @@
     <string name="permlab_write_settings">"escribir información de accesos directos y de configuración de la página principal"</string>
     <string name="permdesc_write_settings">"Permite que una aplicación modifique la configuración y los accesos directos de la página principal."</string>
     <string name="search_hint">"Búsqueda de Google"</string>
+    <!-- no translation found for gadget_error_text (8359351016167075858) -->
+    <skip />
 </resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index effdc7a..c01ef47 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -17,9 +17,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="application_name">"Accueil"</string>
     <string name="folder_name">"Dossier"</string>
-    <string name="chooser_wallpaper">"Sélectionner l\'arrière-plan à partir de"</string>
-    <string name="wallpaper_instructions">"Configurer l\'arrière-plan"</string>
-    <string name="pick_wallpaper">"Galerie des arrière-plans"</string>
+    <string name="chooser_wallpaper">"Sélectionner à partir de..."</string>
+    <string name="wallpaper_instructions">"Sélectionner"</string>
+    <string name="pick_wallpaper">"Galerie"</string>
     <string name="activity_not_found">"L\'application n\'est pas installée sur votre téléphone."</string>
     <string name="rename_folder_label">"Nom du dossier"</string>
     <string name="rename_folder_title">"Renommer le dossier"</string>
@@ -28,14 +28,18 @@
     <string name="menu_item_add_item">"Ajouter à l\'écran d\'accueil"</string>
     <string name="group_applications">"Application"</string>
     <string name="group_shortcuts">"Raccourci"</string>
-    <string name="group_live_folders">"Dossier live"</string>
+    <string name="group_search">"Recherche"</string>
+    <string name="group_folder">"Dossier"</string>
+    <string name="group_live_folders">"Dossier actif"</string>
     <string name="group_widgets">"Widget"</string>
     <string name="group_wallpapers">"Arrière-plan"</string>
     <string name="add_folder">"Dossier"</string>
     <string name="add_clock">"Horloge"</string>
     <string name="add_photo_frame">"Cadre d\'image"</string>
     <string name="add_search">"Rechercher"</string>
-    <string name="out_of_space">"Plus d\'espace libre sur l\'écran Accueil."</string>
+    <string name="out_of_space">"Plus d\'espace libre sur l\'écran d\'accueil."</string>
+    <string name="title_select_shortcut">"Sélectionner un raccourci"</string>
+    <string name="title_select_live_folder">"Sélectionner dossier actif"</string>
     <string name="menu_add">"Ajouter"</string>
     <string name="menu_wallpaper">"Arrière-plan"</string>
     <string name="menu_search">"Rechercher"</string>
@@ -45,9 +49,11 @@
     <string name="permdesc_install_shortcut">"Permet à une application d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur."</string>
     <string name="permlab_uninstall_shortcut">"désinstaller les raccourcis"</string>
     <string name="permdesc_uninstall_shortcut">"Permet à une application de supprimer les raccourcis sans l\'intervention de l\'utilisateur."</string>
-    <string name="permlab_read_settings">"lire les paramètres et les raccourcis de la page d\'accueil"</string>
+    <string name="permlab_read_settings">"Lire les paramètres et les raccourcis de la page d\'accueil"</string>
     <string name="permdesc_read_settings">"Permet à une application de lire les paramètres et raccourcis de la page d\'accueil."</string>
-    <string name="permlab_write_settings">"écrire les paramètres de la page d\'accueil et des raccourcis"</string>
+    <string name="permlab_write_settings">"Enregistrer les paramètres de la page d\'accueil et des raccourcis"</string>
     <string name="permdesc_write_settings">"Permet à une application de modifier les paramètres et les raccourcis de la page d\'accueil."</string>
     <string name="search_hint">"Recherche Google"</string>
+    <!-- no translation found for gadget_error_text (8359351016167075858) -->
+    <skip />
 </resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index d38fa30..47c6f9f 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -27,7 +27,9 @@
     <string name="cancel_action">"Annulla"</string>
     <string name="menu_item_add_item">"Aggiungi a schermata Home"</string>
     <string name="group_applications">"Applicazione"</string>
-    <string name="group_shortcuts">"Collegamento"</string>
+    <string name="group_shortcuts">"Scorciatoia"</string>
+    <string name="group_search">"Ricerca"</string>
+    <string name="group_folder">"Cartella"</string>
     <string name="group_live_folders">"Cartella dinamica"</string>
     <string name="group_widgets">"Widget"</string>
     <string name="group_wallpapers">"Sfondo"</string>
@@ -36,18 +38,22 @@
     <string name="add_photo_frame">"Cornice immagini"</string>
     <string name="add_search">"Ricerca"</string>
     <string name="out_of_space">"Spazio nella schermata Home esaurito."</string>
+    <string name="title_select_shortcut">"Seleziona collegamento"</string>
+    <string name="title_select_live_folder">"Seleziona cartella dinamica"</string>
     <string name="menu_add">"Aggiungi"</string>
     <string name="menu_wallpaper">"Sfondo"</string>
     <string name="menu_search">"Cerca"</string>
     <string name="menu_notifications">"Notifiche"</string>
     <string name="menu_settings">"Impostazioni"</string>
-    <string name="permlab_install_shortcut">"aggiungere collegamenti"</string>
-    <string name="permdesc_install_shortcut">"Consente a un\'applicazione di aggiungere collegamenti automaticamente."</string>
-    <string name="permlab_uninstall_shortcut">"eliminare collegamenti"</string>
-    <string name="permdesc_uninstall_shortcut">"Consente a un\'applicazione di rimuovere collegamenti automaticamente."</string>
-    <string name="permlab_read_settings">"leggere impostazioni e collegamenti in Home"</string>
-    <string name="permdesc_read_settings">"Consente a un\'applicazione di leggere le impostazioni e i collegamenti in Home."</string>
-    <string name="permlab_write_settings">"creare impostazioni e collegamenti in Home"</string>
-    <string name="permdesc_write_settings">"Consente a un\'applicazione di modificare le impostazioni e i collegamenti in Home."</string>
+    <string name="permlab_install_shortcut">"aggiungere scorciatorie"</string>
+    <string name="permdesc_install_shortcut">"Consente a un\'applicazione di aggiungere scorciatoie automaticamente."</string>
+    <string name="permlab_uninstall_shortcut">"eliminare scorciatoie"</string>
+    <string name="permdesc_uninstall_shortcut">"Consente a un\'applicazione di rimuovere scorciatoie automaticamente."</string>
+    <string name="permlab_read_settings">"leggere impostazioni e scorciatoie in Home"</string>
+    <string name="permdesc_read_settings">"Consente a un\'applicazione di leggere le impostazioni e le scorciatoie in Home."</string>
+    <string name="permlab_write_settings">"creare impostazioni e scorciatoie in Home"</string>
+    <string name="permdesc_write_settings">"Consente a un\'applicazione di modificare le impostazioni e le scorciatoie in Home."</string>
     <string name="search_hint">"Ricerca Google"</string>
+    <!-- no translation found for gadget_error_text (8359351016167075858) -->
+    <skip />
 </resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index c3b1ff8..cf71c65 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -28,6 +28,8 @@
     <string name="menu_item_add_item">"ホーム画面に追加"</string>
     <string name="group_applications">"アプリケーション"</string>
     <string name="group_shortcuts">"ショートカット"</string>
+    <string name="group_search">"検索"</string>
+    <string name="group_folder">"フォルダ"</string>
     <string name="group_live_folders">"ライブフォルダ"</string>
     <string name="group_widgets">"ウィジェット"</string>
     <string name="group_wallpapers">"壁紙"</string>
@@ -35,19 +37,23 @@
     <string name="add_clock">"時計"</string>
     <string name="add_photo_frame">"写真フレーム"</string>
     <string name="add_search">"検索"</string>
-    <string name="out_of_space">"このホーム画面には空きスペースがありません。"</string>
+    <string name="out_of_space">"ホーム画面に空きスペースがありません。"</string>
+    <string name="title_select_shortcut">"ショートカットを選択"</string>
+    <string name="title_select_live_folder">"ライブフォルダを選択"</string>
     <string name="menu_add">"追加"</string>
     <string name="menu_wallpaper">"壁紙"</string>
     <string name="menu_search">"検索"</string>
     <string name="menu_notifications">"通知"</string>
     <string name="menu_settings">"設定"</string>
     <string name="permlab_install_shortcut">"ショートカットのインストール"</string>
-    <string name="permdesc_install_shortcut">"ユーザー操作なしで、ショートカットをアプリケーションで追加できるようにします。"</string>
+    <string name="permdesc_install_shortcut">"ユーザー操作なしでショートカットの追加をアプリケーションに許可します。"</string>
     <string name="permlab_uninstall_shortcut">"ショートカットのアンインストール"</string>
-    <string name="permdesc_uninstall_shortcut">"ユーザー操作なしで、ショートカットをアプリケーションで削除できるようにします。"</string>
-    <string name="permlab_read_settings">"ホームの設定とショートカットの読み取り"</string>
+    <string name="permdesc_uninstall_shortcut">"ユーザー操作なしでショートカットの削除をアプリケーションに許可します。"</string>
+    <string name="permlab_read_settings">"ホーム設定とショートカットの読み取り"</string>
     <string name="permdesc_read_settings">"ホームの設定とショートカットの読み取りをアプリケーションに許可します。"</string>
     <string name="permlab_write_settings">"ホームの設定とショートカットの書き込み"</string>
     <string name="permdesc_write_settings">"ホームの設定とショートカットの変更をアプリケーションに許可します。"</string>
     <string name="search_hint">"Google検索"</string>
+    <!-- no translation found for gadget_error_text (8359351016167075858) -->
+    <skip />
 </resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
new file mode 100644
index 0000000..75ad6d8
--- /dev/null
+++ b/res/values-ko/strings.xml
@@ -0,0 +1,59 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="application_name">"홈"</string>
+    <string name="folder_name">"폴더"</string>
+    <string name="chooser_wallpaper">"배경화면 선택"</string>
+    <string name="wallpaper_instructions">"배경화면 설정"</string>
+    <string name="pick_wallpaper">"배경화면 갤러리"</string>
+    <string name="activity_not_found">"전화기에 설치되어 있지 않은 응용프로그램입니다."</string>
+    <string name="rename_folder_label">"폴더 이름"</string>
+    <string name="rename_folder_title">"폴더 이름 바꾸기"</string>
+    <string name="rename_action">"확인"</string>
+    <string name="cancel_action">"취소"</string>
+    <string name="menu_item_add_item">"홈 화면에 추가"</string>
+    <string name="group_applications">"응용프로그램"</string>
+    <string name="group_shortcuts">"바로가기"</string>
+    <string name="group_search">"검색"</string>
+    <string name="group_folder">"폴더"</string>
+    <string name="group_live_folders">"라이브 폴더"</string>
+    <string name="group_widgets">"위젯"</string>
+    <string name="group_wallpapers">"배경화면"</string>
+    <string name="add_folder">"폴더"</string>
+    <string name="add_clock">"시계"</string>
+    <string name="add_photo_frame">"사진 프레임"</string>
+    <string name="add_search">"검색"</string>
+    <string name="out_of_space">"홈 화면에 더 이상 공간이 없습니다."</string>
+    <string name="title_select_shortcut">"바로가기 선택"</string>
+    <string name="title_select_live_folder">"라이브 폴더 선택"</string>
+    <string name="menu_add">"추가"</string>
+    <string name="menu_wallpaper">"배경화면"</string>
+    <string name="menu_search">"검색"</string>
+    <string name="menu_notifications">"알림"</string>
+    <string name="menu_settings">"설정"</string>
+    <string name="permlab_install_shortcut">"바로가기 설치"</string>
+    <string name="permdesc_install_shortcut">"응용프로그램이 사용자의 작업 없이 바로가기를 추가할 수 있도록 합니다."</string>
+    <string name="permlab_uninstall_shortcut">"바로가기 제거"</string>
+    <string name="permdesc_uninstall_shortcut">"응용프로그램이 사용자의 작업 없이 바로가기를 제거할 수 있도록 합니다."</string>
+    <string name="permlab_read_settings">"홈 설정 및 바로가기 읽기"</string>
+    <string name="permdesc_read_settings">"응용프로그램이 홈에 있는 설정 및 바로가기를 읽을 수 있습니다."</string>
+    <string name="permlab_write_settings">"홈 설정 및 바로가기 쓰기"</string>
+    <string name="permdesc_write_settings">"응용프로그램이 홈에 있는 설정 및 바로가기를 변경할 수 있습니다."</string>
+    <string name="search_hint">"Google 검색"</string>
+    <!-- no translation found for gadget_error_text (8359351016167075858) -->
+    <skip />
+</resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
new file mode 100644
index 0000000..b14a5da
--- /dev/null
+++ b/res/values-nb/strings.xml
@@ -0,0 +1,59 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="application_name">"Hjem"</string>
+    <string name="folder_name">"Mappe"</string>
+    <string name="chooser_wallpaper">"Velg bakgrunnsbilde fra"</string>
+    <string name="wallpaper_instructions">"Velg bakgrunnsbilde"</string>
+    <string name="pick_wallpaper">"Bildegalleri"</string>
+    <string name="activity_not_found">"Applikasjonen er ikke installert."</string>
+    <string name="rename_folder_label">"Mappenavn"</string>
+    <string name="rename_folder_title">"Gi nytt navn til mappe"</string>
+    <string name="rename_action">"OK"</string>
+    <string name="cancel_action">"Avbryt"</string>
+    <string name="menu_item_add_item">"Legg til skrivebord"</string>
+    <string name="group_applications">"Applikasjon"</string>
+    <string name="group_shortcuts">"Snarvei"</string>
+    <string name="group_search">"Søk"</string>
+    <string name="group_folder">"Mappe"</string>
+    <string name="group_live_folders">"Aktiv mappe"</string>
+    <string name="group_widgets">"Skrivebordselement"</string>
+    <string name="group_wallpapers">"Bakgrunnsbilde"</string>
+    <string name="add_folder">"Mappe"</string>
+    <string name="add_clock">"Klokke"</string>
+    <string name="add_photo_frame">"Bilderamme"</string>
+    <string name="add_search">"Søk"</string>
+    <string name="out_of_space">"Ikke nok plass på skrivebordet."</string>
+    <string name="title_select_shortcut">"Velg snarvei"</string>
+    <string name="title_select_live_folder">"Velg aktiv mappe"</string>
+    <string name="menu_add">"Legg til"</string>
+    <string name="menu_wallpaper">"Bakgrunnsbilde"</string>
+    <string name="menu_search">"Søk"</string>
+    <string name="menu_notifications">"Varslinger"</string>
+    <string name="menu_settings">"Innstillinger"</string>
+    <string name="permlab_install_shortcut">"installere snarveier"</string>
+    <string name="permdesc_install_shortcut">"Lar applikasjonen legge til snarveier uten å involvere brukeren."</string>
+    <string name="permlab_uninstall_shortcut">"avinstallere snarveier"</string>
+    <string name="permdesc_uninstall_shortcut">"Lar applikasjonen fjerne snarveier uten å involvere brukeren."</string>
+    <string name="permlab_read_settings">"lese skrivebordsinnstillinger og -snarveier"</string>
+    <string name="permdesc_read_settings">"Lar applikasjonen lese innstillinger og snarveier fra skrivebordet."</string>
+    <string name="permlab_write_settings">"skrive skrivebordsinnstillinger og -snarveier"</string>
+    <string name="permdesc_write_settings">"Lar applikasjonen endre innstillinger og snarveier på skrivebordet."</string>
+    <string name="search_hint">"Google-søk"</string>
+    <!-- no translation found for gadget_error_text (8359351016167075858) -->
+    <skip />
+</resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 9e55d8f..e4959c0 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -28,6 +28,8 @@
     <string name="menu_item_add_item">"Toevoegen aan startpagina"</string>
     <string name="group_applications">"Toepassing"</string>
     <string name="group_shortcuts">"Snelkoppeling"</string>
+    <string name="group_search">"Zoeken"</string>
+    <string name="group_folder">"Map"</string>
     <string name="group_live_folders">"Live map"</string>
     <string name="group_widgets">"Widget"</string>
     <string name="group_wallpapers">"Achtergrond"</string>
@@ -36,6 +38,8 @@
     <string name="add_photo_frame">"Fotolijstje"</string>
     <string name="add_search">"Zoeken"</string>
     <string name="out_of_space">"Er is geen ruimte meer op dit startscherm."</string>
+    <string name="title_select_shortcut">"Snelkoppeling selecteren"</string>
+    <string name="title_select_live_folder">"Live map selecteren"</string>
     <string name="menu_add">"Toevoegen"</string>
     <string name="menu_wallpaper">"Achtergrond"</string>
     <string name="menu_search">"Zoeken"</string>
@@ -49,5 +53,7 @@
     <string name="permdesc_read_settings">"Hiermee kan een toepassing de instellingen en snelkoppelingen op de startpagina lezen."</string>
     <string name="permlab_write_settings">"instellingen en snelkoppelingen voor de startpagina schrijven"</string>
     <string name="permdesc_write_settings">"Hiermee kan een toepassing de instellingen en snelkoppelingen op de startpagina wijzigen."</string>
-    <string name="search_hint">"Zoeken met Google"</string>
+    <string name="search_hint">"Google Zoeken"</string>
+    <!-- no translation found for gadget_error_text (8359351016167075858) -->
+    <skip />
 </resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 9356954..99718cb 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -15,7 +15,7 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="application_name">"Strona główna"</string>
+    <string name="application_name">"Sieć"</string>
     <string name="folder_name">"Folder"</string>
     <string name="chooser_wallpaper">"Wybierz tapetę"</string>
     <string name="wallpaper_instructions">"Ustaw tapetę"</string>
@@ -28,14 +28,18 @@
     <string name="menu_item_add_item">"Dodaj do strony głównej"</string>
     <string name="group_applications">"Aplikacja"</string>
     <string name="group_shortcuts">"Skrót"</string>
-    <string name="group_live_folders">"Folder Live"</string>
+    <string name="group_search">"Wyszukiwarka"</string>
+    <string name="group_folder">"Folder"</string>
+    <string name="group_live_folders">"Folder aktywny"</string>
     <string name="group_widgets">"Widget"</string>
     <string name="group_wallpapers">"Tapeta"</string>
     <string name="add_folder">"Folder"</string>
     <string name="add_clock">"Zegar"</string>
     <string name="add_photo_frame">"Ramka obrazu"</string>
-    <string name="add_search">"Szukaj"</string>
+    <string name="add_search">"Wyszukiwarka"</string>
     <string name="out_of_space">"Brak miejsca na tej stronie głównej"</string>
+    <string name="title_select_shortcut">"Wybierz skrót"</string>
+    <string name="title_select_live_folder">"Wybierz folder aktywny"</string>
     <string name="menu_add">"Dodaj"</string>
     <string name="menu_wallpaper">"Tapeta"</string>
     <string name="menu_search">"Szukaj"</string>
@@ -50,4 +54,6 @@
     <string name="permlab_write_settings">"zapisywanie ustawień i skrótów strony głównej"</string>
     <string name="permdesc_write_settings">"Umożliwia aplikacji zmianę ustawień i skrótów strony głównej."</string>
     <string name="search_hint">"Szukaj w Google"</string>
+    <!-- no translation found for gadget_error_text (8359351016167075858) -->
+    <skip />
 </resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 9bb4787..ab6120e 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -28,6 +28,8 @@
     <string name="menu_item_add_item">"Добавление на главный экран"</string>
     <string name="group_applications">"Приложение"</string>
     <string name="group_shortcuts">"Ярлык"</string>
+    <string name="group_search">"Поиск"</string>
+    <string name="group_folder">"Папка"</string>
     <string name="group_live_folders">"Динамическая папка"</string>
     <string name="group_widgets">"Виджет"</string>
     <string name="group_wallpapers">"Фоновый рисунок"</string>
@@ -36,6 +38,8 @@
     <string name="add_photo_frame">"Рамка для картинки"</string>
     <string name="add_search">"Поиск"</string>
     <string name="out_of_space">"На главном экране больше нет места."</string>
+    <string name="title_select_shortcut">"Выберите ярлык"</string>
+    <string name="title_select_live_folder">"Выберите активную папку"</string>
     <string name="menu_add">"Добавить"</string>
     <string name="menu_wallpaper">"Фоновый рисунок"</string>
     <string name="menu_search">"Искать"</string>
@@ -50,4 +54,6 @@
     <string name="permlab_write_settings">"записывать ярлыки и настройки главного экрана"</string>
     <string name="permdesc_write_settings">"Позволяет приложению изменять настройки и ярлыки на главном экране."</string>
     <string name="search_hint">"Поиск Google"</string>
+    <!-- no translation found for gadget_error_text (8359351016167075858) -->
+    <skip />
 </resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 72ffcf4..f5d929d 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -28,6 +28,8 @@
     <string name="menu_item_add_item">"添加到“主页”屏幕"</string>
     <string name="group_applications">"应用程序"</string>
     <string name="group_shortcuts">"快捷键"</string>
+    <string name="group_search">"搜索"</string>
+    <string name="group_folder">"文件夹"</string>
     <string name="group_live_folders">"活动的文件夹"</string>
     <string name="group_widgets">"小工具"</string>
     <string name="group_wallpapers">"壁纸"</string>
@@ -36,6 +38,8 @@
     <string name="add_photo_frame">"相框"</string>
     <string name="add_search">"搜索"</string>
     <string name="out_of_space">"该“主页”屏幕上没有多余空间。"</string>
+    <string name="title_select_shortcut">"选择快捷键"</string>
+    <string name="title_select_live_folder">"选择活动文件夹"</string>
     <string name="menu_add">"添加"</string>
     <string name="menu_wallpaper">"壁纸"</string>
     <string name="menu_search">"搜索"</string>
@@ -50,4 +54,6 @@
     <string name="permlab_write_settings">"写入“主页”设置和快捷键"</string>
     <string name="permdesc_write_settings">"允许应用程序更改“主页”中的设置和快捷键。"</string>
     <string name="search_hint">"Google 搜索"</string>
+    <!-- no translation found for gadget_error_text (8359351016167075858) -->
+    <skip />
 </resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 35db622..326f2a2 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -28,6 +28,8 @@
     <string name="menu_item_add_item">"新增至首頁畫面"</string>
     <string name="group_applications">"應用程式"</string>
     <string name="group_shortcuts">"捷徑"</string>
+    <string name="group_search">"搜尋"</string>
+    <string name="group_folder">"資料夾"</string>
     <string name="group_live_folders">"使用中的資料夾"</string>
     <string name="group_widgets">"Widget"</string>
     <string name="group_wallpapers">"桌布"</string>
@@ -36,6 +38,8 @@
     <string name="add_photo_frame">"相框"</string>
     <string name="add_search">"搜尋"</string>
     <string name="out_of_space">"首頁已無空間"</string>
+    <string name="title_select_shortcut">"選取捷徑"</string>
+    <string name="title_select_live_folder">"選取作用中資料夾"</string>
     <string name="menu_add">"新增"</string>
     <string name="menu_wallpaper">"桌布"</string>
     <string name="menu_search">"搜尋"</string>
@@ -50,4 +54,6 @@
     <string name="permlab_write_settings">"寫入首頁設定和捷徑"</string>
     <string name="permdesc_write_settings">"允許應用程式變更首頁中的設定和捷徑。"</string>
     <string name="search_hint">"Google 搜尋"</string>
+    <!-- no translation found for gadget_error_text (8359351016167075858) -->
+    <skip />
 </resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 87f5b78..ab545aa 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -69,4 +69,11 @@
         <attr name="direction" />
     </declare-styleable>
 
+    <!-- AllAppsGridView specific attributes. These attributes are used to customize
+         the list of all apps in XML files. -->
+    <declare-styleable name="AllAppsGridView">
+        <!-- The background texture. -->
+        <attr name="texture" format="reference"  />
+    </declare-styleable>
+
 </resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index d4051f0..5574944 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -22,4 +22,6 @@
     <color name="grid_dark_background">#EB191919</color>     
     <color name="bubble_dark_background">#B2191919</color>
     <color name="delete_color_filter">#A5FF0000</color>
+
+    <color name="appwidget_error_color">#fccc</color>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c1d3455..a7945cb 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -52,8 +52,13 @@
     <string name="group_applications">Application</string>
     <!-- Options in "Add to Home" dialog box; Title of the group containing the list of all shortcuts -->
     <string name="group_shortcuts">Shortcut</string>
+    <!-- Options in "Add to Home" dialog box; Title of the search gadget -->
+    <string name="group_search">Search</string>
+    <!-- Options in "Add to Home" dialog box; Title of the folder gadget -->
+    <string name="group_folder">Folder</string>
+    <!-- Options in "Add to Home" dialog box; Title of the group containing the list of all live folders -->
     <string name="group_live_folders">Live folder</string>
-    <!-- Options in "Add to Home" dialog box; Title of the group containing the list of all widgets -->
+    <!-- Options in "Add to Home" dialog box; Title of the group containing the list of all widgets/gadgets -->
     <string name="group_widgets">Widget</string>
     <!-- Options in "Add to Home" dialog box; Title of the group containing the list of apps that can set the wallpaper-->
     <string name="group_wallpapers">Wallpaper</string>
@@ -68,6 +73,11 @@
     <!-- Error message when user has filled a home screen, possibly not used -->
     <string name="out_of_space">No more room on this Home screen.</string>
 
+    <!-- Title of dialog when user is selecting shortcut to add to homescreen -->
+    <string name="title_select_shortcut">Select shortcut</string>
+    <!-- Title of dialog when user is selecting live folder to add to homescreen -->
+    <string name="title_select_live_folder">Select live folder</string>
+
     <!-- Menus items: -->
     <skip />
     <!-- Verb, menu item used to add an item on the desktop -->
@@ -101,5 +111,8 @@
          This translation SHOULD MATCH the string "search_hint" which is found in 
          GoogleSearch/res/values/strings.xml -->
     <string name="search_hint">Google Search</string>
-        
+
+    <!-- Text to show user in place of a gadget when we can't display it properly -->
+    <string name="gadget_error_text">Problem loading widget</string>
+
 </resources>
diff --git a/src/com/android/launcher/AddAdapter.java b/src/com/android/launcher/AddAdapter.java
index 8eebe39..cbcb338 100644
--- a/src/com/android/launcher/AddAdapter.java
+++ b/src/com/android/launcher/AddAdapter.java
@@ -16,456 +16,111 @@
 
 package com.android.launcher;
 
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.BaseAdapter;
 import android.widget.TextView;
-import android.widget.BaseExpandableListAdapter;
-import android.graphics.drawable.Drawable;
-import android.provider.LiveFolders;
 
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
 
 /**
- * Shows a list of all the items that can be added to the workspace.
+ * Adapter showing the types of items that can be added to a {@link Workspace}.
  */
-public final class AddAdapter extends BaseExpandableListAdapter {
-    private static final int GROUP_APPLICATIONS = 0;
-    private static final int GROUP_SHORTCUTS = 1;
-    private static final int GROUP_WIDGETS = 2;
-    private static final int GROUP_LIVE_FOLDERS = 3;
-    private static final int GROUP_WALLPAPERS = 4;
-
-    private final Intent mCreateShortcutIntent;
-    private final Intent mCreateLiveFolderIntent;
-    private Intent mSetWallpaperIntent;
+public class AddAdapter extends BaseAdapter {
+    
+    private final Launcher mLauncher;
     private final LayoutInflater mInflater;
-    private Launcher mLauncher;
-    private Group[] mGroups;
-
-    /**
-     * Abstract class representing one thing that can be added
-     */
-    public abstract class AddAction implements Runnable {
-        protected final Context mContext;
-
-        AddAction(Context context) {
-            mContext = context;
-        }
-
-        Drawable getIcon(int resource) {
-            return mContext.getResources().getDrawable(resource);
-        }
-
-        public abstract void bindView(View v);
-    }
-
-    /**
-     * Class representing an action that will create set the wallpaper.
-     */
-    public class SetWallpaperAction extends CreateShortcutAction {
-        SetWallpaperAction(Context context, ResolveInfo info) {
-            super(context, info);
-        }
-
-        public void run() {
-            Intent intent = new Intent(mSetWallpaperIntent);
-            ActivityInfo activityInfo = mInfo.activityInfo;
-            intent.setComponent(new ComponentName(activityInfo.applicationInfo.packageName,
-                    activityInfo.name));
-            mLauncher.startActivity(intent);
-        }
-    }
+    
+    private final ArrayList<ListItem> mItems = new ArrayList<ListItem>();
+    
+    public static final int ITEM_APPLICATION = 0;
+    public static final int ITEM_SHORTCUT = 1;
+    public static final int ITEM_SEARCH = 2;
+    public static final int ITEM_APPWIDGET = 3;
+    public static final int ITEM_LIVE_FOLDER = 4;
+    public static final int ITEM_FOLDER = 5;
+    public static final int ITEM_WALLPAPER = 6;
     
     /**
-     * Class representing an action that will create a specific type
-     * of shortcut
+     * Specific item in our list.
      */
-    public class CreateShortcutAction extends AddAction {
+    public class ListItem {
+        public final CharSequence text;
+        public final Drawable image;
+        public final int actionTag;
         
-        ResolveInfo mInfo;
-        private CharSequence mLabel;
-        private Drawable mIcon;
-
-        CreateShortcutAction(Context context, ResolveInfo info) {
-            super(context);
-            mInfo = info;
-        }
-
-        @Override
-        public void bindView(View view) {
-            ResolveInfo info = mInfo;
-            TextView text = (TextView) view;
-
-            PackageManager pm = mLauncher.getPackageManager();
-
-            if (mLabel == null) {
-                mLabel = info.loadLabel(pm);
-                if (mLabel == null) {
-                    mLabel = info.activityInfo.name;
-                }
+        public ListItem(Resources res, int textResourceId, int imageResourceId, int actionTag) {
+            text = res.getString(textResourceId);
+            if (imageResourceId != -1) {
+                image = res.getDrawable(imageResourceId);
+            } else {
+                image = null;
             }
-
-            if (mIcon == null) {
-                mIcon = Utilities.createIconThumbnail(info.loadIcon(pm), mContext);
-            }
-
-            text.setText(mLabel);
-            text.setCompoundDrawablesWithIntrinsicBounds(mIcon, null, null, null);
-        }
-
-        public void run() {
-            Intent intent = new Intent(mCreateShortcutIntent);
-            ActivityInfo activityInfo = mInfo.activityInfo;
-            intent.setComponent(new ComponentName(activityInfo.applicationInfo.packageName,
-                    activityInfo.name));
-            mLauncher.addShortcut(intent);
-        }
-    }
-
-    /**
-     * Class representing an action that will create a specific type
-     * of live folder
-     */
-    public class CreateLiveFolderAction extends CreateShortcutAction {
-        CreateLiveFolderAction(Context context, ResolveInfo info) {
-            super(context, info);
-        }
-
-        @Override
-        public void run() {
-            Intent intent = new Intent(mCreateLiveFolderIntent);
-            ActivityInfo activityInfo = mInfo.activityInfo;
-            intent.setComponent(new ComponentName(activityInfo.applicationInfo.packageName,
-                    activityInfo.name));
-            mLauncher.addLiveFolder(intent);
-        }
-    }
-
-    /**
-     * Class representing an action that will add a folder
-     */
-    public class CreateFolderAction extends AddAction {
-        private Drawable mIcon;
-
-        CreateFolderAction(Context context) {
-            super(context);
-        }
-
-        @Override
-        public void bindView(View view) {
-            TextView text = (TextView) view;
-            text.setText(R.string.add_folder);
-            if (mIcon == null) mIcon = getIcon(R.drawable.ic_launcher_folder);
-            text.setCompoundDrawablesWithIntrinsicBounds(mIcon, null, null, null);
-        }
-
-        public void run() {
-            mLauncher.addFolder();
-        }
-    }
-
-    /**
-     * Class representing an action that will add a folder
-     */
-    public class CreateClockAction extends AddAction {
-
-        CreateClockAction(Context context) {
-            super(context);
-        }
-
-        @Override
-        public void bindView(View view) {
-            TextView text = (TextView) view;
-            text.setText(R.string.add_clock);
-            Drawable icon = getIcon(R.drawable.ic_launcher_alarmclock);
-            text.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
-        }
-
-        public void run() {
-            mLauncher.addClock();
-        }
-    }
-
-    /**
-     * Class representing an action that will add a PhotoFrame
-     */
-    public class CreatePhotoFrameAction extends AddAction {
-        private Drawable mIcon;
-
-        CreatePhotoFrameAction(Context context) {
-            super(context);
-        }
-
-        @Override
-        public void bindView(View view) {
-            TextView text = (TextView) view;
-            text.setText(R.string.add_photo_frame);
-            if (mIcon == null) mIcon = getIcon(R.drawable.ic_launcher_gallery);
-            text.setCompoundDrawablesWithIntrinsicBounds(mIcon, null, null, null);
-        }
-
-        public void run() {
-            mLauncher.getPhotoForPhotoFrame();
-        }
-    }
-
-
-    /**
-     * Class representing an action that will add a Search widget
-     */
-    public class CreateSearchAction extends AddAction {
-        private Drawable mIcon;
-
-        CreateSearchAction(Context context) {
-            super(context);
-        }
-
-        @Override
-        public void bindView(View view) {
-            TextView text = (TextView) view;
-            text.setText(R.string.add_search);
-            if (mIcon == null) mIcon = getIcon(R.drawable.ic_search_gadget);
-            text.setCompoundDrawablesWithIntrinsicBounds(mIcon, null, null, null);
-        }
-
-        public void run() {
-            mLauncher.addSearch();
+            this.actionTag = actionTag;
         }
     }
     
-    private class Group {
-        private String mName;
-        private ArrayList<AddAction> mList;
-
-        Group(String name) {
-            mName = name;
-            mList = new ArrayList<AddAction>();
-        }
-
-        void add(AddAction action) {
-            mList.add(action);
-        }
-
-        int size() {
-            return mList.size();
-        }
-
-        String getName() {
-            return mName;
-        }
-
-        void run(int position) {
-            mList.get(position).run();
-        }
-
-        void bindView(int childPosition, View view) {
-            mList.get(childPosition).bindView(view);
-        }
-
-        public Object get(int childPosition) {
-            return mList.get(childPosition);
-        }
-    }
-
-    private class ApplicationsGroup extends Group {
-        private final Launcher mLauncher;
-        private final ArrayList<ApplicationInfo> mApplications;
-
-        ApplicationsGroup(Launcher launcher, String name) {
-            super(name);
-            mLauncher = launcher;
-            mApplications = Launcher.getModel().getApplications();
-        }
-
-        @Override
-        int size() {
-            return mApplications == null ? 0 : mApplications.size();
-        }
-
-        @Override
-        void add(AddAction action) {
-        }
-
-        @Override
-        void run(int position) {
-            final ApplicationInfo info = mApplications.get(position);
-            mLauncher.addApplicationShortcut(info);
-        }
-
-        @Override
-        void bindView(int childPosition, View view) {
-            TextView text = (TextView) view.findViewById(R.id.title);
-
-            final ApplicationInfo info = mApplications.get(childPosition);
-            text.setText(info.title);
-            if (!info.filtered) {
-                info.icon = Utilities.createIconThumbnail(info.icon, mLauncher);
-                info.filtered = true;
-            }
-            text.setCompoundDrawablesWithIntrinsicBounds(info.icon, null, null, null);
-        }
-
-        @Override
-        public Object get(int childPosition) {
-            return mApplications.get(childPosition);
-        }
-    }
-
-    public AddAdapter(Launcher launcher, boolean forFolder) {
-        mCreateShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
-        mCreateShortcutIntent.setComponent(null);
-
-        mCreateLiveFolderIntent = new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER);
-        mCreateLiveFolderIntent.setComponent(null);
-
-        mSetWallpaperIntent = new Intent(Intent.ACTION_SET_WALLPAPER);
-        mSetWallpaperIntent.setComponent(null);
-
+    public AddAdapter(Launcher launcher) {
+        super();
+        
         mLauncher = launcher;
-        mInflater = (LayoutInflater) launcher.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        mInflater = (LayoutInflater) mLauncher.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        
+        // Create default actions
+        Resources res = launcher.getResources();
+        
+        mItems.add(new ListItem(res, R.string.group_applications,
+                R.drawable.ic_launcher_application, ITEM_APPLICATION));
+        
+        mItems.add(new ListItem(res, R.string.group_shortcuts,
+                R.drawable.ic_launcher_empty, ITEM_SHORTCUT));
+        
+        mItems.add(new ListItem(res, R.string.group_search,
+                R.drawable.ic_search_widget, ITEM_SEARCH));
+        
+        mItems.add(new ListItem(res, R.string.group_widgets,
+                R.drawable.ic_launcher_appwidget, ITEM_APPWIDGET));
+        
+        mItems.add(new ListItem(res, R.string.group_live_folders,
+                R.drawable.ic_launcher_empty, ITEM_LIVE_FOLDER));
+        
+        mItems.add(new ListItem(res, R.string.group_folder,
+                R.drawable.ic_launcher_folder, ITEM_FOLDER));
+        
+        mItems.add(new ListItem(res, R.string.group_wallpapers,
+                R.drawable.ic_launcher_gallery, ITEM_WALLPAPER));
 
-        mGroups = new Group[forFolder ? 2 : 5];
-        final Group[] groups = mGroups;
-        groups[GROUP_APPLICATIONS] = new ApplicationsGroup(mLauncher,
-                mLauncher.getString(R.string.group_applications));
-        groups[GROUP_SHORTCUTS] = new Group(mLauncher.getString(R.string.group_shortcuts));
-        groups[GROUP_LIVE_FOLDERS] = new Group(mLauncher.getString(R.string.group_live_folders));
+    }
 
-        if (!forFolder) {
-            groups[GROUP_WALLPAPERS] = new Group(mLauncher.getString(R.string.group_wallpapers));
-            groups[GROUP_SHORTCUTS].add(new CreateFolderAction(launcher));
-            groups[GROUP_WIDGETS] = new Group(mLauncher.getString(R.string.group_widgets));
-
-            final Group widgets = groups[GROUP_WIDGETS];
-            widgets.add(new CreateClockAction(launcher));
-            widgets.add(new CreatePhotoFrameAction(launcher));
-            widgets.add(new CreateSearchAction(launcher));
+    public View getView(int position, View convertView, ViewGroup parent) {
+        ListItem item = (ListItem) getItem(position);
+        
+        if (convertView == null) {
+            convertView = mInflater.inflate(R.layout.add_list_item, parent, false);
         }
         
-        PackageManager packageManager = launcher.getPackageManager();
-
-        List<ResolveInfo> list = findTargetsForIntent(mCreateShortcutIntent, packageManager);
-        if (list != null && list.size() > 0) {
-            int count = list.size();
-            final Group shortcuts = groups[GROUP_SHORTCUTS];
-            for (int i = 0; i < count; i++) {
-                ResolveInfo resolveInfo = list.get(i);
-                shortcuts.add(new CreateShortcutAction(launcher, resolveInfo));
-            }
-        }
-
-        list = findTargetsForIntent(mCreateLiveFolderIntent, packageManager);
-        if (list != null && list.size() > 0) {
-            int count = list.size();
-            final Group shortcuts = groups[GROUP_LIVE_FOLDERS];
-            for (int i = 0; i < count; i++) {
-                ResolveInfo resolveInfo = list.get(i);
-                shortcuts.add(new CreateLiveFolderAction(launcher, resolveInfo));
-            }
-        }
-
-        list = findTargetsForIntent(mSetWallpaperIntent, packageManager);
-        if (list != null && list.size() > 0) {
-            int count = list.size();
-            final Group shortcuts = groups[GROUP_WALLPAPERS];
-            for (int i = 0; i < count; i++) {
-                ResolveInfo resolveInfo = list.get(i);
-                shortcuts.add(new SetWallpaperAction(launcher, resolveInfo));
-            }
-        }
+        TextView textView = (TextView) convertView;
+        textView.setTag(item);
+        textView.setText(item.text);
+        textView.setCompoundDrawablesWithIntrinsicBounds(item.image, null, null, null);
+        
+        return convertView;
     }
 
-    private List<ResolveInfo> findTargetsForIntent(Intent intent, PackageManager packageManager) {
-        List<ResolveInfo> list = packageManager.queryIntentActivities(intent,
-                PackageManager.MATCH_DEFAULT_ONLY);
-        if (list != null) {
-            int count = list.size();
-            if (count > 1) {
-                // Only display the first matches that are either of equal
-                // priority or have asked to be default options.
-                ResolveInfo firstInfo = list.get(0);
-                for (int i=1; i<count; i++) {
-                    ResolveInfo resolveInfo = list.get(i);
-                    if (firstInfo.priority != resolveInfo.priority ||
-                        firstInfo.isDefault != resolveInfo.isDefault) {
-                        while (i < count) {
-                            list.remove(i);
-                            count--;
-                        }
-                    }
-                }
-                Collections.sort(list, new ResolveInfo.DisplayNameComparator(packageManager));
-            }
-        }
-        return list;
+    public int getCount() {
+        return mItems.size();
     }
 
-    public int getGroupCount() {
-        return mGroups.length;
+    public Object getItem(int position) {
+        return mItems.get(position);
     }
 
-    public int getChildrenCount(int groupPosition) {
-        return mGroups[groupPosition].size();
+    public long getItemId(int position) {
+        return position;
     }
-
-    public Object getGroup(int groupPosition) {
-        return mGroups[groupPosition].getName();
-    }
-
-    public Object getChild(int groupPosition, int childPosition) {
-        return mGroups[groupPosition].get(childPosition);
-    }
-
-    public long getGroupId(int groupPosition) {
-        return groupPosition;
-    }
-
-    public long getChildId(int groupPosition, int childPosition) {
-        return (groupPosition << 16) | childPosition;
-    }
-
-    public boolean hasStableIds() {
-        return true;
-    }
-
-    public View getGroupView(int groupPosition, boolean isExpanded,
-            View convertView, ViewGroup parent) {
-        View view;
-        if (convertView == null) {
-            view = mInflater.inflate(R.layout.create_shortcut_group_item, parent, false);
-        } else {
-            view = convertView;
-        }
-        ((TextView) view).setText(mGroups[groupPosition].getName());
-        return view;
-    }
-
-    public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
-            View convertView, ViewGroup parent) {
-        View view;
-        if (convertView == null) {
-            view = mInflater.inflate(R.layout.create_shortcut_list_item, parent, false);
-        } else {
-            view = convertView;
-        }
-        mGroups[groupPosition].bindView(childPosition, view);
-        return view;
-    }
-
-    public boolean isChildSelectable(int groupPosition, int childPosition) {
-        return true;
-    }
-
-    void performAction(int groupPosition, int childPosition) {
-        mGroups[groupPosition].run(childPosition);
-    }
+    
 }
diff --git a/src/com/android/launcher/AllAppsGridView.java b/src/com/android/launcher/AllAppsGridView.java
index a898c1a..b8f7902 100644
--- a/src/com/android/launcher/AllAppsGridView.java
+++ b/src/com/android/launcher/AllAppsGridView.java
@@ -19,25 +19,46 @@
 import android.widget.GridView;
 import android.widget.AdapterView;
 import android.content.Context;
+import android.content.res.TypedArray;
 import android.util.AttributeSet;
 import android.view.View;
+import android.graphics.BitmapFactory;
+import android.graphics.Bitmap;
+import android.graphics.Paint;
+import android.graphics.Canvas;
 
 public class AllAppsGridView extends GridView implements AdapterView.OnItemClickListener,
         AdapterView.OnItemLongClickListener, DragSource {
 
     private DragController mDragger;
     private Launcher mLauncher;
+    private Bitmap mTexture;
+    private Paint mPaint;
+    private int mTextureWidth;
+    private int mTextureHeight;
 
     public AllAppsGridView(Context context) {
         super(context);
     }
 
     public AllAppsGridView(Context context, AttributeSet attrs) {
-        super(context, attrs);
+        this(context, attrs, com.android.internal.R.attr.gridViewStyle);
     }
 
     public AllAppsGridView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
+
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AllAppsGridView, defStyle, 0);
+        final int textureId = a.getResourceId(R.styleable.AllAppsGridView_texture, 0);
+        if (textureId != 0) {
+            mTexture = BitmapFactory.decodeResource(getResources(), textureId);
+            mTextureWidth = mTexture.getWidth();
+            mTextureHeight = mTexture.getHeight();
+
+            mPaint = new Paint();
+            mPaint.setDither(false);
+        }
+        a.recycle();
     }
 
     @Override
@@ -46,6 +67,32 @@
         setOnItemLongClickListener(this);
     }
 
+    @Override
+    public void draw(Canvas canvas) {
+        final Bitmap texture = mTexture;
+        final Paint paint = mPaint;
+
+        final int width = getWidth();
+        final int height = getHeight();
+
+        final int textureWidth = mTextureWidth;
+        final int textureHeight = mTextureHeight;
+
+        int x = 0;
+        int y;
+
+        while (x < width) {
+            y = 0;
+            while (y < height) {
+                canvas.drawBitmap(texture, x, y, paint);
+                y += textureHeight;
+            }
+            x += textureWidth;
+        }
+
+        super.draw(canvas);
+    }
+
     public void onItemClick(AdapterView parent, View v, int position, long id) {
         ApplicationInfo app = (ApplicationInfo) parent.getItemAtPosition(position);
         mLauncher.startActivitySafely(app.intent);
diff --git a/src/com/android/launcher/BubbleTextView.java b/src/com/android/launcher/BubbleTextView.java
index f2c31e9..3782454 100644
--- a/src/com/android/launcher/BubbleTextView.java
+++ b/src/com/android/launcher/BubbleTextView.java
@@ -40,6 +40,9 @@
 
     private boolean mBackgroundSizeChanged;
     private Drawable mBackground;
+    private float mCornerRadius;
+    private float mPaddingH;
+    private float mPaddingV;
 
     public BubbleTextView(Context context) {
         super(context);
@@ -64,6 +67,12 @@
 
         mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
         mPaint.setColor(getContext().getResources().getColor(R.color.bubble_dark_background));
+
+        final float scale = getContext().getResources().getDisplayMetrics().density;
+        mCornerRadius = CORNER_RADIUS * scale;
+        mPaddingH = PADDING_H * scale;
+        //noinspection PointlessArithmeticExpression
+        mPaddingV = PADDING_V * scale;
     }
 
     @Override
@@ -114,11 +123,11 @@
         final int left = getCompoundPaddingLeft();
         final int top = getExtendedPaddingTop();
 
-        rect.set(left + layout.getLineLeft(0) - PADDING_H,
-                top + layout.getLineTop(0) - PADDING_V,
-                Math.min(left + layout.getLineRight(0) + PADDING_H, mScrollX + mRight - mLeft),
-                top + layout.getLineBottom(0) + PADDING_V);
-        canvas.drawRoundRect(rect, CORNER_RADIUS, CORNER_RADIUS, mPaint);
+        rect.set(left + layout.getLineLeft(0) - mPaddingH,
+                top + layout.getLineTop(0) -  mPaddingV,
+                Math.min(left + layout.getLineRight(0) + mPaddingH, mScrollX + mRight - mLeft),
+                top + layout.getLineBottom(0) + mPaddingV);
+        canvas.drawRoundRect(rect, mCornerRadius, mCornerRadius, mPaint);
 
         super.draw(canvas);
     }
diff --git a/src/com/android/launcher/CellLayout.java b/src/com/android/launcher/CellLayout.java
index 02646bf..91f0420 100644
--- a/src/com/android/launcher/CellLayout.java
+++ b/src/com/android/launcher/CellLayout.java
@@ -56,6 +56,8 @@
 
     private RectF mDragRect = new RectF();
 
+    private boolean mDirtyTag;
+
     public CellLayout(Context context) {
         this(context, null);
     }
@@ -157,6 +159,7 @@
                         cellInfo.spanY = lp.cellVSpan;
                         cellInfo.valid = true;
                         found = true;
+                        mDirtyTag = false;
                         break;
                     }
                 }
@@ -181,10 +184,12 @@
                 cellInfo.valid = cellXY[0] >= 0 && cellXY[1] >= 0 && cellXY[0] < xCount &&
                         cellXY[1] < yCount && !occupied[cellXY[0]][cellXY[1]];
 
-                if (cellInfo.valid) {
-                    findIntersectingVacantCells(cellInfo, cellXY[0], cellXY[1],
-                            xCount, yCount, occupied);
-                }
+                // 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);
         } else if (action == MotionEvent.ACTION_UP) {
@@ -194,12 +199,31 @@
             cellInfo.spanX = 0;
             cellInfo.spanY = 0;
             cellInfo.valid = false;
+            mDirtyTag = false;
             setTag(cellInfo);
         }
 
         return false;
     }
 
+    @Override
+    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 boolean[][] occupied = mOccupied;
+            findOccupiedCells(xCount, yCount, occupied);
+
+            findIntersectingVacantCells(info, info.cellX, info.cellY, xCount, yCount, occupied);
+
+            mDirtyTag = false;
+        }
+        return info;
+    }
+
     private static void findIntersectingVacantCells(CellInfo cellInfo, int x, int y,
             int xCount, int yCount, boolean[][] occupied) {
 
@@ -207,14 +231,15 @@
         cellInfo.maxVacantSpanXSpanY = Integer.MIN_VALUE;
         cellInfo.maxVacantSpanY = Integer.MIN_VALUE;
         cellInfo.maxVacantSpanYSpanX = Integer.MIN_VALUE;
-        cellInfo.vacantCells = new ArrayList<CellInfo.VacantCell>();
+        cellInfo.clearVacantCells();
 
         if (occupied[x][y]) {
             return;
         }
 
-        Rect current = new Rect(x, y, x, y);
-        findVacantCell(current, xCount, yCount, occupied, cellInfo);
+        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,
@@ -256,7 +281,7 @@
     }
 
     private static void addVacantCell(Rect current, CellInfo cellInfo) {
-        CellInfo.VacantCell cell = new CellInfo.VacantCell();
+        CellInfo.VacantCell cell = CellInfo.VacantCell.acquire();
         cell.cellX = current.left;
         cell.cellY = current.top;
         cell.spanX = current.right - current.left + 1;
@@ -317,10 +342,9 @@
         cellInfo.maxVacantSpanXSpanY = Integer.MIN_VALUE;
         cellInfo.maxVacantSpanY = Integer.MIN_VALUE;
         cellInfo.maxVacantSpanYSpanX = Integer.MIN_VALUE;
-        cellInfo.vacantCells = new ArrayList<CellInfo.VacantCell>();
         cellInfo.screen = mCellInfo.screen;
 
-        Rect current = new Rect();
+        Rect current = cellInfo.current;
 
         for (int x = 0; x < xCount; x++) {
             for (int y = 0; y < yCount; y++) {
@@ -333,16 +357,10 @@
         }
 
         cellInfo.valid = cellInfo.vacantCells.size() > 0;
-        if (cellInfo.valid) {
-            int[] xy = new int[2];
-            if (cellInfo.findCellForSpan(xy, 1, 1)) {
-                cellInfo.cellX = xy[0];
-                cellInfo.cellY = xy[1];
-                cellInfo.spanY = 1;
-                cellInfo.spanX = 1;
-            }
-        }
 
+        // Assume the caller will perform their own cell searching, otherwise we
+        // risk causing an unnecessary rebuild after findCellForSpan()
+        
         return cellInfo;
     }
 
@@ -634,6 +652,26 @@
         
         dragRect.set(x, y, x + width, y + height);
     }
+    
+    /**
+     * Computes the required horizontal and vertical cell spans to always 
+     * fit the given rectangle.
+     *  
+     * @param width Width in pixels
+     * @param height Height in pixels
+     */
+    public int[] rectToCell(int width, int height) {
+        // Always assume we're working with the smallest span to make sure we
+        // reserve enough space in both orientations.
+        int actualWidth = mCellWidth + mWidthGap;
+        int actualHeight = mCellHeight + mHeightGap;
+        int smallerSize = Math.min(actualWidth, actualHeight);
+        
+        // Always round up to next largest cell
+        int spanX = (width + smallerSize) / smallerSize;
+        int spanY = (height + smallerSize) / smallerSize;
+        return new int[] { spanX, spanY };
+    }
 
     /**
      * Find the first vacant cell, if there is one.
@@ -811,12 +849,54 @@
     }
 
     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 +
@@ -832,17 +912,27 @@
         int screen;
         boolean valid;
 
-        ArrayList<VacantCell> vacantCells;
+        final ArrayList<VacantCell> vacantCells = new ArrayList<VacantCell>(VacantCell.POOL_LIMIT);
         int maxVacantSpanX;
         int maxVacantSpanXSpanY;
         int maxVacantSpanY;
         int maxVacantSpanYSpanX;
+        final Rect current = new Rect();
+
+        private 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 findVacantCellsFromOccupied(boolean[] occupied, int xCount, int yCount) {
             if (cellX < 0 || cellY < 0) {
                 maxVacantSpanX = maxVacantSpanXSpanY = Integer.MIN_VALUE;
                 maxVacantSpanY = maxVacantSpanYSpanX = Integer.MIN_VALUE;
-                vacantCells = new ArrayList<VacantCell>();
+                clearVacantCells();
                 return;
             }
 
@@ -855,26 +945,40 @@
             CellLayout.findIntersectingVacantCells(this, cellX, cellY, xCount, yCount, unflattened);
         }
 
+        /**
+         * 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.
+         *
+         * @param cellXY The array that will contain the position of a vacant cell if such a cell
+         *               can be found.
+         * @param spanX The horizontal span of the cell we want to find.
+         * @param spanY The vertical span of the cell we want to find.
+         *
+         * @return True if a vacant cell of the specified dimension was found, false otherwise.
+         */
         boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
-            if (vacantCells == null) {
-                return false;
-            }
+            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;
-                return true;
+                found = true;
             }
 
-            final ArrayList<VacantCell> list = vacantCells;
-            final int count = list.size();
             // 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;
-                    return true;
+                    found = true;
+                    break;
                 }
             }
 
@@ -884,11 +988,14 @@
                 if (cell.spanX >= spanX && cell.spanY >= spanY) {
                     cellXY[0] = cell.cellX;
                     cellXY[1] = cell.cellY;
-                    return true;
+                    found = true;
+                    break;
                 }
             }
 
-            return false;
+            clearVacantCells();
+
+            return found;
         }
 
         @Override
diff --git a/src/com/android/launcher/DeleteZone.java b/src/com/android/launcher/DeleteZone.java
index 798cf0d..7f92c23 100644
--- a/src/com/android/launcher/DeleteZone.java
+++ b/src/com/android/launcher/DeleteZone.java
@@ -85,7 +85,11 @@
 
         final LauncherModel model = Launcher.getModel();
         if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-            model.removeDesktopItem(item);
+            if (item instanceof LauncherAppWidgetInfo) {
+                model.removeDesktopAppWidget((LauncherAppWidgetInfo) item);
+            } else {
+                model.removeDesktopItem(item);
+            }
         } else {
             if (source instanceof UserFolder) {
                 final UserFolder userFolder = (UserFolder) source;
@@ -97,6 +101,12 @@
             final UserFolderInfo userFolderInfo = (UserFolderInfo)item;
             LauncherModel.deleteUserFolderContentsFromDatabase(mLauncher, userFolderInfo);
             model.removeUserFolder(userFolderInfo);
+        } else if (item instanceof LauncherAppWidgetInfo) {
+            final LauncherAppWidgetInfo launcherAppWidgetInfo = (LauncherAppWidgetInfo) item;
+            final LauncherAppWidgetHost appWidgetHost = mLauncher.getAppWidgetHost();
+            if (appWidgetHost != null) {
+                appWidgetHost.deleteAppWidgetId(launcherAppWidgetInfo.appWidgetId);
+            }
         }
         LauncherModel.deleteItemFromDatabase(mLauncher, item);
     }
diff --git a/src/com/android/launcher/DragLayer.java b/src/com/android/launcher/DragLayer.java
index 56140dd..b542de6 100644
--- a/src/com/android/launcher/DragLayer.java
+++ b/src/com/android/launcher/DragLayer.java
@@ -32,6 +32,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.KeyEvent;
+import android.view.inputmethod.InputMethodManager;
 import android.widget.FrameLayout;
 
 /**
@@ -127,6 +128,8 @@
     private int mAnimationType;
     private int mAnimationState = ANIMATION_STATE_DONE;
 
+    private InputMethodManager mInputMethodManager;
+
     /**
      * Used to create a new DragLayer from XML.
      *
@@ -144,7 +147,14 @@
         if (PROFILE_DRAWING_DURING_DRAG) {
             android.os.Debug.startMethodTracing("Launcher");
         }
-        
+
+        // Hide soft keyboard, if visible
+        if (mInputMethodManager == null) {
+            mInputMethodManager = (InputMethodManager)
+                getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+        }
+        mInputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
+
         if (mListener != null) {
             mListener.onDragStart(v, source, dragInfo, dragAction);
         }
@@ -320,40 +330,33 @@
 
             break;
         case MotionEvent.ACTION_MOVE:
-            if (Launcher.sOpenGlEnabled) {
-                mLastMotionX = x;
-                mLastMotionY = y;
+            final int scrollX = mScrollX;
+            final int scrollY = mScrollY;
 
-                invalidate();
-            } else {
-                final int scrollX = mScrollX;
-                final int scrollY = mScrollY;
+            final float touchX = mTouchOffsetX;
+            final float touchY = mTouchOffsetY;
 
-                final float touchX = mTouchOffsetX;
-                final float touchY = mTouchOffsetY;
+            final int offsetX = mBitmapOffsetX;
+            final int offsetY = mBitmapOffsetY;
 
-                final int offsetX = mBitmapOffsetX;
-                final int offsetY = mBitmapOffsetY;
+            int left = (int) (scrollX + mLastMotionX - touchX - offsetX);
+            int top = (int) (scrollY + mLastMotionY - touchY - offsetY);
 
-                int left = (int) (scrollX + mLastMotionX - touchX - offsetX);
-                int top = (int) (scrollY + mLastMotionY - touchY - offsetY);
+            final Bitmap dragBitmap = mDragBitmap;
+            final int width = dragBitmap.getWidth();
+            final int height = dragBitmap.getHeight();
 
-                final Bitmap dragBitmap = mDragBitmap;
-                final int width = dragBitmap.getWidth();
-                final int height = dragBitmap.getHeight();
+            final Rect rect = mRect;
+            rect.set(left - 1, top - 1, left + width + 1, top + height + 1);
 
-                final Rect rect = mRect;
-                rect.set(left - 1, top - 1, left + width + 1, top + height + 1);
+            mLastMotionX = x;
+            mLastMotionY = y;
 
-                mLastMotionX = x;
-                mLastMotionY = y;
+            left = (int) (scrollX + x - touchX - offsetX);
+            top = (int) (scrollY + y - touchY - offsetY);
 
-                left = (int) (scrollX + x - touchX - offsetX);
-                top = (int) (scrollY + y - touchY - offsetY);
-
-                rect.union(left - 1, top - 1, left + width + 1, top + height + 1);
-                invalidate(rect);
-            }
+            rect.union(left - 1, top - 1, left + width + 1, top + height + 1);
+            invalidate(rect);
 
             final int[] coordinates = mDropCoordinates;
             DropTarget dropTarget = findDropTarget((int) x, (int) y, coordinates);
diff --git a/src/com/android/launcher/ItemInfo.java b/src/com/android/launcher/ItemInfo.java
index 61745dd..51449a7 100644
--- a/src/com/android/launcher/ItemInfo.java
+++ b/src/com/android/launcher/ItemInfo.java
@@ -38,10 +38,8 @@
     /**
      * One of {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION},
      * {@link LauncherSettings.Favorites#ITEM_TYPE_SHORTCUT},
-     * {@link LauncherSettings.Favorites#ITEM_TYPE_USER_FOLDER},
-     * {@link LauncherSettings.Favorites#ITEM_TYPE_WIDGET_CLOCK},
-     * {@link LauncherSettings.Favorites#ITEM_TYPE_WIDGET_SEARCH} or
-     * {@link LauncherSettings.Favorites#ITEM_TYPE_WIDGET_PHOTO_FRAME},
+     * {@link LauncherSettings.Favorites#ITEM_TYPE_USER_FOLDER}, or
+     * {@link LauncherSettings.Favorites#ITEM_TYPE_APPWIDGET}.
      */
     int itemType;
     
diff --git a/src/com/android/launcher/Launcher.java b/src/com/android/launcher/Launcher.java
index 465400b..17f16a7 100644
--- a/src/com/android/launcher/Launcher.java
+++ b/src/com/android/launcher/Launcher.java
@@ -24,17 +24,21 @@
 import android.app.StatusBarManager;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
+import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
+import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.TransitionDrawable;
@@ -64,15 +68,16 @@
 import android.view.WindowManager;
 import android.view.View.OnLongClickListener;
 import android.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView;
 import android.widget.EditText;
-import android.widget.ExpandableListView;
-import android.widget.ImageView;
+import android.widget.ListView;
 import android.widget.TextView;
 import android.widget.Toast;
 import android.widget.GridView;
+import android.widget.SlidingDrawer;
 import android.app.IWallpaperService;
-
-import com.android.internal.widget.SlidingDrawer;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -82,12 +87,12 @@
  */
 public final class Launcher extends Activity implements View.OnClickListener, OnLongClickListener {
     static final String LOG_TAG = "Launcher";
+    static final boolean LOGD = false;
 
     private static final boolean PROFILE_STARTUP = false;
+    private static final boolean PROFILE_DRAWER = false;
     private static final boolean DEBUG_USER_INTERFACE = false;
 
-    private static final boolean REMOVE_SHORTCUT_ON_PACKAGE_REMOVE = false;    
-
     private static final int WALLPAPER_SCREENS_SPAN = 2;
 
     private static final int MENU_GROUP_ADD = 1;
@@ -98,9 +103,12 @@
     private static final int MENU_SETTINGS = MENU_NOTIFICATIONS + 1;
 
     private static final int REQUEST_CREATE_SHORTCUT = 1;
-    private static final int REQUEST_CHOOSE_PHOTO = 2;
-    private static final int REQUEST_UPDATE_PHOTO = 3;
     private static final int REQUEST_CREATE_LIVE_FOLDER = 4;
+    private static final int REQUEST_CREATE_APPWIDGET = 5;
+    private static final int REQUEST_PICK_APPLICATION = 6;
+    private static final int REQUEST_PICK_SHORTCUT = 7;
+    private static final int REQUEST_PICK_LIVE_FOLDER = 8;
+    private static final int REQUEST_PICK_APPWIDGET = 9;
 
     static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate";
 
@@ -144,14 +152,10 @@
     // Type: long
     private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME_ID = "launcher.rename_folder_id";
 
-    private static LauncherModel sModel;
+    private static final LauncherModel sModel = new LauncherModel();
 
     private static Bitmap sWallpaper;
 
-    // Indicates whether the OpenGL pipeline was enabled, either through
-    // USE_OPENGL_BY_DEFAULT or the system property launcher.opengl
-    static boolean sOpenGlEnabled;
-
     private static final Object sLock = new Object();
     private static int sScreen = DEFAULT_SCREN;
 
@@ -164,7 +168,12 @@
 
     private DragLayer mDragLayer;
     private Workspace mWorkspace;
-
+    
+    private AppWidgetManager mAppWidgetManager;
+    private LauncherAppWidgetHost mAppWidgetHost;
+    
+    static final int APPWIDGET_HOST_ID = 1024;
+    
     private CellLayout.CellInfo mAddItemCellInfo;
     private CellLayout.CellInfo mMenuAddInfo;
     private final int[] mCellCoordinates = new int[2];
@@ -172,6 +181,7 @@
 
     private SlidingDrawer mDrawer;
     private TransitionDrawable mHandleIcon;
+    private HandleView mHandleView;
     private AllAppsGridView mAllAppsGrid;
 
     private boolean mDesktopLocked = true;
@@ -185,11 +195,18 @@
     private boolean mWaitingForResult;
     private boolean mLocaleChanged;
 
+    private Bundle mSavedInstanceState;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mInflater = getLayoutInflater();
-
+        
+        mAppWidgetManager = AppWidgetManager.getInstance(this);
+        
+        mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
+        mAppWidgetHost.startListening();
+        
         if (PROFILE_STARTUP) {
             android.os.Debug.startMethodTracing("/sdcard/launcher");
         }
@@ -197,10 +214,6 @@
         checkForLocaleChange();
         setWallpaperDimension();
 
-        if (sModel == null) {
-            sModel = new LauncherModel();
-        }
-
         setContentView(R.layout.launcher);
         setupViews();
 
@@ -260,8 +273,9 @@
     }
 
     private void startLoaders() {
-        sModel.loadApplications(true, this, mLocaleChanged);
-        sModel.loadUserItems(!mLocaleChanged, this, mLocaleChanged, true);
+        boolean loadApplications = sModel.loadApplications(true, this, mLocaleChanged);
+        sModel.loadUserItems(!mLocaleChanged, this, mLocaleChanged, loadApplications);
+
         mRestoring = false;
     }
 
@@ -283,20 +297,42 @@
 
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        // The pattern used here is that a user PICKs a specific application,
+        // which, depending on the target, might need to CREATE the actual target.
+        
+        // For example, the user would PICK_SHORTCUT for "Music playlist", and we
+        // launch over to the Music app to actually CREATE_SHORTCUT.
+        
         if (resultCode == RESULT_OK && mAddItemCellInfo != null) {
             switch (requestCode) {
+                case REQUEST_PICK_APPLICATION:
+                    completeAddApplication(this, data, mAddItemCellInfo, !mDesktopLocked);
+                    break;
+                case REQUEST_PICK_SHORTCUT:
+                    addShortcut(data);
+                    break;
                 case REQUEST_CREATE_SHORTCUT:
                     completeAddShortcut(data, mAddItemCellInfo, !mDesktopLocked);
                     break;
-                case REQUEST_CHOOSE_PHOTO:
-                    completeAddPhotoFrame(data, mAddItemCellInfo);
-                    break;
-                case REQUEST_UPDATE_PHOTO:
-                    completeUpdatePhotoFrame(data, mAddItemCellInfo);
+                case REQUEST_PICK_LIVE_FOLDER:
+                    addLiveFolder(data);
                     break;
                 case REQUEST_CREATE_LIVE_FOLDER:
                     completeAddLiveFolder(data, mAddItemCellInfo, !mDesktopLocked);
                     break;
+                case REQUEST_PICK_APPWIDGET:
+                    addAppWidget(data);
+                    break;
+                case REQUEST_CREATE_APPWIDGET:
+                    completeAddAppWidget(data, mAddItemCellInfo, !mDesktopLocked);
+                    break;
+            }
+        } else if (requestCode == REQUEST_PICK_APPWIDGET &&
+                resultCode == RESULT_CANCELED && data != null) {
+            // Clean up the appWidgetId if we canceled
+            int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
+            if (appWidgetId != -1) {
+                mAppWidgetHost.deleteAppWidgetId(appWidgetId);
             }
         }
         mWaitingForResult = false;
@@ -312,19 +348,21 @@
     }
 
     @Override
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
-        boolean handled = super.onKeyUp(keyCode, event);
-        if (keyCode == KeyEvent.KEYCODE_SEARCH) {
-            handled = mWorkspace.snapToSearch();
-            if (handled) closeDrawer(true);
-        }
-        return handled;
+    protected void onPause() {
+        super.onPause();
+        closeDrawer(false);        
+    }
+
+    private boolean acceptFilter() {
+        final InputMethodManager inputManager = (InputMethodManager)
+                getSystemService(Context.INPUT_METHOD_SERVICE);
+        return !inputManager.isFullscreenMode();
     }
 
     @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         boolean handled = super.onKeyDown(keyCode, event);
-        if (!handled && keyCode != KeyEvent.KEYCODE_ENTER) {
+        if (!handled && acceptFilter() && keyCode != KeyEvent.KEYCODE_ENTER) {
             boolean gotKey = TextKeyListener.getInstance().onKeyDown(mWorkspace, mDefaultKeySsb,
                     keyCode, event);
             if (gotKey && mDefaultKeySsb != null && mDefaultKeySsb.length() > 0) {
@@ -398,7 +436,6 @@
             mRestoring = true;
         }
 
-
         boolean renameFolder = savedState.getBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, false);
         if (renameFolder) {
             long id = savedState.getLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID);
@@ -425,9 +462,9 @@
 
         final DeleteZone deleteZone = (DeleteZone) dragLayer.findViewById(R.id.delete_zone);
 
-        final HandleView handleIcon = (HandleView) drawer.findViewById(R.id.all_apps);
-        handleIcon.setLauncher(this);
-        mHandleIcon = (TransitionDrawable) handleIcon.getDrawable();
+        mHandleView = (HandleView) drawer.findViewById(R.id.all_apps);
+        mHandleView.setLauncher(this);
+        mHandleIcon = (TransitionDrawable) mHandleView.getDrawable();
         mHandleIcon.setCrossFadeEnabled(true);
 
         drawer.lock();
@@ -439,10 +476,6 @@
         grid.setTextFilterEnabled(true);
         grid.setDragger(dragLayer);
         grid.setLauncher(this);
-        if (sOpenGlEnabled) {
-            grid.setScrollingCacheEnabled(false);
-            grid.setFadingEdgeLength(0);
-        }
 
         workspace.setOnLongClickListener(this);
         workspace.setDragger(dragLayer);
@@ -451,23 +484,11 @@
 
         deleteZone.setLauncher(this);
         deleteZone.setDragController(dragLayer);
-        deleteZone.setHandle(handleIcon);
+        deleteZone.setHandle(mHandleView);
 
         dragLayer.setIgnoredDropTarget(grid);
         dragLayer.setDragScoller(workspace);
         dragLayer.setDragListener(deleteZone);
-
-        if (DEBUG_USER_INTERFACE) {
-            android.widget.Button finishButton = new android.widget.Button(this);
-            finishButton.setText("Finish");
-            workspace.addInScreen(finishButton, 1, 0, 0, 1, 1);
-
-            finishButton.setOnClickListener(new android.widget.Button.OnClickListener() {
-                public void onClick(View v) {
-                    finish();
-                }
-            });
-        }
     }
 
     /**
@@ -507,11 +528,44 @@
         return favorite;
     }
 
-    void addApplicationShortcut(ApplicationInfo info) {
-        mAddItemCellInfo.screen = mWorkspace.getCurrentScreen();
-        mWorkspace.addApplicationShortcut(info, mAddItemCellInfo);
-    }
+    /**
+     * Add an application shortcut to the workspace.
+     *
+     * @param data The intent describing the application.
+     * @param cellInfo The position on screen where to create the shortcut.
+     */
+    void completeAddApplication(Context context, Intent data, CellLayout.CellInfo cellInfo,
+            boolean insertAtFirst) {
+        cellInfo.screen = mWorkspace.getCurrentScreen();
+        if (!findSingleSlot(cellInfo)) return;
 
+        // Find details for this application
+        ComponentName component = data.getComponent();
+        PackageManager packageManager = context.getPackageManager();
+        ActivityInfo activityInfo = null;
+        try {
+            activityInfo = packageManager.getActivityInfo(component, 0 /* no flags */);
+        } catch (NameNotFoundException e) {
+            Log.e(LOG_TAG, "Couldn't find ActivityInfo for selected application", e);
+        }
+        
+        if (activityInfo != null) {
+            ApplicationInfo itemInfo = new ApplicationInfo();
+            
+            itemInfo.title = activityInfo.loadLabel(packageManager);
+            if (itemInfo.title == null) {
+                itemInfo.title = activityInfo.name;
+            }
+            
+            itemInfo.setActivity(component, Intent.FLAG_ACTIVITY_NEW_TASK |
+                    Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+            itemInfo.icon = activityInfo.loadIcon(packageManager);
+            itemInfo.container = ItemInfo.NO_ID;
+
+            mWorkspace.addApplicationShortcut(itemInfo, cellInfo, insertAtFirst);
+        }
+    }
+    
     /**
      * Add a shortcut to the workspace.
      *
@@ -521,8 +575,9 @@
      */
     private void completeAddShortcut(Intent data, CellLayout.CellInfo cellInfo,
             boolean insertAtFirst) {
-
         cellInfo.screen = mWorkspace.getCurrentScreen();
+        if (!findSingleSlot(cellInfo)) return;
+        
         final ApplicationInfo info = addShortcut(this, data, cellInfo, false);
 
         if (!mRestoring) {
@@ -535,6 +590,60 @@
         }
     }
 
+    
+    /**
+     * Add a widget to the workspace.
+     *
+     * @param data The intent describing the appWidgetId.
+     * @param cellInfo The position on screen where to create the widget.
+     */
+    private void completeAddAppWidget(Intent data, CellLayout.CellInfo cellInfo,
+            boolean insertAtFirst) {
+
+        Bundle extras = data.getExtras();
+        int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
+        
+        Log.d(LOG_TAG, "dumping extras content="+extras.toString());
+        
+        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);
+        
+        // Try finding open space on Launcher screen
+        final int[] xy = mCellCoordinates;
+        if (!findSlot(cellInfo, xy, spans[0], spans[1])) return;
+
+        // Build Launcher-specific widget info and save to database
+        LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId);
+        launcherInfo.spanX = spans[0];
+        launcherInfo.spanY = spans[1];
+        
+        LauncherModel.addItemToDatabase(this, launcherInfo,
+                LauncherSettings.Favorites.CONTAINER_DESKTOP,
+                mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
+
+        if (!mRestoring) {
+            sModel.addDesktopAppWidget(launcherInfo);
+            
+            // Perform actual inflation because we're live
+            launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
+            
+            launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo);
+            launcherInfo.hostView.setTag(launcherInfo);
+            
+            mWorkspace.addInCurrentScreen(launcherInfo.hostView, xy[0], xy[1],
+                    launcherInfo.spanX, launcherInfo.spanY, insertAtFirst);
+        } else if (sModel.isDesktopLoaded()) {
+            sModel.addDesktopAppWidget(launcherInfo);
+        }
+    }
+    
+    public LauncherAppWidgetHost getAppWidgetHost() {
+        return mAppWidgetHost;
+    }
+    
     static ApplicationInfo addShortcut(Context context, Intent data,
             CellLayout.CellInfo cellInfo, boolean notify) {
 
@@ -584,107 +693,6 @@
         return info;
     }
 
-    /**
-     * Add a PhotFrame to the workspace.
-     *
-     * @param data The intent describing the photo.
-     * @param cellInfo The position on screen where to create the shortcut.
-     */
-    private void completeAddPhotoFrame(Intent data, CellLayout.CellInfo cellInfo) {
-        final Bundle extras = data.getExtras();
-        if (extras != null) {
-            Bitmap photo = extras.getParcelable("data");
-
-            Widget info = Widget.makePhotoFrame();
-            info.photo = photo;
-
-            final int[] xy = mCellCoordinates;
-            if (!findSlot(cellInfo, xy, info.spanX, info.spanY)) return;
-
-            LauncherModel.addItemToDatabase(this, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
-                    mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
-
-            if (!mRestoring) {
-                sModel.addDesktopItem(info);
-
-                final PhotoFrame view = (PhotoFrame) mInflater.inflate(info.layoutResource, null);
-                view.setImageBitmap(photo);
-                view.setTag(info);
-
-                mWorkspace.addInCurrentScreen(view, xy[0], xy[1], info.spanX, info.spanY);
-            } else if (sModel.isDesktopLoaded()) {
-                sModel.addDesktopItem(info);
-            }
-        }
-    }
-
-    /**
-     * Updates a workspace PhotoFrame.
-     *
-     * @param data The intent describing the photo.
-     * @param cellInfo The position on screen of the PhotoFrame to update.
-     */
-    private void completeUpdatePhotoFrame(Intent data, CellLayout.CellInfo cellInfo) {
-        final Bundle extras = data.getExtras();
-        if (extras != null) {
-            Widget info;
-            Bitmap photo = extras.getParcelable("data");
-
-            if (!mRestoring) {
-                final CellLayout layout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen);
-                final PhotoFrame view = (PhotoFrame) layout.findCell(cellInfo.cellX, cellInfo.cellY,
-                        cellInfo.spanX, cellInfo.spanY, null);
-                view.setImageBitmap(photo);
-                info = (Widget) view.getTag();
-            } else {
-                info = LauncherModel.getPhotoFrameInfo(this, cellInfo.screen,
-                        cellInfo.cellX, cellInfo.cellY);
-            }
-
-            info.photo = photo;
-            LauncherModel.updateItemInDatabase(this, info);
-        }
-    }
-
-    /**
-     * Starts a new Intent to let the user update the PhotoFrame defined by the
-     * specified Widget.
-     *
-     * @param widget The Widget info defining which PhotoFrame to update.
-     */
-    void updatePhotoFrame(Widget widget) {
-        CellLayout.CellInfo info = new CellLayout.CellInfo();
-        info.screen = widget.screen;
-        info.cellX = widget.cellX;
-        info.cellY = widget.cellY;
-        info.spanX = widget.spanX;
-        info.spanY = widget.spanY;
-        mAddItemCellInfo = info;
-
-        startActivityForResult(createPhotoPickIntent(), Launcher.REQUEST_UPDATE_PHOTO);
-    }
-
-    /**
-     * Creates an Intent used to let the user pick a photo for a PhotoFrame.
-     *
-     * @return The Intent to pick a photo suited for a PhotoFrame.
-     */
-    private static Intent createPhotoPickIntent() {
-        // TODO: Move this method to PhotoFrame?
-        // TODO: get these values from constants somewhere
-        // TODO: Adjust the PhotoFrame's image size to avoid on the fly scaling
-        Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
-        intent.setType("image/*");
-        intent.putExtra("crop", "true");
-        intent.putExtra("aspectX", 1);
-        intent.putExtra("aspectY", 1);
-        intent.putExtra("outputX", 192);
-        intent.putExtra("outputY", 192);
-        intent.putExtra("noFaceDetection", true);
-        intent.putExtra("return-data", true);
-        return intent;
-    }
-
     @Override
     protected void onNewIntent(Intent intent) {
         super.onNewIntent(intent);
@@ -730,6 +738,12 @@
     }
 
     @Override
+    protected void onRestoreInstanceState(Bundle savedInstanceState) {
+        // Do not call super here
+        mSavedInstanceState = savedInstanceState;
+    }
+
+    @Override
     protected void onSaveInstanceState(Bundle outState) {
         outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getCurrentScreen());
 
@@ -776,6 +790,12 @@
         mDestroyed = true;
 
         super.onDestroy();
+        
+        try {
+            mAppWidgetHost.stopListening();
+        } catch (NullPointerException ex) {
+            Log.w(LOG_TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex);
+        }
 
         TextKeyListener.getInstance().release();
 
@@ -853,9 +873,7 @@
                 startWallpaper();
                 return true;
             case MENU_SEARCH:
-                if (!mWorkspace.snapToSearch()) {
-                    onSearchRequested();
-                }
+                onSearchRequested();
                 return true;
             case MENU_NOTIFICATIONS:
                 showNotifications();
@@ -865,16 +883,63 @@
         return super.onOptionsItemSelected(item);
     }
 
+    @Override
+    public boolean onSearchRequested() {
+        if (mWorkspace.snapToSearch()) {
+            closeDrawer(true);                // search widget: get drawer out of the way
+            return true;
+        } else {
+            return super.onSearchRequested(); // no search widget: use system search UI
+        }
+    }
+
     private void addItems() {
         showAddDialog(mMenuAddInfo);
     }
 
     private void removeShortcutsForPackage(String packageName) {
         if (packageName != null && packageName.length() > 0) {
-            android.util.Log.d(LOG_TAG, packageName);
             mWorkspace.removeShortcutsForPackage(packageName);
         }
     }
+    
+    void addAppWidget(Intent data) {
+        // TODO: catch bad widget exception when sent
+        int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
+        AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
+
+        if (appWidget.configure != null) {
+            // Launch over to configure widget, if needed
+            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
+            intent.setComponent(appWidget.configure);
+            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+
+            startActivityForResult(intent, REQUEST_CREATE_APPWIDGET);
+        } else {
+            // Otherwise just add it
+            onActivityResult(REQUEST_CREATE_APPWIDGET, Activity.RESULT_OK, data);
+        }
+    }
+    
+    void addSearch() {
+        final Widget info = Widget.makeSearch();
+        final CellLayout.CellInfo cellInfo = mAddItemCellInfo;
+        
+        final int[] xy = mCellCoordinates;
+        final int spanX = info.spanX;
+        final int spanY = info.spanY;
+    
+        if (!findSlot(cellInfo, xy, spanX, spanY)) return;
+    
+        sModel.addDesktopItem(info);
+        LauncherModel.addItemToDatabase(this, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
+        mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
+    
+        final View view = mInflater.inflate(info.layoutResource, null);
+        view.setTag(info);
+    
+        mWorkspace.addInCurrentScreen(view, xy[0], xy[1], info.spanX, spanY);
+    }
 
     void addShortcut(Intent intent) {
         startActivityForResult(intent, REQUEST_CREATE_SHORTCUT);
@@ -884,28 +949,32 @@
         startActivityForResult(intent, REQUEST_CREATE_LIVE_FOLDER);
     }
 
-    void addFolder() {
+    void addFolder(boolean insertAtFirst) {
         UserFolderInfo folderInfo = new UserFolderInfo();
         folderInfo.title = getText(R.string.folder_name);
-        int cellX = mAddItemCellInfo.cellX;
-        int cellY = mAddItemCellInfo.cellY;
+
+        CellLayout.CellInfo cellInfo = mAddItemCellInfo;
+        cellInfo.screen = mWorkspace.getCurrentScreen();
+        if (!findSingleSlot(cellInfo)) return;
 
         // Update the model
         LauncherModel.addItemToDatabase(this, folderInfo, LauncherSettings.Favorites.CONTAINER_DESKTOP,
-                mWorkspace.getCurrentScreen(), cellX, cellY, false);
+                mWorkspace.getCurrentScreen(), cellInfo.cellX, cellInfo.cellY, false);
         sModel.addDesktopItem(folderInfo);
         sModel.addFolder(folderInfo);
 
         // Create the view
         FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
                 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), folderInfo);
-        mWorkspace.addInCurrentScreen(newFolder, cellX, cellY, 1, 1);
+        mWorkspace.addInCurrentScreen(newFolder,
+                cellInfo.cellX, cellInfo.cellY, 1, 1, insertAtFirst);
     }
-
+    
     private void completeAddLiveFolder(Intent data, CellLayout.CellInfo cellInfo,
             boolean insertAtFirst) {
-
         cellInfo.screen = mWorkspace.getCurrentScreen();
+        if (!findSingleSlot(cellInfo)) return;
+
         final LiveFolderInfo info = addLiveFolder(this, data, cellInfo, false);
 
         if (!mRestoring) {
@@ -964,37 +1033,14 @@
         return info;
     }
 
-    void getPhotoForPhotoFrame() {
-        startActivityForResult(createPhotoPickIntent(), REQUEST_CHOOSE_PHOTO);
-    }
-
-    void addClock() {
-        final Widget info = Widget.makeClock();
-        addWidget(info);
-    }
-
-    void addSearch() {
-        final Widget info = Widget.makeSearch();
-        addWidget(info);
-    }
-    
-    private void addWidget(final Widget info) {
-        final CellLayout.CellInfo cellInfo = mAddItemCellInfo;
-
-        final int[] xy = mCellCoordinates;
-        final int spanX = info.spanX;
-        final int spanY = info.spanY;
-
-        if (!findSlot(cellInfo, xy, spanX, spanY)) return;
-
-        sModel.addDesktopItem(info);
-        LauncherModel.addItemToDatabase(this, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
-                mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
-
-        final View view = mInflater.inflate(info.layoutResource, null);
-        view.setTag(info);
-
-        mWorkspace.addInCurrentScreen(view, xy[0], xy[1], info.spanX, spanY);
+    private boolean findSingleSlot(CellLayout.CellInfo cellInfo) {
+        final int[] xy = new int[2];
+        if (findSlot(cellInfo, xy, 1, 1)) {
+            cellInfo.cellX = xy[0];
+            cellInfo.cellY = xy[1];
+            return true;
+        }
+        return false;
     }
 
     private boolean findSlot(CellLayout.CellInfo cellInfo, int[] xy, int spanX, int spanY) {
@@ -1117,9 +1163,9 @@
 
     void onDesktopItemsLoaded() {
         if (mDestroyed) return;
-
+android.util.Log.d("Home", "setting grid adapter");
+        mAllAppsGrid.setAdapter(sModel.getApplicationsAdapter());
         bindDesktopItems();
-        mAllAppsGrid.setAdapter(Launcher.getModel().getApplicationsAdapter());
     }
 
     /**
@@ -1127,7 +1173,8 @@
      */
     private void bindDesktopItems() {
         final ArrayList<ItemInfo> shortcuts = sModel.getDesktopItems();
-        if (shortcuts == null) {
+        final ArrayList<LauncherAppWidgetInfo> appWidgets = sModel.getDesktopAppWidgets();
+        if (shortcuts == null || appWidgets == null) {
             return;
         }
 
@@ -1136,20 +1183,30 @@
         for (int i = 0; i < count; i++) {
             ((ViewGroup) workspace.getChildAt(i)).removeAllViewsInLayout();
         }
+        
+        if (DEBUG_USER_INTERFACE) {
+            android.widget.Button finishButton = new android.widget.Button(this);
+            finishButton.setText("Finish");
+            workspace.addInScreen(finishButton, 1, 0, 0, 1, 1);
 
-        count = shortcuts.size();
+            finishButton.setOnClickListener(new android.widget.Button.OnClickListener() {
+                public void onClick(View v) {
+                    finish();
+                }
+            });
+        }
 
-        final DesktopItemsBinder binder = new DesktopItemsBinder(this, shortcuts);
-        binder.obtainMessage(DesktopItemsBinder.MESSAGE_BIND_ITEMS, 0, count).sendToTarget();
+        final DesktopBinder binder = new DesktopBinder(this, shortcuts, appWidgets);
+        binder.startBindingItems();
     }
 
-    private void bindItems(Launcher.DesktopItemsBinder binder,
+    private void bindItems(Launcher.DesktopBinder binder,
             ArrayList<ItemInfo> shortcuts, int start, int count) {
 
         final Workspace workspace = mWorkspace;
         final boolean desktopLocked = mDesktopLocked;
 
-        final int end = Math.min(start + DesktopItemsBinder.ITEMS_COUNT, count);
+        final int end = Math.min(start + DesktopBinder.ITEMS_COUNT, count);
         int i = start;
 
         for ( ; i < end; i++) {
@@ -1176,11 +1233,16 @@
                     workspace.addInScreen(newLiveFolder, item.screen, item.cellX, item.cellY, 1, 1,
                             !desktopLocked);
                     break;
-                default:
+                case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH:
+                    final int screen = workspace.getCurrentScreen();
+                    final View view = mInflater.inflate(R.layout.widget_search,
+                            (ViewGroup) workspace.getChildAt(screen), false);
+                    
                     final Widget widget = (Widget) item;
-                    final View view = createWidget(mInflater, widget);
                     view.setTag(widget);
+                    
                     workspace.addWidget(view, widget, !desktopLocked);
+                    break;
             }
         }
 
@@ -1188,14 +1250,17 @@
 
         if (end >= count) {
             finishBindDesktopItems();
+            binder.startBindingAppWidgets();
         } else {
-            binder.obtainMessage(DesktopItemsBinder.MESSAGE_BIND_ITEMS, i, count).sendToTarget();
+            binder.obtainMessage(DesktopBinder.MESSAGE_BIND_ITEMS, i, count).sendToTarget();
         }
     }
 
     private void finishBindDesktopItems() {
         if (mSavedState != null) {
-            mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
+            if (!mWorkspace.hasFocus()) {
+                mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
+            }
 
             final long[] userFolders = mSavedState.getLongArray(RUNTIME_STATE_USER_FOLDERS);
             if (userFolders != null) {
@@ -1208,35 +1273,67 @@
                 final Folder openFolder = mWorkspace.getOpenFolder();
                 if (openFolder != null) {
                     openFolder.requestFocus();
-                } else {
-                    mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
                 }
             }
 
             final boolean allApps = mSavedState.getBoolean(RUNTIME_STATE_ALL_APPS_FOLDER, false);
             if (allApps) {
                 mDrawer.open();
-                mDrawer.requestFocus();
             }
 
             mSavedState = null;
         }
 
+        if (mSavedInstanceState != null) {
+            super.onRestoreInstanceState(mSavedInstanceState);
+            mSavedInstanceState = null;
+        }
+
+        if (mDrawer.isOpened() && !mDrawer.hasFocus()) {
+            mDrawer.requestFocus();
+        }
+
         mDesktopLocked = false;
         mDrawer.unlock();
     }
 
-    private View createWidget(LayoutInflater inflater, Widget widget) {
-        final Workspace workspace = mWorkspace;
-        final int screen = workspace.getCurrentScreen();
-        View v = inflater.inflate(widget.layoutResource,
-                (ViewGroup) workspace.getChildAt(screen), false);
-        if (widget.itemType == LauncherSettings.Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME) {
-            ((ImageView)v).setImageBitmap(widget.photo);
-        }
-        return v;
-    }
+    private void bindAppWidgets(Launcher.DesktopBinder binder,
+            ArrayList<LauncherAppWidgetInfo> appWidgets, int start, int count) {
 
+        final Workspace workspace = mWorkspace;
+        final boolean desktopLocked = mDesktopLocked;
+
+        final int end = Math.min(start + DesktopBinder.APPWIDGETS_COUNT, count);
+        int i = start;
+
+        for ( ; i < end; i++) {
+            final LauncherAppWidgetInfo item = appWidgets.get(i);
+                    
+            final int appWidgetId = item.appWidgetId;
+            final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
+            item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
+            
+            if (LOGD) Log.d(LOG_TAG, String.format("about to setAppWidget for id=%d, info=%s", appWidgetId, appWidgetInfo));
+            
+            item.hostView.setAppWidget(appWidgetId, appWidgetInfo);
+            item.hostView.setTag(item);
+            
+            workspace.addInScreen(item.hostView, item.screen, item.cellX,
+                    item.cellY, item.spanX, item.spanY, !desktopLocked);
+        }
+
+        workspace.requestLayout();
+
+        if (end >= count) {
+            finishBindDesktopAppWidgets();
+        } else {
+            binder.obtainMessage(DesktopBinder.MESSAGE_BIND_APPWIDGETS, i, count).sendToTarget();
+        }
+    }
+    
+    private void finishBindDesktopAppWidgets() {
+    }
+    
     DragController getDragController() {
         return mDragLayer;
     }
@@ -1263,6 +1360,11 @@
             startActivity(intent);
         } catch (ActivityNotFoundException e) {
             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+        } catch (SecurityException e) {
+            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+            Log.e(LOG_TAG, "Launcher does not have the permission to launch " + intent +
+                    ". Make sure to create a MAIN intent-filter for the corresponding activity " +
+                    "or use the exported attribute for this activity.", e);
         }
     }
 
@@ -1355,13 +1457,14 @@
 
         // This happens when long clicking an item with the dpad/trackball
         if (cellInfo == null) {
-            return false;
+            return true;
         }
 
         if (mWorkspace.allowLongPress()) {
             if (cellInfo.cell == null) {
                 if (cellInfo.valid) {
                     // User long pressed on empty space
+                    mWorkspace.setAllowLongPress(false);
                     showAddDialog(cellInfo);
                 }
             } else {
@@ -1382,10 +1485,22 @@
         mDrawer.close();
     }
 
+    View getDrawerHandle() {
+        return mHandleView;
+    }
+
     boolean isDrawerDown() {
         return !mDrawer.isMoving() && !mDrawer.isOpened();
     }
 
+    boolean isDrawerUp() {
+        return mDrawer.isOpened() && !mDrawer.isMoving();
+    }
+
+    boolean isDrawerMoving() {
+        return mDrawer.isMoving();
+    }
+
     Workspace getWorkspace() {
         return mWorkspace;
     }
@@ -1508,24 +1623,24 @@
      * Displays the shortcut creation dialog and launches, if necessary, the
      * appropriate activity.
      */
-    private class CreateShortcut implements ExpandableListView.OnChildClickListener,
-            DialogInterface.OnCancelListener, ExpandableListView.OnGroupExpandListener {
+    private class CreateShortcut implements AdapterView.OnItemClickListener,
+            DialogInterface.OnCancelListener {
         private AddAdapter mAdapter;
-        private ExpandableListView mList;
-
+        private ListView mList;
+        
         Dialog createDialog() {
             mWaitingForResult = true;
-            mAdapter = new AddAdapter(Launcher.this, false);
+            
+            mAdapter = new AddAdapter(Launcher.this);
             
             final AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this);
             builder.setTitle(getString(R.string.menu_item_add_item));
             builder.setIcon(0);
 
-            mList = (ExpandableListView)
+            mList = (ListView)
                     View.inflate(Launcher.this, R.layout.create_shortcut_list, null);
             mList.setAdapter(mAdapter);
-            mList.setOnChildClickListener(this);
-            mList.setOnGroupExpandListener(this);
+            mList.setOnItemClickListener(this);
             builder.setView(mList);
             builder.setInverseBackgroundForced(true);
 
@@ -1539,13 +1654,6 @@
             return dialog;
         }
 
-        public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
-                int childPosition, long id) {
-            mAdapter.performAction(groupPosition, childPosition);
-            cleanup();
-            return true;
-        }
-
         public void onCancel(DialogInterface dialog) {
             mWaitingForResult = false;
             cleanup();
@@ -1556,10 +1664,75 @@
             dismissDialog(DIALOG_CREATE_SHORTCUT);
         }
 
-        public void onGroupExpand(int groupPosition) {
-            long packged = ExpandableListView.getPackedPositionForGroup(groupPosition);
-            int position = mList.getFlatListPosition(packged);
-            mList.setSelectionFromTop(position, 0);
+        public void onItemClick(AdapterView parent, View view, int position, long id) {
+            // handle which item was clicked based on position
+            // this will launch off pick intent
+            
+            Object tag = view.getTag();
+            if (tag instanceof AddAdapter.ListItem) {
+                AddAdapter.ListItem item = (AddAdapter.ListItem) tag;
+                cleanup();
+                switch (item.actionTag) {
+                    case AddAdapter.ITEM_APPLICATION: {
+                        Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+                        mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+
+                        Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
+                        pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent);
+                        startActivityForResult(pickIntent, REQUEST_PICK_APPLICATION);
+                        break;
+                    }
+
+                    case AddAdapter.ITEM_SHORTCUT: {
+                        Intent shortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
+
+                        Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
+                        pickIntent.putExtra(Intent.EXTRA_INTENT, shortcutIntent);
+                        pickIntent.putExtra(Intent.EXTRA_TITLE,
+                                getText(R.string.title_select_shortcut));
+                        startActivityForResult(pickIntent, REQUEST_PICK_SHORTCUT);
+                        break;
+                    }
+                    
+                    case AddAdapter.ITEM_SEARCH: {
+                        addSearch();
+                        break;
+                    }
+                    
+                    case AddAdapter.ITEM_APPWIDGET: {
+                        int appWidgetId = Launcher.this.mAppWidgetHost.allocateAppWidgetId();
+                        
+                        Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
+                        pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+                        startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET);
+                        break;
+                    }
+                    
+                    case AddAdapter.ITEM_LIVE_FOLDER: {
+                        Intent liveFolderIntent = new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER);
+
+                        Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
+                        pickIntent.putExtra(Intent.EXTRA_INTENT, liveFolderIntent);
+                        pickIntent.putExtra(Intent.EXTRA_TITLE,
+                                getText(R.string.title_select_live_folder));
+                        startActivityForResult(pickIntent, REQUEST_PICK_LIVE_FOLDER);
+                        break;
+                    }
+
+                    case AddAdapter.ITEM_FOLDER: {
+                        addFolder(!mDesktopLocked);
+                        dismissDialog(DIALOG_CREATE_SHORTCUT);
+                        break;
+                    }
+
+                    case AddAdapter.ITEM_WALLPAPER: {
+                        startWallpaper();
+                        break;
+                    }
+
+                }                
+                
+            }
         }
     }
 
@@ -1569,13 +1742,24 @@
     private class ApplicationsIntentReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
-            //noinspection ConstantConditions
-            if (REMOVE_SHORTCUT_ON_PACKAGE_REMOVE &&
-                    Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
-                removeShortcutsForPackage(intent.getData().getSchemeSpecificPart());
+            boolean reloadWorkspace = false;
+android.util.Log.d("Home", "application intent received: " + intent.getAction());
+android.util.Log.d("Home", "  --> " + intent.getData());
+            if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
+                if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                    removeShortcutsForPackage(intent.getData().getSchemeSpecificPart());
+                } else {
+                    reloadWorkspace = true;
+                }
             }
             removeDialog(DIALOG_CREATE_SHORTCUT);
-            sModel.loadApplications(false, Launcher.this, false);
+            if (!reloadWorkspace) {
+android.util.Log.d("Home", "  --> loading apps");
+                sModel.loadApplications(false, Launcher.this, false);
+            } else {
+android.util.Log.d("Home", "  --> loading workspace");
+                sModel.loadUserItems(false, Launcher.this, false, true);
+            }
         }
     }
 
@@ -1637,48 +1821,93 @@
         public void onDrawerOpened() {
             if (!mOpen) {
                 mHandleIcon.reverseTransition(150);
+
+                final Rect bounds = mWorkspace.mDrawerBounds;
+                offsetBoundsToDragLayer(bounds, mAllAppsGrid);
+
                 mOpen = true;
             }
         }
 
+        private void offsetBoundsToDragLayer(Rect bounds, View view) {
+            view.getDrawingRect(bounds);
+
+            while (view != mDragLayer) {
+                bounds.offset(view.getLeft(), view.getTop());
+                view = (View) view.getParent();
+            }
+        }
+
         public void onDrawerClosed() {
             if (mOpen) {
                 mHandleIcon.reverseTransition(150);
+                mWorkspace.mDrawerBounds.setEmpty();
                 mOpen = false;
             }
+
             mAllAppsGrid.setSelection(0);
             mAllAppsGrid.clearTextFilter();
-            mWorkspace.clearChildrenCache();
         }
 
         public void onScrollStarted() {
-            mWorkspace.enableChildrenCache();
+            if (PROFILE_DRAWER) {
+                android.os.Debug.startMethodTracing("/sdcard/launcher-drawer");
+            }
+
+            mWorkspace.mDrawerContentWidth = mAllAppsGrid.getWidth();
+            mWorkspace.mDrawerContentHeight = mAllAppsGrid.getHeight();
         }
 
         public void onScrollEnded() {
+            if (PROFILE_DRAWER) {
+                android.os.Debug.stopMethodTracing();
+            }
         }
     }
 
-    private static class DesktopItemsBinder extends Handler {
+    private static class DesktopBinder extends Handler {
         static final int MESSAGE_BIND_ITEMS = 0x1;
+        static final int MESSAGE_BIND_APPWIDGETS = 0x2;
         // Number of items to bind in every pass
         static final int ITEMS_COUNT = 6;
+        static final int APPWIDGETS_COUNT = 1;
 
         private final ArrayList<ItemInfo> mShortcuts;
+        private final ArrayList<LauncherAppWidgetInfo> mAppWidgets;
         private final WeakReference<Launcher> mLauncher;
 
-        DesktopItemsBinder(Launcher launcher, ArrayList<ItemInfo> shortcuts) {
+        DesktopBinder(Launcher launcher, ArrayList<ItemInfo> shortcuts,
+                ArrayList<LauncherAppWidgetInfo> appWidgets) {
+
             mLauncher = new WeakReference<Launcher>(launcher);
             mShortcuts = shortcuts;
+            mAppWidgets = appWidgets;
+        }
+        
+        public void startBindingItems() {
+            obtainMessage(MESSAGE_BIND_ITEMS, 0, mShortcuts.size()).sendToTarget();
         }
 
+        public void startBindingAppWidgets() {
+            obtainMessage(MESSAGE_BIND_APPWIDGETS, 0, mAppWidgets.size()).sendToTarget();
+        }
+        
         @Override
         public void handleMessage(Message msg) {
+            Launcher launcher = mLauncher.get();
+            if (launcher == null) {
+                return;
+            }
+            
             switch (msg.what) {
-                case MESSAGE_BIND_ITEMS:
-                    Launcher launcher = mLauncher.get();
-                    if (launcher != null) launcher.bindItems(this, mShortcuts, msg.arg1, msg.arg2);
+                case MESSAGE_BIND_ITEMS: {
+                    launcher.bindItems(this, mShortcuts, msg.arg1, msg.arg2);
                     break;
+                }
+                case MESSAGE_BIND_APPWIDGETS: {
+                    launcher.bindAppWidgets(this, mAppWidgets, msg.arg1, msg.arg2);
+                    break;
+                }
             }
         }
     }
diff --git a/src/com/android/launcher/LauncherAppWidgetHost.java b/src/com/android/launcher/LauncherAppWidgetHost.java
new file mode 100644
index 0000000..22fd5b6
--- /dev/null
+++ b/src/com/android/launcher/LauncherAppWidgetHost.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+package com.android.launcher;
+
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.Context;
+
+/**
+ * Specific {@link AppWidgetHost} that creates our {@link LauncherAppWidgetHostView}
+ * which correctly captures all long-press events. This ensures that users can
+ * always pick up and move widgets.
+ */
+public class LauncherAppWidgetHost extends AppWidgetHost {
+    public LauncherAppWidgetHost(Context context, int hostId) {
+        super(context, hostId);
+    }
+    
+    @Override
+    protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
+            AppWidgetProviderInfo appWidget) {
+        return new LauncherAppWidgetHostView(context);
+    }
+}
diff --git a/src/com/android/launcher/LauncherAppWidgetHostView.java b/src/com/android/launcher/LauncherAppWidgetHostView.java
new file mode 100644
index 0000000..1e21a19
--- /dev/null
+++ b/src/com/android/launcher/LauncherAppWidgetHostView.java
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+package com.android.launcher;
+
+import android.appwidget.AppWidgetHostView;
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+/**
+ * {@inheritDoc}
+ */
+public class LauncherAppWidgetHostView extends AppWidgetHostView {
+    private boolean mHasPerformedLongPress;
+    
+    private CheckForLongPress mPendingCheckForLongPress;
+    
+    private LayoutInflater mInflater;
+    
+    public LauncherAppWidgetHostView(Context context) {
+        super(context);
+        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+    }
+    
+    @Override
+    protected View getErrorView() {
+        return mInflater.inflate(R.layout.appwidget_error, this, false);
+    }
+
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        // Consume any touch events for ourselves after longpress is triggered
+        if (mHasPerformedLongPress) {
+            mHasPerformedLongPress = false;
+            return true;
+        }
+            
+        // Watch for longpress events at this level to make sure
+        // users can always pick up this widget
+        switch (ev.getAction()) {
+            case MotionEvent.ACTION_DOWN: {
+                postCheckForLongClick();
+                break;
+            }
+            
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                mHasPerformedLongPress = false;
+                if (mPendingCheckForLongPress != null) {
+                    removeCallbacks(mPendingCheckForLongPress);
+                }
+                break;
+        }
+        
+        // Otherwise continue letting touch events fall through to children
+        return false;
+    }
+    
+    class CheckForLongPress implements Runnable {
+        private int mOriginalWindowAttachCount;
+
+        public void run() {
+            if ((mParent != null) && hasWindowFocus()
+                    && mOriginalWindowAttachCount == getWindowAttachCount()
+                    && !mHasPerformedLongPress) {
+                if (performLongClick()) {
+                    mHasPerformedLongPress = true;
+                }
+            }
+        }
+
+        public void rememberWindowAttachCount() {
+            mOriginalWindowAttachCount = getWindowAttachCount();
+        }
+    }
+
+    private void postCheckForLongClick() {
+        mHasPerformedLongPress = false;
+
+        if (mPendingCheckForLongPress == null) {
+            mPendingCheckForLongPress = new CheckForLongPress();
+        }
+        mPendingCheckForLongPress.rememberWindowAttachCount();
+        postDelayed(mPendingCheckForLongPress, ViewConfiguration.getLongPressTimeout());
+    }
+}
diff --git a/src/com/android/launcher/LauncherAppWidgetInfo.java b/src/com/android/launcher/LauncherAppWidgetInfo.java
new file mode 100644
index 0000000..3b5f08e
--- /dev/null
+++ b/src/com/android/launcher/LauncherAppWidgetInfo.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+package com.android.launcher;
+
+import android.appwidget.AppWidgetHostView;
+import android.content.ContentValues;
+
+/**
+ * Represents a widget, which just contains an identifier.
+ */
+class LauncherAppWidgetInfo extends ItemInfo {
+
+    /**
+     * Identifier for this widget when talking with {@link AppWidgetManager} for updates.
+     */
+    int appWidgetId;
+    
+    /**
+     * View that holds this widget after it's been created.  This view isn't created
+     * until Launcher knows it's needed.
+     */
+    AppWidgetHostView hostView = null;
+
+    LauncherAppWidgetInfo(int appWidgetId) {
+        itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
+        this.appWidgetId = appWidgetId;
+    }
+    
+    @Override
+    void onAddToDatabase(ContentValues values) {
+        super.onAddToDatabase(values);
+        values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
+    }
+
+    @Override
+    public String toString() {
+        return Integer.toString(appWidgetId);
+    }
+}
diff --git a/src/com/android/launcher/LauncherModel.java b/src/com/android/launcher/LauncherModel.java
index 314a502..70b4c10 100644
--- a/src/com/android/launcher/LauncherModel.java
+++ b/src/com/android/launcher/LauncherModel.java
@@ -33,7 +33,6 @@
 import android.os.Process;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Comparator;
@@ -44,33 +43,41 @@
 /**
  * Maintains in-memory state of the Launcher. It is expected that there should be only one
  * LauncherModel object held in a static. Also provide APIs for updating the database state
- * for the Launcher
+ * for the Launcher.
  */
 public class LauncherModel {
     private static final int UI_NOTIFICATION_RATE = 4;
     private static final int DEFAULT_APPLICATIONS_NUMBER = 42;
     private static final long APPLICATION_NOT_RESPONDING_TIMEOUT = 5000;
+    private static final int INITIAL_ICON_CACHE_CAPACITY = 50;
 
-    private final Collator sCollator = Collator.getInstance();    
+    private static final boolean DEBUG = false;
+
+    private static final Collator sCollator = Collator.getInstance();
 
     private boolean mApplicationsLoaded;
     private boolean mDesktopItemsLoaded;
 
     private ArrayList<ItemInfo> mDesktopItems;
+    private ArrayList<LauncherAppWidgetInfo> mDesktopAppWidgets;
     private HashMap<Long, FolderInfo> mFolders;
 
     private ArrayList<ApplicationInfo> mApplications;
     private ApplicationsAdapter mApplicationsAdapter;
     private ApplicationsLoader mApplicationsLoader;
     private DesktopItemsLoader mDesktopItemsLoader;
-    private Thread mLoader;
-    private Thread mDesktopLoader;
+    private Thread mApplicationsLoaderThread;
+    private Thread mDesktopLoaderThread;
 
-    void abortLoaders() {
+    private final HashMap<ComponentName, ApplicationInfo> mAppInfoCache =
+            new HashMap<ComponentName, ApplicationInfo>(INITIAL_ICON_CACHE_CAPACITY);
+
+    synchronized void abortLoaders() {
         if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
             mApplicationsLoader.stop();
             mApplicationsLoaded = false;
         }
+
         if (mDesktopItemsLoader != null && mDesktopItemsLoader.isRunning()) {
             mDesktopItemsLoader.stop();
             mDesktopItemsLoaded = false;
@@ -78,41 +85,72 @@
     }
 
     /**
-     * Loads the list of installed applications in mApplications.
+     * Drop our cache of components to their lables & icons.  We do
+     * this from Launcher when applications are added/removed.  It's a
+     * bit overkill, but it's a rare operation anyway.
      */
-    void loadApplications(boolean isLaunching, Launcher launcher, boolean localeChanged) {
+    synchronized void dropApplicationCache() {
+        mAppInfoCache.clear();
+    }
+
+    /**
+     * Loads the list of installed applications in mApplications.
+     *
+     * @return true if the applications loader must be started
+     *         (see startApplicationsLoader()), false otherwise.
+     */
+    synchronized boolean loadApplications(boolean isLaunching, Launcher launcher,
+            boolean localeChanged) {
+android.util.Log.d("Home", "load applications");
         if (isLaunching && mApplicationsLoaded && !localeChanged) {
             mApplicationsAdapter = new ApplicationsAdapter(launcher, mApplications);
-            return;
+android.util.Log.d("Home", "  --> applications loaded, return");
+            return false;
         }
 
+        waitForApplicationsLoader();
+
+        if (localeChanged) {
+            dropApplicationCache();
+        }        
+
         if (mApplicationsAdapter == null || isLaunching || localeChanged) {
-            mApplicationsAdapter = new ApplicationsAdapter(launcher,
-                    mApplications = new ArrayList<ApplicationInfo>(DEFAULT_APPLICATIONS_NUMBER));
-        }
-
-        if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
-            mApplicationsLoader.stop();
-            // Wait for the currently running thread to finish, this can take a little
-            // time but it should be well below the timeout limit
-            try {
-                mLoader.join(APPLICATION_NOT_RESPONDING_TIMEOUT);
-            } catch (InterruptedException e) {
-                // Empty
-            }
+            mApplications = new ArrayList<ApplicationInfo>(DEFAULT_APPLICATIONS_NUMBER);
+            mApplicationsAdapter = new ApplicationsAdapter(launcher, mApplications);
         }
 
         mApplicationsLoaded = false;
 
         if (!isLaunching) {
             startApplicationsLoader(launcher);
+            return false;
+        }
+
+        return true;
+    }
+
+    private synchronized void waitForApplicationsLoader() {
+        if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
+            android.util.Log.d("Home", "  --> wait for applications loader");
+
+            mApplicationsLoader.stop();
+            // Wait for the currently running thread to finish, this can take a little
+            // time but it should be well below the timeout limit
+            try {
+                mApplicationsLoaderThread.join(APPLICATION_NOT_RESPONDING_TIMEOUT);
+            } catch (InterruptedException e) {
+                // EMpty
+            }
         }
     }
 
-    private void startApplicationsLoader(Launcher launcher) {
+    private synchronized void startApplicationsLoader(Launcher launcher) {
+android.util.Log.d("Home", "  --> starting applications loader");
+        waitForApplicationsLoader();
+
         mApplicationsLoader = new ApplicationsLoader(launcher);
-        mLoader = new Thread(mApplicationsLoader, "Applications Loader");
-        mLoader.start();
+        mApplicationsLoaderThread = new Thread(mApplicationsLoader, "Applications Loader");
+        mApplicationsLoaderThread.start();
     }
 
     private class ApplicationsLoader implements Runnable {
@@ -149,36 +187,39 @@
                 final int count = apps.size();
                 final ApplicationsAdapter applicationList = mApplicationsAdapter;
 
-                ChangeNotifier action = new ChangeNotifier(applicationList);
+                ChangeNotifier action = new ChangeNotifier(applicationList, true);
+                final HashMap<ComponentName, ApplicationInfo> appInfoCache = mAppInfoCache;
 
                 for (int i = 0; i < count && !mStopped; i++) {
-                    ApplicationInfo application = new ApplicationInfo();
                     ResolveInfo info = apps.get(i);
-
-                    application.title = info.loadLabel(manager);
-                    if (application.title == null) {
-                        application.title = info.activityInfo.name;
-                    }
-                    application.setActivity(new ComponentName(
+                    ComponentName componentName = new ComponentName(
                             info.activityInfo.applicationInfo.packageName,
-                            info.activityInfo.name),
-                            Intent.FLAG_ACTIVITY_NEW_TASK |
-                            Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-                    application.icon = info.activityInfo.loadIcon(manager);
-                    application.container = ItemInfo.NO_ID;
-
-                    action.add(application);
-                }
-
-                action.sort(new Comparator<ApplicationInfo>() {
-                    public final int compare(ApplicationInfo a, ApplicationInfo b) {
-                        return sCollator.compare(a.title.toString(), b.title.toString());
+                            info.activityInfo.name);
+                    ApplicationInfo application = appInfoCache.get(componentName);
+                    if (application == null) {
+                        application = new ApplicationInfo();
+                        application.title = info.loadLabel(manager);
+                        if (application.title == null) {
+                            application.title = info.activityInfo.name;
+                        }
+                        application.setActivity(componentName,
+                                Intent.FLAG_ACTIVITY_NEW_TASK |
+                                Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+                        application.container = ItemInfo.NO_ID;
+                        application.icon = info.activityInfo.loadIcon(manager);
+                        if (DEBUG) {
+                            Log.d(Launcher.LOG_TAG, "Loaded ApplicationInfo for " + componentName);
+                        }
+                        appInfoCache.put(componentName, application);
                     }
-                });
 
-                if (!mStopped) {
-                    launcher.runOnUiThread(action);
+                    if (action.add(application)) {
+                        launcher.runOnUiThread(action);
+                        action = new ChangeNotifier(applicationList, false);
+                    }
                 }
+
+                launcher.runOnUiThread(action);
             }
 
             if (!mStopped) {
@@ -188,51 +229,66 @@
         }
     }
 
-    private static class ChangeNotifier implements Runnable {
+    private static class ChangeNotifier implements Runnable, Comparator<ApplicationInfo> {
         private final ApplicationsAdapter mApplicationList;
-        private ArrayList<ApplicationInfo> mBuffer;
+        private final ArrayList<ApplicationInfo> mBuffer;
 
-        ChangeNotifier(ApplicationsAdapter applicationList) {
+        private boolean mFirst = true;
+
+        ChangeNotifier(ApplicationsAdapter applicationList, boolean first) {
             mApplicationList = applicationList;
+            mFirst = first;
             mBuffer = new ArrayList<ApplicationInfo>(UI_NOTIFICATION_RATE);
         }
 
         public void run() {
-            final ArrayList<ApplicationInfo> buffer = mBuffer;
             final ApplicationsAdapter applicationList = mApplicationList;
+
+            if (mFirst) {
+                applicationList.setNotifyOnChange(false);
+                applicationList.clear();
+                mFirst = false;
+            }
+
+            final ArrayList<ApplicationInfo> buffer = mBuffer;
             final int count = buffer.size();
 
-            applicationList.clear();
             for (int i = 0; i < count; i++) {
                 applicationList.setNotifyOnChange(false);
                 applicationList.add(buffer.get(i));
             }
 
-            applicationList.notifyDataSetChanged();
             buffer.clear();
+
+            applicationList.sort(this);
+            applicationList.notifyDataSetChanged();
         }
 
-        void add(ApplicationInfo application) {
-            mBuffer.add(application);
+        boolean add(ApplicationInfo application) {
+            final ArrayList<ApplicationInfo> buffer = mBuffer;
+            buffer.add(application);
+            return buffer.size() >= UI_NOTIFICATION_RATE;
         }
 
-        void sort(Comparator<ApplicationInfo> comparator) {
-            Collections.sort(mBuffer, comparator);
+        public final int compare(ApplicationInfo a, ApplicationInfo b) {
+            return sCollator.compare(a.title.toString(), b.title.toString());
         }
     }
 
     boolean isDesktopLoaded() {
-        return mDesktopItems != null && mDesktopItemsLoaded;
+        return mDesktopItems != null && mDesktopAppWidgets != null && mDesktopItemsLoaded;
     }
-    
+
     /**
      * Loads all of the items on the desktop, in folders, or in the dock.
      * These can be apps, shortcuts or widgets
      */
     void loadUserItems(boolean isLaunching, Launcher launcher, boolean localeChanged,
             boolean loadApplications) {
+android.util.Log.d("Home", "loading user items");
 
-        if (isLaunching && mDesktopItems != null && mDesktopItemsLoaded) {
+        if (isLaunching && isDesktopLoaded()) {
+android.util.Log.d("Home", "  --> items loaded, return");
             if (loadApplications) startApplicationsLoader(launcher);
             // We have already loaded our data from the DB
             launcher.onDesktopItemsLoaded();
@@ -244,16 +300,17 @@
             // Wait for the currently running thread to finish, this can take a little
             // time but it should be well below the timeout limit
             try {
-                mDesktopLoader.join(APPLICATION_NOT_RESPONDING_TIMEOUT);
+                mDesktopLoaderThread.join(APPLICATION_NOT_RESPONDING_TIMEOUT);
             } catch (InterruptedException e) {
                 // Empty
             }
         }
 
+android.util.Log.d("Home", "  --> starting workspace loader");
         mDesktopItemsLoaded = false;
         mDesktopItemsLoader = new DesktopItemsLoader(launcher, localeChanged, loadApplications);
-        mDesktopLoader = new Thread(mDesktopItemsLoader, "Desktop Items Loader");
-        mDesktopLoader.start();
+        mDesktopLoaderThread = new Thread(mDesktopItemsLoader, "Desktop Items Loader");
+        mDesktopLoaderThread.start();
     }
 
     private static void updateShortcutLabels(ContentResolver resolver, PackageManager manager) {
@@ -272,7 +329,8 @@
         try {
             while (c.moveToNext()) {
                 try {
-                    if (c.getInt(itemTypeIndex) != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+                    if (c.getInt(itemTypeIndex) !=
+                            LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
                         continue;
                     }
 
@@ -357,9 +415,11 @@
             }
 
             mDesktopItems = new ArrayList<ItemInfo>();
+            mDesktopAppWidgets = new ArrayList<LauncherAppWidgetInfo>();
             mFolders = new HashMap<Long, FolderInfo>();
 
             final ArrayList<ItemInfo> desktopItems = mDesktopItems;
+            final ArrayList<LauncherAppWidgetInfo> desktopAppWidgets = mDesktopAppWidgets;
 
             final Cursor c = contentResolver.query(
                     LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
@@ -374,15 +434,19 @@
                 final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
                 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
                 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
+                final int appWidgetIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.APPWIDGET_ID);
                 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);
                 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
                 final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
 
                 ApplicationInfo info;
                 String intentDescription;
-                Widget widgetInfo = null;
+                Widget widgetInfo;
+                LauncherAppWidgetInfo appWidgetInfo;
                 int container;
                 long id;
                 Intent intent;
@@ -494,41 +558,44 @@
                                     break;
                             }
                             break;
-                        case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_CLOCK:
                         case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH:
-                        case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME:
-                            switch (itemType) {
-                            case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_CLOCK:
-                                widgetInfo = Widget.makeClock();
-                                break;
-                            case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH:
-                                widgetInfo = Widget.makeSearch();
-                                break;
-                            case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME:
-                                widgetInfo = Widget.makePhotoFrame();
-                                byte[] data = c.getBlob(iconIndex);
-                                if (data != null) {
-                                    widgetInfo.photo =
-                                            BitmapFactory.decodeByteArray(data, 0, data.length);
-                                }
-                                break;
+                            widgetInfo = Widget.makeSearch();
+
+                            container = c.getInt(containerIndex);
+                            if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+                                Log.e(Launcher.LOG_TAG, "Widget found where container "
+                                        + "!= CONTAINER_DESKTOP  ignoring!");
+                                continue;
                             }
 
-                            if (widgetInfo != null) {
-                                container = c.getInt(containerIndex);
-                                if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-                                    Log.e(Launcher.LOG_TAG, "Widget found where container "
-                                            + "!= CONTAINER_DESKTOP -- ignoring!");
-                                    continue;
-                                }
-                                widgetInfo.id = c.getLong(idIndex);
-                                widgetInfo.screen = c.getInt(screenIndex);
-                                widgetInfo.container = container;
-                                widgetInfo.cellX = c.getInt(cellXIndex);
-                                widgetInfo.cellY = c.getInt(cellYIndex);
+                            widgetInfo.id = c.getLong(idIndex);
+                            widgetInfo.screen = c.getInt(screenIndex);
+                            widgetInfo.container = container;
+                            widgetInfo.cellX = c.getInt(cellXIndex);
+                            widgetInfo.cellY = c.getInt(cellYIndex);
 
-                                desktopItems.add(widgetInfo);
+                            desktopItems.add(widgetInfo);
+                            break;
+                        case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+                            // Read all Launcher-specific widget details
+                            int appWidgetId = c.getInt(appWidgetIdIndex);
+                            appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
+                            appWidgetInfo.id = c.getLong(idIndex);
+                            appWidgetInfo.screen = c.getInt(screenIndex);
+                            appWidgetInfo.cellX = c.getInt(cellXIndex);
+                            appWidgetInfo.cellY = c.getInt(cellYIndex);
+                            appWidgetInfo.spanX = c.getInt(spanXIndex);
+                            appWidgetInfo.spanY = c.getInt(spanYIndex);
+
+                            container = c.getInt(containerIndex);
+                            if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+                                Log.e(Launcher.LOG_TAG, "Widget found where container "
+                                        + "!= CONTAINER_DESKTOP -- ignoring!");
+                                continue;
                             }
+                            appWidgetInfo.container = c.getInt(containerIndex);
+
+                            desktopAppWidgets.add(appWidgetInfo);
                             break;
                         }
                     } catch (Exception e) {
@@ -578,7 +645,7 @@
                 break;
             default:
                 liveFolderInfo.icon =
-                        launcher.getResources().getDrawable(R.drawable.ic_launcher_folder);                                    
+                        launcher.getResources().getDrawable(R.drawable.ic_launcher_folder);
         }
     }
 
@@ -586,7 +653,7 @@
      * Finds the user folder defined by the specified id.
      *
      * @param id The id of the folder to look for.
-     * 
+     *
      * @return A UserFolderInfo if the folder exists or null otherwise.
      */
     FolderInfo findFolderById(long id) {
@@ -635,8 +702,10 @@
         mApplicationsAdapter = null;
         unbindAppDrawables(mApplications);
         unbindDrawables(mDesktopItems);
+        unbindAppWidgetHostViews(mDesktopAppWidgets);
+        unbindCachedIconDrawables();
     }
-    
+
     /**
      * Remove the callback for the cached drawables or we leak the previous
      * Home screen on orientation change.
@@ -650,11 +719,12 @@
                 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                     ((ApplicationInfo)item).icon.setCallback(null);
+                    break;
                 }
             }
         }
     }
-    
+
     /**
      * Remove the callback for the cached drawables or we leak the previous
      * Home screen on orientation change.
@@ -669,31 +739,54 @@
     }
 
     /**
-     * @return The current list of applications
+     * Remove any {@link LauncherAppWidgetHostView} references in our widgets.
      */
-    public ArrayList<ApplicationInfo> getApplications() {
-        return mApplications;
+    private void unbindAppWidgetHostViews(ArrayList<LauncherAppWidgetInfo> appWidgets) {
+        if (appWidgets != null) {
+            final int count = appWidgets.size();
+            for (int i = 0; i < count; i++) {
+                LauncherAppWidgetInfo launcherInfo = appWidgets.get(i);
+                launcherInfo.hostView = null;
+            }
+        }
+    }
+
+    /**
+     * Remove the callback for the cached drawables or we leak the previous
+     * Home screen on orientation change.
+     */
+    private void unbindCachedIconDrawables() {
+        for (ApplicationInfo appInfo : mAppInfoCache.values()) {
+            appInfo.icon.setCallback(null);
+        }
     }
 
     /**
      * @return The current list of applications
      */
-    public ApplicationsAdapter getApplicationsAdapter() {
+    ApplicationsAdapter getApplicationsAdapter() {
         return mApplicationsAdapter;
     }
 
     /**
      * @return The current list of desktop items
      */
-    public ArrayList<ItemInfo> getDesktopItems() {
+    ArrayList<ItemInfo> getDesktopItems() {
         return mDesktopItems;
     }
+    
+    /**
+     * @return The current list of desktop items
+     */
+    ArrayList<LauncherAppWidgetInfo> getDesktopAppWidgets() {
+        return mDesktopAppWidgets;
+    }
 
     /**
      * Add an item to the desktop
      * @param info
      */
-    public void addDesktopItem(ItemInfo info) {
+    void addDesktopItem(ItemInfo info) {
         // TODO: write to DB; also check that folder has been added to folders list
         mDesktopItems.add(info);
     }
@@ -702,12 +795,26 @@
      * Remove an item from the desktop
      * @param info
      */
-    public void removeDesktopItem(ItemInfo info) {
+    void removeDesktopItem(ItemInfo info) {
         // TODO: write to DB; figure out if we should remove folder from folders list
         mDesktopItems.remove(info);
     }
 
     /**
+     * Add a widget to the desktop
+     */
+    void addDesktopAppWidget(LauncherAppWidgetInfo info) {
+        mDesktopAppWidgets.add(info);
+    }
+    
+    /**
+     * Remove a widget from the desktop
+     */
+    void removeDesktopAppWidget(LauncherAppWidgetInfo info) {
+        mDesktopAppWidgets.remove(info);
+    }
+
+    /**
      * Make an ApplicationInfo object for an application
      */
     private static ApplicationInfo getApplicationInfo(PackageManager manager, Intent intent) {
@@ -886,37 +993,6 @@
         return null;
     }
 
-    static Widget getPhotoFrameInfo(Context context, int screen, int cellX, int cellY) {
-        final ContentResolver cr = context.getContentResolver();
-        Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
-            null, "screen=? and cellX=? and cellY=? and itemType=?",
-            new String[] { String.valueOf(screen), String.valueOf(cellX), String.valueOf(cellY),
-                String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME) }, null);
-
-        try {
-            if (c.moveToFirst()) {
-                final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ID);
-                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);
-
-                Widget widgetInfo = Widget.makePhotoFrame();
-                widgetInfo.id = c.getLong(idIndex);
-                widgetInfo.screen = c.getInt(screenIndex);
-                widgetInfo.container = c.getInt(containerIndex);
-                widgetInfo.cellX = c.getInt(cellXIndex);
-                widgetInfo.cellY = c.getInt(cellYIndex);
-
-                return widgetInfo;
-            }
-        } finally {
-            c.close();
-        }
-
-        return null;
-    }
-
     /**
      * Add an item to the database in a specified container. Sets the container, screen, cellX and
      * cellY fields of the item. Also assigns an ID to the item.
@@ -972,7 +1048,7 @@
         final ContentResolver cr = context.getContentResolver();
 
         cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
-        cr.delete(LauncherSettings.Favorites.CONTENT_URI, LauncherSettings.Favorites.CONTAINER + "=" + info.id,
-                null);
+        cr.delete(LauncherSettings.Favorites.CONTENT_URI,
+                LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
     }
 }
diff --git a/src/com/android/launcher/LauncherProvider.java b/src/com/android/launcher/LauncherProvider.java
index a3e529d..5cd7a0f 100644
--- a/src/com/android/launcher/LauncherProvider.java
+++ b/src/com/android/launcher/LauncherProvider.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher;
 
+import android.appwidget.AppWidgetHost;
 import android.content.ContentProvider;
 import android.content.Context;
 import android.content.ContentValues;
@@ -29,6 +30,7 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteQueryBuilder;
 import android.database.Cursor;
+import android.database.SQLException;
 import android.util.Log;
 import android.util.Xml;
 import android.net.Uri;
@@ -40,18 +42,25 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.util.ArrayList;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import com.android.internal.util.XmlUtils;
+import com.android.launcher.LauncherSettings.Favorites;
 
 public class LauncherProvider extends ContentProvider {
-    private static final String LOG_TAG = "LauncherSettingsProvider";
+    private static final String LOG_TAG = "LauncherProvider";
+    private static final boolean LOGD = true;
 
     private static final String DATABASE_NAME = "launcher.db";
-    private static final int DATABASE_VERSION = 1;
+    
+    private static final int DATABASE_VERSION = 3;
 
     static final String AUTHORITY = "com.android.launcher.settings";
+    
+    static final String EXTRA_BIND_SOURCES = "com.android.launcher.settings.bindsources";
+    static final String EXTRA_BIND_TARGETS = "com.android.launcher.settings.bindtargets";
 
     static final String TABLE_FAVORITES = "favorites";
     static final String PARAMETER_NOTIFY = "notify";
@@ -168,14 +177,18 @@
         private static final String ATTRIBUTE_Y = "y";
 
         private final Context mContext;
+        private final AppWidgetHost mAppWidgetHost;
 
         DatabaseHelper(Context context) {
             super(context, DATABASE_NAME, null, DATABASE_VERSION);
             mContext = context;
+            mAppWidgetHost = new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID);
         }
 
         @Override
         public void onCreate(SQLiteDatabase db) {
+            if (LOGD) Log.d(LOG_TAG, "creating new launcher database");
+            
             db.execSQL("CREATE TABLE favorites (" +
                     "_id INTEGER PRIMARY KEY," +
                     "title TEXT," +
@@ -187,6 +200,7 @@
                     "spanX INTEGER," +
                     "spanY INTEGER," +
                     "itemType INTEGER," +
+                    "appWidgetId INTEGER NOT NULL DEFAULT -1," +
                     "isShortcut INTEGER," +
                     "iconType INTEGER," +
                     "iconPackage TEXT," +
@@ -196,6 +210,11 @@
                     "displayMode INTEGER" +
                     ");");
 
+            // Database was just created, so wipe any previous widgets
+            if (mAppWidgetHost != null) {
+                mAppWidgetHost.deleteHost();
+            }
+            
             if (!convertDatabase(db)) {
                 // Populate favorites table with initial favorites
                 loadFavorites(db, DEFAULT_FAVORITES_PATH);
@@ -203,10 +222,11 @@
         }
 
         private boolean convertDatabase(SQLiteDatabase db) {
+            if (LOGD) Log.d(LOG_TAG, "converting database from an older format, but not onUpgrade");
             boolean converted = false;
 
             final Uri uri = Uri.parse("content://" + Settings.AUTHORITY +
-                    "/favorites?notify=true");
+                    "/old_favorites?notify=true");
             final ContentResolver resolver = mContext.getContentResolver();
             Cursor cursor = null;
 
@@ -228,6 +248,12 @@
                     resolver.delete(uri, null, null);
                 }
             }
+            
+            if (converted) {
+                // Convert widgets from this import into widgets
+                if (LOGD) Log.d(LOG_TAG, "converted and now triggering widget upgrade");
+                convertWidgets(db);
+            }
 
             return converted;
         }
@@ -261,6 +287,7 @@
                 values.put(LauncherSettings.Favorites.ICON_RESOURCE, c.getString(iconResourceIndex));
                 values.put(LauncherSettings.Favorites.CONTAINER, c.getInt(containerIndex));
                 values.put(LauncherSettings.Favorites.ITEM_TYPE, c.getInt(itemTypeIndex));
+                values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1);
                 values.put(LauncherSettings.Favorites.SCREEN, c.getInt(screenIndex));
                 values.put(LauncherSettings.Favorites.CELLX, c.getInt(cellXIndex));
                 values.put(LauncherSettings.Favorites.CELLY, c.getInt(cellYIndex));
@@ -290,14 +317,130 @@
 
         @Override
         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-            Log.w(LOG_TAG, "Upgrading database from version " + oldVersion + " to " +
-                    newVersion + ", which will destroy all old data");
+            if (LOGD) Log.d(LOG_TAG, "onUpgrade triggered");
+            
+            int version = oldVersion;
+            if (version < 3) {
+                // upgrade 1,2 -> 3 added appWidgetId column
+                db.beginTransaction();
+                try {
+                    // Insert new column for holding appWidgetIds
+                    db.execSQL("ALTER TABLE favorites " +
+                        "ADD COLUMN appWidgetId INTEGER NOT NULL DEFAULT -1;");
+                    db.setTransactionSuccessful();
+                    version = 3;
+                } catch (SQLException ex) {
+                    // Old version remains, which means we wipe old data
+                    Log.e(LOG_TAG, ex.getMessage(), ex);
+                } finally {
+                    db.endTransaction();
+                }
+                
+                // Convert existing widgets only if table upgrade was successful
+                if (version == 3) {
+                    convertWidgets(db);
+                }
+            }
+            
+            if (version != DATABASE_VERSION) {
+                Log.w(LOG_TAG, "Destroying all old data.");
+                db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
+                onCreate(db);
+            }
+        }
+        
+        /**
+         * Upgrade existing clock and photo frame widgets into their new widget
+         * equivalents. This method allocates appWidgetIds, and then hands off to
+         * LauncherAppWidgetBinder to finish the actual binding.
+         */
+        private void convertWidgets(SQLiteDatabase db) {
+            final int[] bindSources = new int[] {
+                    Favorites.ITEM_TYPE_WIDGET_CLOCK,
+                    Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME,
+            };
+            
+            final ArrayList<ComponentName> bindTargets = new ArrayList<ComponentName>();
+            bindTargets.add(new ComponentName("com.android.alarmclock",
+                    "com.android.alarmclock.AnalogAppWidgetProvider"));
+            bindTargets.add(new ComponentName("com.android.camera",
+                    "com.android.camera.PhotoAppWidgetProvider"));
+            
+            final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE, bindSources);
+            
+            Cursor c = null;
+            boolean allocatedAppWidgets = false;
+            
+            db.beginTransaction();
+            try {
+                // Select and iterate through each matching widget
+                c = db.query(TABLE_FAVORITES, new String[] { Favorites._ID },
+                        selectWhere, null, null, null, null);
+                
+                if (LOGD) Log.d(LOG_TAG, "found upgrade cursor count="+c.getCount());
+                
+                final ContentValues values = new ContentValues();
+                while (c != null && c.moveToNext()) {
+                    long favoriteId = c.getLong(0);
+                    
+                    // Allocate and update database with new appWidgetId
+                    try {
+                        int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
+                        
+                        if (LOGD) Log.d(LOG_TAG, "allocated appWidgetId="+appWidgetId+" for favoriteId="+favoriteId);
+                        
+                        values.clear();
+                        values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
+                        
+                        // Original widgets might not have valid spans when upgrading
+                        values.put(LauncherSettings.Favorites.SPANX, 2);
+                        values.put(LauncherSettings.Favorites.SPANY, 2);
 
-            db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
-            onCreate(db);
+                        String updateWhere = Favorites._ID + "=" + favoriteId;
+                        db.update(TABLE_FAVORITES, values, updateWhere, null);
+                        
+                        allocatedAppWidgets = true;
+                    } catch (RuntimeException ex) {
+                        Log.e(LOG_TAG, "Problem allocating appWidgetId", ex);
+                    }
+                }
+                
+                db.setTransactionSuccessful();
+            } catch (SQLException ex) {
+                Log.w(LOG_TAG, "Problem while allocating appWidgetIds for existing widgets", ex);
+            } finally {
+                db.endTransaction();
+                if (c != null) {
+                    c.close();
+                }
+            }
+            
+            // If any appWidgetIds allocated, then launch over to binder
+            if (allocatedAppWidgets) {
+                launchAppWidgetBinder(bindSources, bindTargets);
+            }
         }
 
-
+        /**
+         * Launch the widget binder that walks through the Launcher database,
+         * binding any matching widgets to the corresponding targets. We can't
+         * bind ourselves because our parent process can't obtain the
+         * BIND_APPWIDGET permission.
+         */
+        private void launchAppWidgetBinder(int[] bindSources, ArrayList<ComponentName> bindTargets) {
+            final Intent intent = new Intent();
+            intent.setComponent(new ComponentName("com.android.settings",
+                    "com.android.settings.LauncherAppWidgetBinder"));
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            
+            final Bundle extras = new Bundle();
+            extras.putIntArray(EXTRA_BIND_SOURCES, bindSources);
+            extras.putParcelableArrayList(EXTRA_BIND_TARGETS, bindTargets);
+            intent.putExtras(extras);
+            
+            mContext.startActivity(intent);
+        }
+        
         /**
          * Loads the default set of favorite packages from an xml file.
          *
@@ -370,20 +513,7 @@
             } catch (IOException e) {
                 Log.w(LOG_TAG, "Got exception parsing favorites.", e);
             }
-
-            // Add a clock
-            values.clear();
-            values.put(LauncherSettings.Favorites.CONTAINER,
-                    LauncherSettings.Favorites.CONTAINER_DESKTOP);
-            values.put(LauncherSettings.Favorites.ITEM_TYPE,
-                    LauncherSettings.Favorites.ITEM_TYPE_WIDGET_CLOCK);
-            values.put(LauncherSettings.Favorites.SCREEN, 1);
-            values.put(LauncherSettings.Favorites.CELLX, 1);
-            values.put(LauncherSettings.Favorites.CELLY, 0);
-            values.put(LauncherSettings.Favorites.SPANX, 2);
-            values.put(LauncherSettings.Favorites.SPANY, 2);
-            db.insert(TABLE_FAVORITES, null, values);
-
+            
             // Add a search box
             values.clear();
             values.put(LauncherSettings.Favorites.CONTAINER,
@@ -396,11 +526,63 @@
             values.put(LauncherSettings.Favorites.SPANX, 4);
             values.put(LauncherSettings.Favorites.SPANY, 1);
             db.insert(TABLE_FAVORITES, null, values);
+            
+            final int[] bindSources = new int[] {
+                    Favorites.ITEM_TYPE_WIDGET_CLOCK,
+            };
+            
+            final ArrayList<ComponentName> bindTargets = new ArrayList<ComponentName>();
+            bindTargets.add(new ComponentName("com.android.alarmclock",
+                    "com.android.alarmclock.AnalogAppWidgetProvider"));
+            
+            boolean allocatedAppWidgets = false;
+            
+            // Try binding to an analog clock widget
+            try {
+                int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
+                
+                values.clear();
+                values.put(LauncherSettings.Favorites.CONTAINER,
+                        LauncherSettings.Favorites.CONTAINER_DESKTOP);
+                values.put(LauncherSettings.Favorites.ITEM_TYPE,
+                        LauncherSettings.Favorites.ITEM_TYPE_WIDGET_CLOCK);
+                values.put(LauncherSettings.Favorites.SCREEN, 1);
+                values.put(LauncherSettings.Favorites.CELLX, 1);
+                values.put(LauncherSettings.Favorites.CELLY, 0);
+                values.put(LauncherSettings.Favorites.SPANX, 2);
+                values.put(LauncherSettings.Favorites.SPANY, 2);
+                values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
+                db.insert(TABLE_FAVORITES, null, values);
+                
+                allocatedAppWidgets = true;
+            } catch (RuntimeException ex) {
+                Log.e(LOG_TAG, "Problem allocating appWidgetId", ex);
+            }
 
+            // If any appWidgetIds allocated, then launch over to binder
+            if (allocatedAppWidgets) {
+                launchAppWidgetBinder(bindSources, bindTargets);
+            }
+            
             return i;
         }
     }
 
+    /**
+     * Build a query string that will match any row where the column matches
+     * anything in the values list.
+     */
+    static String buildOrWhereString(String column, int[] values) {
+        StringBuilder selectWhere = new StringBuilder();
+        for (int i = values.length - 1; i >= 0; i--) {
+            selectWhere.append(column).append("=").append(values[i]);
+            if (i > 0) {
+                selectWhere.append(" OR ");
+            }
+        }
+        return selectWhere.toString();
+    }
+
     static class SqlArguments {
         public final String table;
         public final String where;
diff --git a/src/com/android/launcher/LauncherSettings.java b/src/com/android/launcher/LauncherSettings.java
index c5dfd1f..60ea0df 100644
--- a/src/com/android/launcher/LauncherSettings.java
+++ b/src/com/android/launcher/LauncherSettings.java
@@ -24,7 +24,8 @@
  */
 class LauncherSettings {
     /**
-     * Favorites.
+     * Favorites. When changing these values, be sure to update
+     * {@link com.android.settings.LauncherAppWidgetBinder} as needed.
      */
     static final class Favorites implements BaseColumns {
         /**
@@ -147,6 +148,11 @@
         static final int ITEM_TYPE_LIVE_FOLDER = 3;
 
         /**
+         * The favorite is a widget
+         */
+        static final int ITEM_TYPE_APPWIDGET = 4;
+
+        /**
          * The favorite is a clock
          */
         static final int ITEM_TYPE_WIDGET_CLOCK = 1000;
@@ -162,6 +168,13 @@
         static final int ITEM_TYPE_WIDGET_PHOTO_FRAME = 1002;
 
         /**
+         * The appWidgetId of the widget
+         *
+         * <P>Type: INTEGER</P>
+         */
+        static final String APPWIDGET_ID = "appWidgetId";
+        
+        /**
          * Indicates whether this favorite is an application-created shortcut or not.
          * If the value is 0, the favorite is not an application-created shortcut, if the
          * value is 1, it is an application-created shortcut.
diff --git a/src/com/android/launcher/LiveFolder.java b/src/com/android/launcher/LiveFolder.java
index 37b98e0..5d727f8 100644
--- a/src/com/android/launcher/LiveFolder.java
+++ b/src/com/android/launcher/LiveFolder.java
@@ -24,8 +24,14 @@
 import android.widget.AdapterView;
 import android.net.Uri;
 import android.provider.LiveFolders;
+import android.os.AsyncTask;
+import android.database.Cursor;
+
+import java.lang.ref.WeakReference;
 
 public class LiveFolder extends Folder {
+    private AsyncTask<LiveFolderInfo,Void,Cursor> mLoadingTask;
+
     public LiveFolder(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -66,7 +72,10 @@
 
     void bind(FolderInfo info) {
         super.bind(info);
-        setContentAdapter(new LiveFolderAdapter(mLauncher, (LiveFolderInfo) info));
+        if (mLoadingTask != null && mLoadingTask.getStatus() == AsyncTask.Status.RUNNING) {
+            mLoadingTask.cancel(true);
+        }
+        mLoadingTask = new FolderLoadingTask(this).execute((LiveFolderInfo) info);
     }
 
     @Override
@@ -78,6 +87,42 @@
     @Override
     void onClose() {
         super.onClose();
+        if (mLoadingTask != null && mLoadingTask.getStatus() == AsyncTask.Status.RUNNING) {
+            mLoadingTask.cancel(true);
+        }
         ((LiveFolderAdapter) mContent.getAdapter()).cleanup();
     }
+
+    static class FolderLoadingTask extends AsyncTask<LiveFolderInfo, Void, Cursor> {
+        private final WeakReference<LiveFolder> mFolder;
+        private LiveFolderInfo mInfo;
+
+        FolderLoadingTask(LiveFolder folder) {
+            mFolder = new WeakReference<LiveFolder>(folder);
+        }
+
+        protected Cursor doInBackground(LiveFolderInfo... params) {
+            final LiveFolder folder = mFolder.get();
+            if (folder != null) {
+                mInfo = params[0];
+                return LiveFolderAdapter.query(folder.mLauncher, mInfo);
+            }
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Cursor cursor) {
+            if (!isCancelled()) {
+                if (cursor != null) {
+                    final LiveFolder folder = mFolder.get();
+                    if (folder != null) {
+                        final Launcher launcher = folder.mLauncher;
+                        folder.setContentAdapter(new LiveFolderAdapter(launcher, mInfo, cursor));
+                    }
+                }
+            } else if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
 }
diff --git a/src/com/android/launcher/LiveFolderAdapter.java b/src/com/android/launcher/LiveFolderAdapter.java
index 01db5a6..4a5c077 100644
--- a/src/com/android/launcher/LiveFolderAdapter.java
+++ b/src/com/android/launcher/LiveFolderAdapter.java
@@ -45,8 +45,8 @@
             new HashMap<Long, SoftReference<Drawable>>();
     private final Launcher mLauncher;
 
-    LiveFolderAdapter(Launcher launcher, LiveFolderInfo info) {
-        super(launcher, query(launcher, info), true);
+    LiveFolderAdapter(Launcher launcher, LiveFolderInfo info, Cursor cursor) {
+        super(launcher, cursor, true);
         mIsList = info.displayMode == LiveFolders.DISPLAY_MODE_LIST;
         mInflater = LayoutInflater.from(launcher);
         mLauncher = launcher;
@@ -54,8 +54,9 @@
         mLauncher.startManagingCursor(getCursor());
     }
 
-    private static Cursor query(Context context, LiveFolderInfo info) {
-        return context.getContentResolver().query(info.uri, null, null, null, LiveFolders.NAME + " ASC");
+    static Cursor query(Context context, LiveFolderInfo info) {
+        return context.getContentResolver().query(info.uri, null, null,
+                null, LiveFolders.NAME + " ASC");
     }
 
     public View newView(Context context, Cursor cursor, ViewGroup parent) {
@@ -178,10 +179,13 @@
         }
         mCustomIcons.clear();
 
-        try {
-            getCursor().close();
-        } finally {
-            mLauncher.stopManagingCursor(getCursor());
+        final Cursor cursor = getCursor();
+        if (cursor != null) {
+            try {
+                cursor.close();
+            } finally {
+                mLauncher.stopManagingCursor(cursor);
+            }
         }
     }
 
diff --git a/src/com/android/launcher/PhotoFrame.java b/src/com/android/launcher/PhotoFrame.java
deleted file mode 100644
index 1151322..0000000
--- a/src/com/android/launcher/PhotoFrame.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.ImageView;
-
-
-/**
- * Desktop widget that holds a user folder
- *
- */
-public class PhotoFrame extends ImageView implements OnClickListener {
-    
-    public PhotoFrame(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        setClickable(true);
-        setOnClickListener(this);
-        setWillNotCacheDrawing(true);
-    }
-    
-    public void onClick(View v) {
-        ((Launcher) mContext).updatePhotoFrame((Widget) getTag());
-    }
-}
diff --git a/src/com/android/launcher/Search.java b/src/com/android/launcher/Search.java
index 41d6562..97dcd98 100644
--- a/src/com/android/launcher/Search.java
+++ b/src/com/android/launcher/Search.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher;
 
-import java.util.List;
-
 import android.app.ISearchManager;
 import android.app.SearchManager;
 import android.content.ActivityNotFoundException;
@@ -62,10 +60,11 @@
 public class Search extends LinearLayout implements OnClickListener, OnKeyListener,
         OnLongClickListener, TextWatcher, OnItemClickListener, OnItemSelectedListener {
 
-    private final String TAG = "SearchGadget";
+    private final String TAG = "SearchWidget";
 
     private AutoCompleteTextView mSearchText;
     private ImageButton mGoButton;
+    private ImageButton mVoiceButton;
     private OnLongClickListener mLongClickListener;
     
     // Support for suggestions
@@ -76,7 +75,11 @@
     private String mSuggestionQuery = null;
     private int mItemSelected = -1;
     
+    // For voice searching
+    private Intent mVoiceSearchIntent;
+
     private Rect mTempRect = new Rect();
+    private boolean mRestoreFocus = false;
 
     /**
      * Used to inflate the Workspace from XML.
@@ -86,6 +89,10 @@
      */
     public Search(Context context, AttributeSet attrs) {
         super(context, attrs);
+        
+        mVoiceSearchIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
+        mVoiceSearchIntent.putExtra(android.speech.RecognizerIntent.EXTRA_LANGUAGE_MODEL,
+                android.speech.RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
     }
     
     /**
@@ -94,6 +101,14 @@
     public void onClick(View v) {
         if (v == mGoButton) {
             query();
+        } else if (v == mVoiceButton) {
+            try {
+                getContext().startActivity(mVoiceSearchIntent);
+            } catch (ActivityNotFoundException ex) {
+                // Should not happen, since we check the availability of
+                // voice search before showing the button. But just in case...
+                Log.w(TAG, "Could not find voice search activity");
+            }
         }
     }
 
@@ -154,7 +169,26 @@
 
         getContext().startActivity(launcher);
     }
-    
+
+    @Override
+    public void onWindowFocusChanged(boolean hasWindowFocus) {
+        if (!hasWindowFocus && hasFocus()) {
+            mRestoreFocus = true;
+        }
+
+        super.onWindowFocusChanged(hasWindowFocus);
+
+        if (hasWindowFocus && mRestoreFocus) {
+            if (isInTouchMode()) {
+                final AutoCompleteTextView searchText = mSearchText;
+                searchText.setSelectAllOnFocus(false);
+                searchText.requestFocusFromTouch();
+                searchText.setSelectAllOnFocus(true);
+            }
+            mRestoreFocus = false;
+        }
+    }
+
     /**
      * Implements TextWatcher (for EditText)
      */
@@ -206,7 +240,7 @@
                     return true;
                 }
             }
-        } else if (v == mGoButton) {
+        } else if (v == mGoButton || v == mVoiceButton) {
             boolean handled = false;
             if (!event.isSystem() && 
                     (keyCode != KeyEvent.KEYCODE_DPAD_UP) &&
@@ -243,7 +277,14 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        requestFocusFromTouch();
+        // Request focus unless the user tapped on the voice search button
+        final int x = (int) ev.getX();
+        final int y = (int) ev.getY();
+        final Rect frame = mTempRect;
+        mVoiceButton.getHitRect(frame);
+        if (!frame.contains(x, y)) {
+            requestFocusFromTouch();
+        }
         return super.onInterceptTouchEvent(ev);
     }
     
@@ -268,26 +309,29 @@
         mSearchText.addTextChangedListener(this);
 
         mGoButton = (ImageButton) findViewById(R.id.search_go_btn);
+        mVoiceButton = (ImageButton) findViewById(R.id.search_voice_btn);
         mGoButton.setOnClickListener(this);
+        mVoiceButton.setOnClickListener(this);
         mGoButton.setOnKeyListener(this);
+        mVoiceButton.setOnKeyListener(this);
         
         mSearchText.setOnLongClickListener(this);
         mGoButton.setOnLongClickListener(this);
+        mVoiceButton.setOnLongClickListener(this);
         
         // disable the button since we start out w/empty input
         mGoButton.setEnabled(false);
         mGoButton.setFocusable(false);
         
+        configureSearchableInfo();
         configureSuggestions();
+        configureVoiceSearchButton();
     }
     
-    /** The rest of the class deals with providing search suggestions */
-    
     /**
-     * Set up the suggestions provider mechanism
+     * Read the searchable info from the search manager
      */
-    private void configureSuggestions() {
-        // get SearchableInfo
+    private void configureSearchableInfo() {
         ISearchManager sms;
         SearchableInfo searchable;
         sms = ISearchManager.Stub.asInterface(ServiceManager.getService(Context.SEARCH_SERVICE));
@@ -303,6 +347,36 @@
             return;
         }
         mSearchable = searchable;
+    }
+    
+    /**
+     * If appropriate & available, configure voice search
+     * 
+     * Note:  Because the home screen search widget is always web search, we only check for
+     * getVoiceSearchLaunchWebSearch() modes.  We don't support the alternate form of app-specific
+     * voice search.
+     */
+    private void configureVoiceSearchButton() {
+        boolean voiceSearchVisible = false;
+        if (mSearchable.getVoiceSearchEnabled() && mSearchable.getVoiceSearchLaunchWebSearch()) {
+            // Enable the voice search button if there is an activity that can handle it
+            PackageManager pm = getContext().getPackageManager();
+            ResolveInfo ri = pm.resolveActivity(mVoiceSearchIntent,
+                    PackageManager.MATCH_DEFAULT_ONLY);
+            voiceSearchVisible = ri != null;
+        }
+        
+        // finally, set visible state of voice search button, as appropriate
+        mVoiceButton.setVisibility(voiceSearchVisible ? View.VISIBLE : View.GONE);
+    }
+     
+    /** The rest of the class deals with providing search suggestions */
+    
+    /**
+     * Set up the suggestions provider mechanism
+     */
+    private void configureSuggestions() {
+        // get SearchableInfo
         
         mSearchText.setOnItemClickListener(this);
         mSearchText.setOnItemSelectedListener(this);
@@ -315,6 +389,18 @@
     }
     
     /**
+     * Remove internal cursor references when detaching from window which
+     * prevents {@link Context} leaks.
+     */
+    @Override
+    public void onDetachedFromWindow() {
+        if (mSuggestionsAdapter != null) {
+            mSuggestionsAdapter.changeCursor(null);
+            mSuggestionsAdapter = null;
+        }
+    }
+    
+    /**
      * Implements OnItemClickListener
      */
     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
@@ -443,7 +529,11 @@
                             " returned exception" + e.toString());
         }
     }
-        
+
+    SearchAutoCompleteTextView getSearchInputField() {
+        return (SearchAutoCompleteTextView) mSearchText;
+    }
+
     /**
      * This class provides the filtering-based interface to suggestions providers.
      * It is hardwired in a couple of places to support GoogleSearch - for example, it supports
diff --git a/src/com/android/launcher/SearchAutoCompleteTextView.java b/src/com/android/launcher/SearchAutoCompleteTextView.java
new file mode 100644
index 0000000..e25a8f1
--- /dev/null
+++ b/src/com/android/launcher/SearchAutoCompleteTextView.java
@@ -0,0 +1,106 @@
+/*
+ * 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.launcher;
+
+import android.widget.AutoCompleteTextView;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.graphics.Rect;
+import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
+import android.app.Activity;
+
+/**
+ * This class is not for the faint of heart. Home works in the pan & scan
+ * soft input mode. However, this mode gets rid of the soft keyboard on rotation,
+ * which is a probelm when the Search widget has focus. This special class
+ * changes Home's soft input method temporarily as long as the Search widget holds
+ * the focus. This way, the soft keyboard remains after rotation.
+ */
+public class SearchAutoCompleteTextView extends AutoCompleteTextView {
+    private boolean mShowKeyboard;
+
+    private Handler mLoseFocusHandler = new Handler() {
+        public void handleMessage(Message msg) {
+            if (msg.what == 1 && !hasFocus()) {
+                // Hide the soft keyboard when the search widget loses the focus
+                InputMethodManager.peekInstance().hideSoftInputFromWindow(getWindowToken(), 0);
+            }
+        }
+    };
+    
+    public SearchAutoCompleteTextView(Context context) {
+        super(context);
+    }
+
+    public SearchAutoCompleteTextView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public SearchAutoCompleteTextView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
+        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+
+        final WindowManager.LayoutParams lp = ((Activity) getContext()).getWindow().getAttributes();
+        if (gainFocus) {
+            lp.softInputMode =
+                    (lp.softInputMode & ~WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) |
+                            WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;
+        } else {
+            //noinspection PointlessBitwiseExpression
+            lp.softInputMode =
+                    (lp.softInputMode & ~WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) |
+                            WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED;
+            // If we don't immediately gain focus, we want to hide the IME.
+            mLoseFocusHandler.sendEmptyMessage(1);
+        }
+
+        if (getWindowToken() != null) {
+            final WindowManager manager = (WindowManager)
+                    getContext().getSystemService(Context.WINDOW_SERVICE);
+            manager.updateViewLayout(getRootView(), lp);
+
+            if (mShowKeyboard) {
+                if (getContext().getResources().getConfiguration().hardKeyboardHidden ==
+                        Configuration.HARDKEYBOARDHIDDEN_YES) {
+                    InputMethodManager inputManager = (InputMethodManager)
+                            getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+                    inputManager.showSoftInput(this, 0);
+                }
+                mShowKeyboard = false;
+            }
+        }
+    }
+
+    @Override
+    public void onWindowFocusChanged(boolean hasWindowFocus) {
+        super.onWindowFocusChanged(hasWindowFocus);
+        // See Workspace#focusOnSearch()
+        setFocusableInTouchMode(false);
+    }
+
+    void showKeyboardOnNextFocus() {
+        mShowKeyboard = true;
+    }
+}
diff --git a/src/com/android/launcher/WallpaperChooser.java b/src/com/android/launcher/WallpaperChooser.java
index afbe6f3..9ac922c 100644
--- a/src/com/android/launcher/WallpaperChooser.java
+++ b/src/com/android/launcher/WallpaperChooser.java
@@ -50,7 +50,6 @@
             R.drawable.wallpaper_path_small,
             R.drawable.wallpaper_sunrise_small,
             R.drawable.wallpaper_mountain_small,
-            R.drawable.wallpaper_ripples_small,
             R.drawable.wallpaper_road_small,
             R.drawable.wallpaper_jellyfish_small,
             R.drawable.wallpaper_zanzibar_small,
@@ -61,14 +60,13 @@
     };
 
     private static final Integer[] IMAGE_IDS = {
-            R.drawable.wallpaper_lake,
+            com.android.internal.R.drawable.default_wallpaper,
             R.drawable.wallpaper_sunset,
             R.drawable.wallpaper_beach,
             R.drawable.wallpaper_snow_leopard,
             R.drawable.wallpaper_path,
             R.drawable.wallpaper_sunrise,
             R.drawable.wallpaper_mountain,
-            R.drawable.wallpaper_ripples,
             R.drawable.wallpaper_road,
             R.drawable.wallpaper_jellyfish,
             R.drawable.wallpaper_zanzibar,
@@ -123,9 +121,6 @@
         final String[] extras = resources.getStringArray(R.array.extra_wallpapers);
         final String packageName = getApplication().getPackageName();
 
-        final ArrayList<Integer> images = mImages;
-        final ArrayList<Integer> thumbs = mThumbs;
-
         for (String extra : extras) {
             int res = resources.getIdentifier(extra, "drawable", packageName);
             if (res != 0) {
@@ -133,8 +128,8 @@
                         "drawable", packageName);
 
                 if (thumbRes != 0) {
-                    images.add(res);
-                    thumbs.add(res);
+                    mThumbs.add(thumbRes);
+                    mImages.add(res);
                 }
             }
         }
@@ -148,7 +143,7 @@
 
     public void onItemSelected(AdapterView parent, View v, int position, long id) {
         final ImageView view = mImageView;
-        Bitmap b = BitmapFactory.decodeResource(getResources(), IMAGE_IDS[position], mOptions);
+        Bitmap b = BitmapFactory.decodeResource(getResources(), mImages.get(position), mOptions);
         view.setImageBitmap(b);
 
         // Help the GC
diff --git a/src/com/android/launcher/Widget.java b/src/com/android/launcher/Widget.java
index 8812522..4f246cc 100644
--- a/src/com/android/launcher/Widget.java
+++ b/src/com/android/launcher/Widget.java
@@ -20,32 +20,11 @@
 import android.graphics.Bitmap;
 
 /**
- * Represents one instance of a Launcher widget (clock, search, photo frame).
- *
+ * Represents one instance of a Launcher widget, such as search.
  */
 class Widget extends ItemInfo {
-
     int layoutResource;
-    Bitmap photo;
 
-    static Widget makeClock() {
-        Widget w = new Widget();
-        w.itemType = LauncherSettings.Favorites.ITEM_TYPE_WIDGET_CLOCK;
-        w.spanX = 2;
-        w.spanY = 2;
-        w.layoutResource = R.layout.widget_clock;
-        return w;
-    }
-    
-    static Widget makePhotoFrame() {
-        Widget w = new Widget();
-        w.itemType = LauncherSettings.Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME;
-        w.spanX = 2;
-        w.spanY = 2;
-        w.layoutResource = R.layout.widget_photo_frame;
-        return w;
-    } 
-    
     static Widget makeSearch() {
         Widget w = new Widget();
         w.itemType = LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH;
@@ -54,11 +33,4 @@
         w.layoutResource = R.layout.widget_search;
         return w;
     }
-
-    @Override
-    void onAddToDatabase(ContentValues values) { 
-        super.onAddToDatabase(values);
-        writeBitmap(values, photo);
-    }
-
 }
diff --git a/src/com/android/launcher/Workspace.java b/src/com/android/launcher/Workspace.java
index 0ca1b5a..bc5347e 100644
--- a/src/com/android/launcher/Workspace.java
+++ b/src/com/android/launcher/Workspace.java
@@ -17,12 +17,15 @@
 package com.android.launcher;
 
 import android.content.Context;
+import android.content.Intent;
+import android.content.ComponentName;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.RectF;
 import android.graphics.Rect;
+import android.graphics.Region;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
@@ -89,6 +92,13 @@
     private boolean mAllowLongPress;
     private boolean mLocked;
 
+    private int mTouchSlop;
+
+    final Rect mDrawerBounds = new Rect();
+    final Rect mClipBounds = new Rect();
+    int mDrawerContentHeight;
+    int mDrawerContentWidth;
+
     /**
      * Used to inflate the Workspace from XML.
      *
@@ -126,6 +136,8 @@
 
         mPaint = new Paint();
         mPaint.setDither(false);
+
+        mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
     }
 
     /**
@@ -433,6 +445,29 @@
 
     @Override
     protected void dispatchDraw(Canvas canvas) {
+        boolean restore = false;
+
+        // If the all apps drawer is open and the drawing region for the workspace
+        // is contained within the drawer's bounds, we skip the drawing. This requires
+        // the drawer to be fully opaque.
+        if (mLauncher.isDrawerUp()) {
+            final Rect clipBounds = mClipBounds;
+            canvas.getClipBounds(clipBounds);
+            clipBounds.offset(-mScrollX, -mScrollY);
+            if (mDrawerBounds.contains(clipBounds)) {
+                return;
+            }
+        } else if (mLauncher.isDrawerMoving()) {
+            restore = true;
+            canvas.save(Canvas.CLIP_SAVE_FLAG);
+
+            final View view = mLauncher.getDrawerHandle();
+            final int top = view.getTop() + view.getHeight();
+
+            canvas.clipRect(mScrollX, top, mScrollX + mDrawerContentWidth,
+                    top + mDrawerContentHeight, Region.Op.DIFFERENCE);
+        }
+
         float x = mScrollX * mWallpaperOffset;
         if (x + mWallpaperWidth < mRight - mLeft) {
             x = mRight - mLeft - mWallpaperWidth;
@@ -464,6 +499,10 @@
                 }
             }
         }
+
+        if (restore) {
+            canvas.restore();
+        }
     }
 
     @Override
@@ -626,8 +665,8 @@
                  */
                 final int xDiff = (int) Math.abs(x - mLastMotionX);
                 final int yDiff = (int) Math.abs(y - mLastMotionY);
-                final int touchSlop = ViewConfiguration.getTouchSlop();
-                
+
+                final int touchSlop = mTouchSlop;
                 boolean xMoved = xDiff > touchSlop;
                 boolean yMoved = yDiff > touchSlop;
                 
@@ -669,6 +708,7 @@
                 // Release the drag
                 clearChildrenCache();
                 mTouchState = TOUCH_STATE_REST;
+                mAllowLongPress = false;
                 break;
         }
 
@@ -835,11 +875,16 @@
     }
 
     void addApplicationShortcut(ApplicationInfo info, CellLayout.CellInfo cellInfo) {
+        addApplicationShortcut(info, cellInfo, false);
+    }
+
+    void addApplicationShortcut(ApplicationInfo info, CellLayout.CellInfo cellInfo,
+            boolean insertAtFirst) {
         final CellLayout layout = (CellLayout) getChildAt(cellInfo.screen);
         final int[] result = new int[2];
 
         layout.cellToPoint(cellInfo.cellX, cellInfo.cellY, result);
-        onDropExternal(result[0], result[1], info, layout);
+        onDropExternal(result[0], result[1], info, layout, insertAtFirst);
     }
 
     public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo) {
@@ -878,6 +923,11 @@
     }
 
     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) {
         // Drag from somewhere else
         ItemInfo info = (ItemInfo) dragInfo;
 
@@ -901,7 +951,7 @@
             throw new IllegalStateException("Unknown item type: " + info.itemType);
         }
 
-        cellLayout.addView(view);
+        cellLayout.addView(view, insertAtFirst ? 0 : -1);
         view.setOnLongClickListener(mLongClickListener);
         cellLayout.onDropChild(view, x, y);
         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
@@ -979,12 +1029,12 @@
     /**
      * Find a search widget on the given screen
      */
-    private View findSearchWidget(CellLayout screen) {
+    private Search findSearchWidget(CellLayout screen) {
         final int count = screen.getChildCount();
         for (int i = 0; i < count; i++) {
             View v = screen.getChildAt(i);
             if (v instanceof Search) {
-                return v;
+                return (Search) v;
             }
         }
         return null;
@@ -995,9 +1045,30 @@
      * if there is one.  Also clears the current search selection so we don't 
      */
     private boolean focusOnSearch(int screen) {
-        CellLayout currentScreen = (CellLayout)getChildAt(screen);
-        Search searchWidget = (Search)findSearchWidget(currentScreen);
+        CellLayout currentScreen = (CellLayout) getChildAt(screen);
+        final Search searchWidget = findSearchWidget(currentScreen);
         if (searchWidget != null) {
+            // This is necessary when focus on search is requested from the menu
+            // If the workspace was not in touch mode before the menu is invoked
+            // and the user clicks "Search" by touching the menu item, the following
+            // happens:
+            //
+            // - We request focus from touch on the search widget
+            // - The search widget gains focus
+            // - The window focus comes back to Home's window
+            // - The touch mode change is propagated to Home's window
+            // - The search widget is not focusable in touch mode and ViewRoot
+            //   clears its focus
+            //
+            // Forcing focusable in touch mode ensures the search widget will
+            // keep the focus no matter what happens.
+            //
+            // Note: the search input field disables focusable in touch mode
+            // after the window gets the focus back, see SearchAutoCompleteTextView
+            final SearchAutoCompleteTextView input = searchWidget.getSearchInputField();
+            input.setFocusableInTouchMode(true);
+            input.showKeyboardOnNextFocus();
+
             if (isInTouchMode()) {
                 searchWidget.requestFocusFromTouch();
             } else {
@@ -1124,6 +1195,14 @@
     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 removeShortcutsForPackage(String packageName) {
         final ArrayList<View> childrenToRemove = new ArrayList<View>();
@@ -1138,7 +1217,13 @@
                 Object tag = view.getTag();
                 if (tag instanceof ApplicationInfo) {
                     ApplicationInfo info = (ApplicationInfo) tag;
-                    if (packageName.equals(info.intent.getComponent().getPackageName())) {
+                    // We need to check for ACTION_MAIN otherwise getComponent() might
+                    // return null for some shortcuts (for instance, for shortcuts to
+                    // web pages.)
+                    final Intent intent = info.intent;
+                    final ComponentName name = intent.getComponent();
+                    if (Intent.ACTION_MAIN.equals(intent.getAction()) &&
+                            name != null && packageName.equals(name.getPackageName())) {
                         model.removeDesktopItem(info);
                         LauncherModel.deleteItemFromDatabase(mLauncher, info);
                         childrenToRemove.add(view);
@@ -1155,6 +1240,10 @@
             }
         }
     }
+    
+    // TODO: remove widgets when appwidgetmanager tells us they're gone
+//    void removeAppWidgetsForProvider() {
+//    }
 
     void moveToDefaultScreen() {
         snapToScreen(mDefaultScreen);