resolved conflicts for merge of 35e79e32 to gingerbread-plus-aosp

Change-Id: I039355b9d5852436c8352efc5a85590e5f2bc080
diff --git a/Android.mk b/Android.mk
index dc72ec6..5285e18 100644
--- a/Android.mk
+++ b/Android.mk
@@ -28,6 +28,6 @@
 
 LOCAL_OVERRIDES_PACKAGES := Home
 
-LOCAL_PROGUARD_FLAGS := -include $(LOCAL_PATH)/proguard.flags
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 
 include $(BUILD_PACKAGE)
diff --git a/print_db.py b/print_db.py
new file mode 100755
index 0000000..ebcba6c
--- /dev/null
+++ b/print_db.py
@@ -0,0 +1,220 @@
+#!/usr/bin/env python2.5
+
+import cgi
+import os
+import shutil
+import sys
+import sqlite3
+
+SCREENS = 5
+COLUMNS = 4
+ROWS = 4
+CELL_SIZE = 110
+
+DIR = "db_files"
+AUTO_FILE = DIR + "/launcher.db"
+INDEX_FILE = DIR + "/index.html"
+
+def usage():
+  print "usage: print_db.py launcher.db -- prints a launcher.db"
+  print "usage: print_db.py -- adb pulls a launcher.db from a device"
+  print "       and prints it"
+  print
+  print "The dump will be created in a directory called db_files in cwd."
+  print "This script will delete any db_files directory you have now"
+
+
+def make_dir():
+  shutil.rmtree(DIR, True)
+  os.makedirs(DIR)
+
+def pull_file(fn):
+  print "pull_file: " + fn
+  rv = os.system("adb pull"
+    + " /data/data/com.android.launcher/databases/launcher.db"
+    + " " + fn);
+  if rv != 0:
+    print "adb pull failed"
+    sys.exit(1)
+
+def get_favorites(conn):
+  c = conn.cursor()
+  c.execute("SELECT * FROM favorites")
+  columns = [d[0] for d in c.description]
+  rows = []
+  for row in c:
+    rows.append(row)
+  return columns,rows
+
+def print_intent(out, id, i, cell):
+  if cell:
+    out.write("""<span class="intent" title="%s">shortcut</span>""" % (
+        cgi.escape(cell, True)
+      ))
+
+
+def print_icon(out, id, i, cell):
+  if cell:
+    icon_fn = "icon_%d.png" % id
+    out.write("""<img src="%s">""" % ( icon_fn ))
+    f = file(DIR + "/" + icon_fn, "w")
+    f.write(cell)
+    f.close()
+
+def print_cell(out, id, i, cell):
+  if not cell is None:
+    out.write(cgi.escape(str(cell)))
+
+FUNCTIONS = {
+  "intent": print_intent,
+  "icon": print_icon
+}
+
+def process_file(fn):
+  print "process_file: " + fn
+  conn = sqlite3.connect(fn)
+  columns,rows = get_favorites(conn)
+  data = [dict(zip(columns,row)) for row in rows]
+
+  out = file(INDEX_FILE, "w")
+  out.write("""<html>
+<head>
+<style type="text/css">
+.intent {
+  font-style: italic;
+}
+</style>
+</head>
+<body>
+""")
+
+  # Data table
+  out.write("<b>Favorites table</b><br/>\n")
+  out.write("""<html>
+<table border=1 cellspacing=0 cellpadding=4>
+<tr>
+""")
+  print_functions = []
+  for col in columns:
+    print_functions.append(FUNCTIONS.get(col, print_cell))
+  for i in range(0,len(columns)):
+    col = columns[i]
+    out.write("""  <th>%s</th>
+""" % ( col ))
+  out.write("""
+</tr>
+""")
+  for row in rows:
+    out.write("""<tr>
+""")
+    for i in range(0,len(row)):
+      cell = row[i]
+      # row[0] is always _id
+      out.write("""  <td>""")
+      print_functions[i](out, row[0], row, cell)
+      out.write("""</td>
+""")
+    out.write("""</tr>
+""")
+  out.write("""</table>
+""")
+
+  # Pages
+  screens = []
+  for i in range(0,SCREENS):
+    screen = []
+    for j in range(0,ROWS):
+      m = []
+      for k in range(0,COLUMNS):
+        m.append(None)
+      screen.append(m)
+    screens.append(screen)
+  occupied = "occupied"
+  for row in data:
+    screen = screens[row["screen"]]
+    # desktop
+    if row["container"] != -100:
+      continue
+    cellX = row["cellX"]
+    cellY = row["cellY"]
+    spanX = row["spanX"]
+    spanY = row["spanY"]
+    for j in range(cellY, cellY+spanY):
+      for k in range(cellX, cellX+spanX):
+        screen[j][k] = occupied
+    screen[cellY][cellX] = row
+  i=0
+  for screen in screens:
+    out.write("<br/><b>Screen %d</b><br/>\n" % i)
+    out.write("<table class=layout border=1 cellspacing=0 cellpadding=4>\n")
+    for m in screen:
+      out.write("  <tr>\n")
+      for cell in m:
+        if cell is None:
+          out.write("    <td width=%d height=%d></td>\n" %
+              (CELL_SIZE, CELL_SIZE))
+        elif cell == occupied:
+          pass
+        else:
+          cellX = cell["cellX"]
+          cellY = cell["cellY"]
+          spanX = cell["spanX"]
+          spanY = cell["spanY"]
+          intent = cell["intent"]
+          if intent:
+            title = "title=\"%s\"" % cgi.escape(cell["intent"], True)
+          else:
+            title = ""
+          out.write(("    <td colspan=%d rowspan=%d width=%d height=%d"
+              + " bgcolor=#dddddd align=center valign=middle %s>") % (
+                spanX, spanY,
+                (CELL_SIZE*spanX), (CELL_SIZE*spanY),
+                title))
+          itemType = cell["itemType"]
+          if itemType == 0:
+            out.write("""<img src="icon_%d.png">\n""" % ( cell["_id"] ))
+            out.write("<br/>\n")
+            out.write(cgi.escape(cell["title"]) + " <br/><i>(app)</i>")
+          elif itemType == 1:
+            out.write("""<img src="icon_%d.png">\n""" % ( cell["_id"] ))
+            out.write("<br/>\n")
+            out.write(cgi.escape(cell["title"]) + " <br/><i>(shortcut)</i>")
+          elif itemType == 2:
+            out.write("""<i>folder</i>""")
+          elif itemType == 3:
+            out.write("""<i>live folder</i>""")
+          elif itemType == 4:
+            out.write("<i>widget %d</i><br/>\n" % cell["appWidgetId"])
+          elif itemType == 1000:
+            out.write("""<i>clock</i>""")
+          elif itemType == 1001:
+            out.write("""<i>search</i>""")
+          elif itemType == 1002:
+            out.write("""<i>photo frame</i>""")
+          else:
+            out.write("<b>unknown type: %d</b>" % itemType)
+          out.write("</td>\n")
+      out.write("</tr>\n")
+    out.write("</table>\n")
+    i=i+1
+
+  out.write("""
+</body>
+</html>
+""")
+
+  out.close()
+
+def main(argv):
+  if len(argv) == 1:
+    make_dir()
+    pull_file(AUTO_FILE)
+    process_file(AUTO_FILE)
+  elif len(argv) == 2:
+    make_dir()
+    process_file(argv[1])
+  else:
+    usage()
+
+if __name__=="__main__":
+  main(sys.argv)
diff --git a/res/drawable-hdpi/all_apps_button_focused.png b/res/drawable-hdpi/all_apps_button_focused.png
index 55574ed..ace493b 100644
--- a/res/drawable-hdpi/all_apps_button_focused.png
+++ b/res/drawable-hdpi/all_apps_button_focused.png
Binary files differ
diff --git a/res/drawable-hdpi/all_apps_button_normal.png b/res/drawable-hdpi/all_apps_button_normal.png
index 7e7ec86..cde455d 100644
--- a/res/drawable-hdpi/all_apps_button_normal.png
+++ b/res/drawable-hdpi/all_apps_button_normal.png
Binary files differ
diff --git a/res/drawable-hdpi/all_apps_button_pressed.png b/res/drawable-hdpi/all_apps_button_pressed.png
index 3ccb5af..d5f9f54 100644
--- a/res/drawable-hdpi/all_apps_button_pressed.png
+++ b/res/drawable-hdpi/all_apps_button_pressed.png
Binary files differ
diff --git a/res/drawable-hdpi/hotseat_bg_center.9.png b/res/drawable-hdpi/hotseat_bg_center.9.png
index 468e766..50a9521 100644
--- a/res/drawable-hdpi/hotseat_bg_center.9.png
+++ b/res/drawable-hdpi/hotseat_bg_center.9.png
Binary files differ
diff --git a/res/drawable-hdpi/hotseat_bg_left.9.png b/res/drawable-hdpi/hotseat_bg_left.9.png
index 433a10e..78cbaf9 100644
--- a/res/drawable-hdpi/hotseat_bg_left.9.png
+++ b/res/drawable-hdpi/hotseat_bg_left.9.png
Binary files differ
diff --git a/res/drawable-hdpi/hotseat_bg_right.9.png b/res/drawable-hdpi/hotseat_bg_right.9.png
index 4ea2a73..6cf8ead 100644
--- a/res/drawable-hdpi/hotseat_bg_right.9.png
+++ b/res/drawable-hdpi/hotseat_bg_right.9.png
Binary files differ
diff --git a/res/drawable-hdpi/hotseat_browser_focused.png b/res/drawable-hdpi/hotseat_browser_focused.png
index 6717ad2..4020a89 100644
--- a/res/drawable-hdpi/hotseat_browser_focused.png
+++ b/res/drawable-hdpi/hotseat_browser_focused.png
Binary files differ
diff --git a/res/drawable-hdpi/hotseat_browser_normal.png b/res/drawable-hdpi/hotseat_browser_normal.png
index d02fdd9..c7f902a 100644
--- a/res/drawable-hdpi/hotseat_browser_normal.png
+++ b/res/drawable-hdpi/hotseat_browser_normal.png
Binary files differ
diff --git a/res/drawable-hdpi/hotseat_browser_pressed.png b/res/drawable-hdpi/hotseat_browser_pressed.png
index 71df2d1..bfa23b3 100644
--- a/res/drawable-hdpi/hotseat_browser_pressed.png
+++ b/res/drawable-hdpi/hotseat_browser_pressed.png
Binary files differ
diff --git a/res/drawable-hdpi/hotseat_left.png b/res/drawable-hdpi/hotseat_left.png
deleted file mode 100644
index 5dabf57e..0000000
--- a/res/drawable-hdpi/hotseat_left.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/hotseat_phone_focused.png b/res/drawable-hdpi/hotseat_phone_focused.png
index 3e84a58..f81f0a8 100644
--- a/res/drawable-hdpi/hotseat_phone_focused.png
+++ b/res/drawable-hdpi/hotseat_phone_focused.png
Binary files differ
diff --git a/res/drawable-hdpi/hotseat_phone_normal.png b/res/drawable-hdpi/hotseat_phone_normal.png
index e8a869c..391802e 100644
--- a/res/drawable-hdpi/hotseat_phone_normal.png
+++ b/res/drawable-hdpi/hotseat_phone_normal.png
Binary files differ
diff --git a/res/drawable-hdpi/hotseat_phone_pressed.png b/res/drawable-hdpi/hotseat_phone_pressed.png
index dc4ad6e..a6c2baf 100644
--- a/res/drawable-hdpi/hotseat_phone_pressed.png
+++ b/res/drawable-hdpi/hotseat_phone_pressed.png
Binary files differ
diff --git a/res/drawable-hdpi/hotseat_right.png b/res/drawable-hdpi/hotseat_right.png
deleted file mode 100644
index 114bcb5..0000000
--- a/res/drawable-hdpi/hotseat_right.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/hotseat_browser_normal.png b/res/drawable-mdpi/hotseat_browser_normal.png
index 4c662fd..4a4a6e3 100644
--- a/res/drawable-mdpi/hotseat_browser_normal.png
+++ b/res/drawable-mdpi/hotseat_browser_normal.png
Binary files differ
diff --git a/res/drawable-mdpi/hotseat_phone_normal.png b/res/drawable-mdpi/hotseat_phone_normal.png
index d759b1f..7f20428 100644
--- a/res/drawable-mdpi/hotseat_phone_normal.png
+++ b/res/drawable-mdpi/hotseat_phone_normal.png
Binary files differ
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index bad2730..3c16bbb 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -46,10 +46,14 @@
     <string name="shortcut_uninstalled" msgid="2129499669449749995">"Zástupce <xliff:g id="NAME">%s</xliff:g> byl odebrán."</string>
     <string name="shortcut_duplicate" msgid="4757756326465060694">"Zástupce <xliff:g id="NAME">%s</xliff:g> již existuje."</string>
     <string name="title_select_shortcut" msgid="2858897527672831763">"Vyberte zástupce"</string>
+    <!-- no translation found for title_select_application (8031072293115454221) -->
+    <skip />
     <string name="title_select_live_folder" msgid="3753447798805166749">"Vybrat složku"</string>
     <string name="all_apps_button_label" msgid="3953036962111614813">"Všechny aplikace"</string>
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Plocha"</string>
     <string name="menu_add" msgid="3065046628354640854">"Přidat"</string>
+    <!-- no translation found for menu_manage_apps (2864265778240958621) -->
+    <skip />
     <string name="menu_wallpaper" msgid="5837429080911269832">"Tapeta"</string>
     <string name="menu_search" msgid="4826514464423239041">"Hledat"</string>
     <string name="menu_notifications" msgid="6424587053194766192">"Oznámení"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 6ec1516..9c36d73 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -46,10 +46,14 @@
     <string name="shortcut_uninstalled" msgid="2129499669449749995">"Genvejen \"<xliff:g id="NAME">%s</xliff:g>\" blev fjernet."</string>
     <string name="shortcut_duplicate" msgid="4757756326465060694">"Genvejen \"<xliff:g id="NAME">%s</xliff:g>\" findes allerede."</string>
     <string name="title_select_shortcut" msgid="2858897527672831763">"Vælg genvej"</string>
+    <!-- no translation found for title_select_application (8031072293115454221) -->
+    <skip />
     <string name="title_select_live_folder" msgid="3753447798805166749">"Vælg mappe"</string>
     <string name="all_apps_button_label" msgid="3953036962111614813">"Alle programmer"</string>
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Start"</string>
     <string name="menu_add" msgid="3065046628354640854">"Tilføj"</string>
+    <!-- no translation found for menu_manage_apps (2864265778240958621) -->
+    <skip />
     <string name="menu_wallpaper" msgid="5837429080911269832">"Tapet"</string>
     <string name="menu_search" msgid="4826514464423239041">"Søg"</string>
     <string name="menu_notifications" msgid="6424587053194766192">"Meddelelser"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index e4f705e..b3c85c7 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -46,10 +46,14 @@
     <string name="shortcut_uninstalled" msgid="2129499669449749995">"\"<xliff:g id="NAME">%s</xliff:g>\"-Verknüpfung wurde entfernt."</string>
     <string name="shortcut_duplicate" msgid="4757756326465060694">"\"<xliff:g id="NAME">%s</xliff:g>\"-Verknüpfung ist bereits vorhanden."</string>
     <string name="title_select_shortcut" msgid="2858897527672831763">"Tastenkürzel auswählen"</string>
+    <!-- no translation found for title_select_application (8031072293115454221) -->
+    <skip />
     <string name="title_select_live_folder" msgid="3753447798805166749">"Ordner auswählen"</string>
     <string name="all_apps_button_label" msgid="3953036962111614813">"Alle Anwendungen"</string>
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Startseite"</string>
     <string name="menu_add" msgid="3065046628354640854">"Hinzufügen"</string>
+    <!-- no translation found for menu_manage_apps (2864265778240958621) -->
+    <skip />
     <string name="menu_wallpaper" msgid="5837429080911269832">"Hintergrund"</string>
     <string name="menu_search" msgid="4826514464423239041">"Suchen"</string>
     <string name="menu_notifications" msgid="6424587053194766192">"Benachrichtigungen"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 6514964..a0b3e7a 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -46,10 +46,14 @@
     <string name="shortcut_uninstalled" msgid="2129499669449749995">"Η συντόμευση \"<xliff:g id="NAME">%s</xliff:g>\" καταργήθηκε."</string>
     <string name="shortcut_duplicate" msgid="4757756326465060694">"Η συντόμευση \"<xliff:g id="NAME">%s</xliff:g>\" υπάρχει ήδη."</string>
     <string name="title_select_shortcut" msgid="2858897527672831763">"Επιλογή συντόμευσης"</string>
+    <!-- no translation found for title_select_application (8031072293115454221) -->
+    <skip />
     <string name="title_select_live_folder" msgid="3753447798805166749">"Επιλογή φακέλου"</string>
     <string name="all_apps_button_label" msgid="3953036962111614813">"Όλες οι εφαρμογές"</string>
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Αρχική σελίδα"</string>
     <string name="menu_add" msgid="3065046628354640854">"Προσθήκη"</string>
+    <!-- no translation found for menu_manage_apps (2864265778240958621) -->
+    <skip />
     <string name="menu_wallpaper" msgid="5837429080911269832">"Ταπετσαρία"</string>
     <string name="menu_search" msgid="4826514464423239041">"Αναζήτηση"</string>
     <string name="menu_notifications" msgid="6424587053194766192">"Ειδοποιήσεις"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 5f6b4e5..79fc72d 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -46,10 +46,14 @@
     <string name="shortcut_uninstalled" msgid="2129499669449749995">"El acceso directo \"<xliff:g id="NAME">%s</xliff:g>\" ha sido eliminado."</string>
     <string name="shortcut_duplicate" msgid="4757756326465060694">"El acceso directo \"<xliff:g id="NAME">%s</xliff:g>\" ya existe."</string>
     <string name="title_select_shortcut" msgid="2858897527672831763">"Seleccionar acceso directo"</string>
+    <!-- no translation found for title_select_application (8031072293115454221) -->
+    <skip />
     <string name="title_select_live_folder" msgid="3753447798805166749">"Seleccionar carpeta"</string>
     <string name="all_apps_button_label" msgid="3953036962111614813">"Todas las aplicaciones"</string>
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Página principal"</string>
     <string name="menu_add" msgid="3065046628354640854">"Agregar"</string>
+    <!-- no translation found for menu_manage_apps (2864265778240958621) -->
+    <skip />
     <string name="menu_wallpaper" msgid="5837429080911269832">"Papel tapiz"</string>
     <string name="menu_search" msgid="4826514464423239041">"Buscar"</string>
     <string name="menu_notifications" msgid="6424587053194766192">"Notificaciones"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 5b46c59..4fd5c3d 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -46,10 +46,14 @@
     <string name="shortcut_uninstalled" msgid="2129499669449749995">"Se ha eliminado el acceso directo \"<xliff:g id="NAME">%s</xliff:g>\"."</string>
     <string name="shortcut_duplicate" msgid="4757756326465060694">"El acceso directo \"<xliff:g id="NAME">%s</xliff:g>\" ya existe."</string>
     <string name="title_select_shortcut" msgid="2858897527672831763">"Seleccionar acceso directo"</string>
+    <!-- no translation found for title_select_application (8031072293115454221) -->
+    <skip />
     <string name="title_select_live_folder" msgid="3753447798805166749">"Seleccionar carpeta"</string>
     <string name="all_apps_button_label" msgid="3953036962111614813">"Todas las aplicaciones"</string>
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Inicio"</string>
     <string name="menu_add" msgid="3065046628354640854">"Añadir"</string>
+    <!-- no translation found for menu_manage_apps (2864265778240958621) -->
+    <skip />
     <string name="menu_wallpaper" msgid="5837429080911269832">"Fondo de pantalla"</string>
     <string name="menu_search" msgid="4826514464423239041">"Buscar con Google"</string>
     <string name="menu_notifications" msgid="6424587053194766192">"Notificaciones"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index f1fb440..1799377 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -46,10 +46,14 @@
     <string name="shortcut_uninstalled" msgid="2129499669449749995">"Le raccourci \"<xliff:g id="NAME">%s</xliff:g>\" a été supprimé."</string>
     <string name="shortcut_duplicate" msgid="4757756326465060694">"Le raccourci \"<xliff:g id="NAME">%s</xliff:g>\" existe déjà."</string>
     <string name="title_select_shortcut" msgid="2858897527672831763">"Sélectionner un raccourci"</string>
+    <!-- no translation found for title_select_application (8031072293115454221) -->
+    <skip />
     <string name="title_select_live_folder" msgid="3753447798805166749">"Sélectionner le dossier"</string>
     <string name="all_apps_button_label" msgid="3953036962111614813">"Toutes les applications"</string>
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Page d\'accueil"</string>
     <string name="menu_add" msgid="3065046628354640854">"Ajouter"</string>
+    <!-- no translation found for menu_manage_apps (2864265778240958621) -->
+    <skip />
     <string name="menu_wallpaper" msgid="5837429080911269832">"Fond d\'écran"</string>
     <string name="menu_search" msgid="4826514464423239041">"Rechercher"</string>
     <string name="menu_notifications" msgid="6424587053194766192">"Notifications"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 950e15f..62e91b5 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -46,16 +46,20 @@
     <string name="shortcut_uninstalled" msgid="2129499669449749995">"La scorciatoia \"<xliff:g id="NAME">%s</xliff:g>\" è stata rimossa."</string>
     <string name="shortcut_duplicate" msgid="4757756326465060694">"Scorciatoia \"<xliff:g id="NAME">%s</xliff:g>\" già presente."</string>
     <string name="title_select_shortcut" msgid="2858897527672831763">"Seleziona scorciatoia"</string>
+    <!-- no translation found for title_select_application (8031072293115454221) -->
+    <skip />
     <string name="title_select_live_folder" msgid="3753447798805166749">"Seleziona cartella"</string>
     <string name="all_apps_button_label" msgid="3953036962111614813">"Tutte le applicazioni"</string>
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Home"</string>
     <string name="menu_add" msgid="3065046628354640854">"Aggiungi"</string>
+    <!-- no translation found for menu_manage_apps (2864265778240958621) -->
+    <skip />
     <string name="menu_wallpaper" msgid="5837429080911269832">"Sfondo"</string>
     <string name="menu_search" msgid="4826514464423239041">"Cerca"</string>
     <string name="menu_notifications" msgid="6424587053194766192">"Notifiche"</string>
     <string name="menu_gestures" msgid="514678675575912237">"Gesti"</string>
     <string name="menu_settings" msgid="6233960148378443661">"Impostazioni"</string>
-    <string name="permlab_install_shortcut" msgid="1201690825493376489">"aggiungere scorciatorie"</string>
+    <string name="permlab_install_shortcut" msgid="1201690825493376489">"aggiungere scorciatoie"</string>
     <string name="permdesc_install_shortcut" msgid="7429365847558984148">"Consente a un\'applicazione di aggiungere scorciatoie automaticamente."</string>
     <string name="permlab_uninstall_shortcut" msgid="7696645932555926449">"eliminare scorciatoie"</string>
     <string name="permdesc_uninstall_shortcut" msgid="959972195916090900">"Consente a un\'applicazione di rimuovere scorciatoie automaticamente."</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 021b895..6b09d00 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -46,10 +46,14 @@
     <string name="shortcut_uninstalled" msgid="2129499669449749995">"ショートカット「<xliff:g id="NAME">%s</xliff:g>」を削除しました。"</string>
     <string name="shortcut_duplicate" msgid="4757756326465060694">"ショートカット「<xliff:g id="NAME">%s</xliff:g>」は既に存在します。"</string>
     <string name="title_select_shortcut" msgid="2858897527672831763">"ショートカットを選択"</string>
+    <!-- no translation found for title_select_application (8031072293115454221) -->
+    <skip />
     <string name="title_select_live_folder" msgid="3753447798805166749">"フォルダの選択"</string>
     <string name="all_apps_button_label" msgid="3953036962111614813">"すべてのアプリケーション"</string>
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"ホーム"</string>
     <string name="menu_add" msgid="3065046628354640854">"追加"</string>
+    <!-- no translation found for menu_manage_apps (2864265778240958621) -->
+    <skip />
     <string name="menu_wallpaper" msgid="5837429080911269832">"壁紙"</string>
     <string name="menu_search" msgid="4826514464423239041">"検索"</string>
     <string name="menu_notifications" msgid="6424587053194766192">"通知"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 341c7ee..db32a02 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -46,10 +46,14 @@
     <string name="shortcut_uninstalled" msgid="2129499669449749995">"바로가기(\'<xliff:g id="NAME">%s</xliff:g>\')가 삭제되었습니다."</string>
     <string name="shortcut_duplicate" msgid="4757756326465060694">"바로가기(\'<xliff:g id="NAME">%s</xliff:g>\')가 이미 있습니다."</string>
     <string name="title_select_shortcut" msgid="2858897527672831763">"바로가기 선택"</string>
+    <!-- no translation found for title_select_application (8031072293115454221) -->
+    <skip />
     <string name="title_select_live_folder" msgid="3753447798805166749">"폴더 선택"</string>
     <string name="all_apps_button_label" msgid="3953036962111614813">"모든 애플리케이션"</string>
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"홈"</string>
     <string name="menu_add" msgid="3065046628354640854">"추가"</string>
+    <!-- no translation found for menu_manage_apps (2864265778240958621) -->
+    <skip />
     <string name="menu_wallpaper" msgid="5837429080911269832">"배경화면"</string>
     <string name="menu_search" msgid="4826514464423239041">"검색"</string>
     <string name="menu_notifications" msgid="6424587053194766192">"알림"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 54418e1..4510fe9 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -46,10 +46,14 @@
     <string name="shortcut_uninstalled" msgid="2129499669449749995">"Fjernet snarveien «<xliff:g id="NAME">%s</xliff:g>»."</string>
     <string name="shortcut_duplicate" msgid="4757756326465060694">"Snarveien «<xliff:g id="NAME">%s</xliff:g>» fins allerede."</string>
     <string name="title_select_shortcut" msgid="2858897527672831763">"Velg snarvei"</string>
+    <!-- no translation found for title_select_application (8031072293115454221) -->
+    <skip />
     <string name="title_select_live_folder" msgid="3753447798805166749">"Velg mappe"</string>
     <string name="all_apps_button_label" msgid="3953036962111614813">"Alle programmer"</string>
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Startsiden"</string>
     <string name="menu_add" msgid="3065046628354640854">"Legg til"</string>
+    <!-- no translation found for menu_manage_apps (2864265778240958621) -->
+    <skip />
     <string name="menu_wallpaper" msgid="5837429080911269832">"Bakgrunnsbilde"</string>
     <string name="menu_search" msgid="4826514464423239041">"Søk"</string>
     <string name="menu_notifications" msgid="6424587053194766192">"Varslinger"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index f5ac90e..6caae8b 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -46,10 +46,14 @@
     <string name="shortcut_uninstalled" msgid="2129499669449749995">"Snelkoppeling \'<xliff:g id="NAME">%s</xliff:g>\' is verwijderd."</string>
     <string name="shortcut_duplicate" msgid="4757756326465060694">"Snelkoppeling \'<xliff:g id="NAME">%s</xliff:g>\' bestaat al."</string>
     <string name="title_select_shortcut" msgid="2858897527672831763">"Snelkoppeling selecteren"</string>
+    <!-- no translation found for title_select_application (8031072293115454221) -->
+    <skip />
     <string name="title_select_live_folder" msgid="3753447798805166749">"Map selecteren"</string>
     <string name="all_apps_button_label" msgid="3953036962111614813">"Alle toepassingen"</string>
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Startpagina"</string>
     <string name="menu_add" msgid="3065046628354640854">"Toevoegen"</string>
+    <!-- no translation found for menu_manage_apps (2864265778240958621) -->
+    <skip />
     <string name="menu_wallpaper" msgid="5837429080911269832">"Achtergrond"</string>
     <string name="menu_search" msgid="4826514464423239041">"Zoeken"</string>
     <string name="menu_notifications" msgid="6424587053194766192">"Meldingen"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index d50a59a..0d8e351 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -46,10 +46,14 @@
     <string name="shortcut_uninstalled" msgid="2129499669449749995">"Skrót „<xliff:g id="NAME">%s</xliff:g>” został usunięty."</string>
     <string name="shortcut_duplicate" msgid="4757756326465060694">"Skrót „<xliff:g id="NAME">%s</xliff:g>” już istnieje."</string>
     <string name="title_select_shortcut" msgid="2858897527672831763">"Wybierz skrót"</string>
+    <!-- no translation found for title_select_application (8031072293115454221) -->
+    <skip />
     <string name="title_select_live_folder" msgid="3753447798805166749">"Wybierz folder"</string>
     <string name="all_apps_button_label" msgid="3953036962111614813">"Wszystkie aplikacje"</string>
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Ekran główny"</string>
     <string name="menu_add" msgid="3065046628354640854">"Dodaj"</string>
+    <!-- no translation found for menu_manage_apps (2864265778240958621) -->
+    <skip />
     <string name="menu_wallpaper" msgid="5837429080911269832">"Tapeta"</string>
     <string name="menu_search" msgid="4826514464423239041">"Szukaj"</string>
     <string name="menu_notifications" msgid="6424587053194766192">"Powiadomienia"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index df63f30..824d18a 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -46,10 +46,14 @@
     <string name="shortcut_uninstalled" msgid="2129499669449749995">"O atalho \"<xliff:g id="NAME">%s</xliff:g>\" foi removido."</string>
     <string name="shortcut_duplicate" msgid="4757756326465060694">"O atalho \"<xliff:g id="NAME">%s</xliff:g>\" já existe."</string>
     <string name="title_select_shortcut" msgid="2858897527672831763">"Seleccione o atalho"</string>
+    <!-- no translation found for title_select_application (8031072293115454221) -->
+    <skip />
     <string name="title_select_live_folder" msgid="3753447798805166749">"Seleccione a pasta"</string>
     <string name="all_apps_button_label" msgid="3953036962111614813">"Todas as aplicações"</string>
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Página inicial"</string>
     <string name="menu_add" msgid="3065046628354640854">"Adicionar"</string>
+    <!-- no translation found for menu_manage_apps (2864265778240958621) -->
+    <skip />
     <string name="menu_wallpaper" msgid="5837429080911269832">"Imagem de fundo"</string>
     <string name="menu_search" msgid="4826514464423239041">"Pesquisar"</string>
     <string name="menu_notifications" msgid="6424587053194766192">"Notificações"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index b72f7e0..dc6441d 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -46,10 +46,14 @@
     <string name="shortcut_uninstalled" msgid="2129499669449749995">"O atalho \"<xliff:g id="NAME">%s</xliff:g>\" foi removido."</string>
     <string name="shortcut_duplicate" msgid="4757756326465060694">"O atalho \"<xliff:g id="NAME">%s</xliff:g>\" já existe."</string>
     <string name="title_select_shortcut" msgid="2858897527672831763">"Selecionar atalho"</string>
+    <!-- no translation found for title_select_application (8031072293115454221) -->
+    <skip />
     <string name="title_select_live_folder" msgid="3753447798805166749">"Selecionar pasta"</string>
     <string name="all_apps_button_label" msgid="3953036962111614813">"Todos os aplicativos"</string>
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Página inicial"</string>
     <string name="menu_add" msgid="3065046628354640854">"Adicionar"</string>
+    <!-- no translation found for menu_manage_apps (2864265778240958621) -->
+    <skip />
     <string name="menu_wallpaper" msgid="5837429080911269832">"Plano de fundo"</string>
     <string name="menu_search" msgid="4826514464423239041">"Pesquisa"</string>
     <string name="menu_notifications" msgid="6424587053194766192">"Notificações"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index b151618..2454b58 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -46,10 +46,14 @@
     <string name="shortcut_uninstalled" msgid="2129499669449749995">"Ярлык \"<xliff:g id="NAME">%s</xliff:g>\" удален."</string>
     <string name="shortcut_duplicate" msgid="4757756326465060694">"Ярлык \"<xliff:g id="NAME">%s</xliff:g>\" уже существует."</string>
     <string name="title_select_shortcut" msgid="2858897527672831763">"Выберите ярлык"</string>
+    <!-- no translation found for title_select_application (8031072293115454221) -->
+    <skip />
     <string name="title_select_live_folder" msgid="3753447798805166749">"Выбор папки"</string>
     <string name="all_apps_button_label" msgid="3953036962111614813">"Все приложения"</string>
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Главная"</string>
     <string name="menu_add" msgid="3065046628354640854">"Добавить"</string>
+    <!-- no translation found for menu_manage_apps (2864265778240958621) -->
+    <skip />
     <string name="menu_wallpaper" msgid="5837429080911269832">"Обои"</string>
     <string name="menu_search" msgid="4826514464423239041">"Поиск"</string>
     <string name="menu_notifications" msgid="6424587053194766192">"Уведомления"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 9c29ef1..cf44973 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -46,10 +46,14 @@
     <string name="shortcut_uninstalled" msgid="2129499669449749995">"Genvägen \"<xliff:g id="NAME">%s</xliff:g>\" har tagits bort."</string>
     <string name="shortcut_duplicate" msgid="4757756326465060694">"Genvägen \"<xliff:g id="NAME">%s</xliff:g>\" finns redan."</string>
     <string name="title_select_shortcut" msgid="2858897527672831763">"Välj genväg"</string>
+    <!-- no translation found for title_select_application (8031072293115454221) -->
+    <skip />
     <string name="title_select_live_folder" msgid="3753447798805166749">"Välj mapp"</string>
     <string name="all_apps_button_label" msgid="3953036962111614813">"Alla program"</string>
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Startsida"</string>
     <string name="menu_add" msgid="3065046628354640854">"Lägg till"</string>
+    <!-- no translation found for menu_manage_apps (2864265778240958621) -->
+    <skip />
     <string name="menu_wallpaper" msgid="5837429080911269832">"Bakgrund"</string>
     <string name="menu_search" msgid="4826514464423239041">"Sök"</string>
     <string name="menu_notifications" msgid="6424587053194766192">"Aviseringar"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 43f7520..0e38092 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -46,10 +46,14 @@
     <string name="shortcut_uninstalled" msgid="2129499669449749995">"\"<xliff:g id="NAME">%s</xliff:g>\" kısayolu kaldırıldı."</string>
     <string name="shortcut_duplicate" msgid="4757756326465060694">"\"<xliff:g id="NAME">%s</xliff:g>\" kısayolu zaten var."</string>
     <string name="title_select_shortcut" msgid="2858897527672831763">"Kısayolu seçin"</string>
+    <!-- no translation found for title_select_application (8031072293115454221) -->
+    <skip />
     <string name="title_select_live_folder" msgid="3753447798805166749">"Klasörü seçin"</string>
     <string name="all_apps_button_label" msgid="3953036962111614813">"Tüm uygulamalar"</string>
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"Ana Sayfa"</string>
     <string name="menu_add" msgid="3065046628354640854">"Ekle"</string>
+    <!-- no translation found for menu_manage_apps (2864265778240958621) -->
+    <skip />
     <string name="menu_wallpaper" msgid="5837429080911269832">"Duvar Kağıdı"</string>
     <string name="menu_search" msgid="4826514464423239041">"Ara"</string>
     <string name="menu_notifications" msgid="6424587053194766192">"Bildirimler"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 0705787..5b69a8c 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -46,10 +46,14 @@
     <string name="shortcut_uninstalled" msgid="2129499669449749995">"已删除“<xliff:g id="NAME">%s</xliff:g>”快捷方式。"</string>
     <string name="shortcut_duplicate" msgid="4757756326465060694">"“<xliff:g id="NAME">%s</xliff:g>”快捷方式已存在。"</string>
     <string name="title_select_shortcut" msgid="2858897527672831763">"选择快捷方式"</string>
+    <!-- no translation found for title_select_application (8031072293115454221) -->
+    <skip />
     <string name="title_select_live_folder" msgid="3753447798805166749">"选择文件夹"</string>
     <string name="all_apps_button_label" msgid="3953036962111614813">"所有应用程序"</string>
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"主屏幕"</string>
     <string name="menu_add" msgid="3065046628354640854">"添加"</string>
+    <!-- no translation found for menu_manage_apps (2864265778240958621) -->
+    <skip />
     <string name="menu_wallpaper" msgid="5837429080911269832">"壁纸"</string>
     <string name="menu_search" msgid="4826514464423239041">"搜索"</string>
     <string name="menu_notifications" msgid="6424587053194766192">"通知"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index de37e19..3068335 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -46,10 +46,14 @@
     <string name="shortcut_uninstalled" msgid="2129499669449749995">"已移除「<xliff:g id="NAME">%s</xliff:g>」捷徑。"</string>
     <string name="shortcut_duplicate" msgid="4757756326465060694">"「<xliff:g id="NAME">%s</xliff:g>」捷徑已經存在。"</string>
     <string name="title_select_shortcut" msgid="2858897527672831763">"選取捷徑"</string>
+    <!-- no translation found for title_select_application (8031072293115454221) -->
+    <skip />
     <string name="title_select_live_folder" msgid="3753447798805166749">"選取資料夾"</string>
     <string name="all_apps_button_label" msgid="3953036962111614813">"所有應用程式"</string>
     <string name="all_apps_home_button_label" msgid="1022222300329398558">"主螢幕"</string>
     <string name="menu_add" msgid="3065046628354640854">"新增"</string>
+    <!-- no translation found for menu_manage_apps (2864265778240958621) -->
+    <skip />
     <string name="menu_wallpaper" msgid="5837429080911269832">"桌布"</string>
     <string name="menu_search" msgid="4826514464423239041">"搜尋"</string>
     <string name="menu_notifications" msgid="6424587053194766192">"通知"</string>
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 1fcabb2..6bd16ed 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
     <string-array name="hotseats" translatable="false">
-        <item>intent:#Intent;action=android.intent.action.MAIN;component=com.android.contacts/.ContactsLaunchActivity;end</item>
+        <item>intent:#Intent;action=android.intent.action.DIAL;end</item>
         <item>*BROWSER*</item>
     </string-array>
     <array name="hotseat_icons" translatable="false">
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 790f835..c83986b 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -37,4 +37,8 @@
     
     <!-- delete_zone_size_full - button_bar_height_portrait -->
     <dimen name="delete_zone_padding">14dip</dimen>
+
+    <!-- the area at the edge of the screen that makes the workspace go left
+         or right while you're dragging. -->
+    <dimen name="scroll_zone">20dp</dimen>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 86c1b3c..bae50e4 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -88,6 +88,8 @@
 
     <!-- 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 an application to add to homescreen -->
+    <string name="title_select_application">Select application</string>
     <!-- Title of dialog when user is selecting live folder to add to homescreen -->
     <string name="title_select_live_folder">Select folder</string>
 
@@ -101,6 +103,8 @@
     <skip />
     <!-- Verb, menu item used to add an item on the desktop -->
     <string name="menu_add">Add</string>
+    <!-- Menu item used to manage installed applications -->
+    <string name="menu_manage_apps">Manage Apps</string>
     <!-- Noun, menu item used to set the desktop's wallpaper -->
     <string name="menu_wallpaper">Wallpaper</string>
     <!-- Verb, menu item used to initiate global search -->
diff --git a/src/com/android/launcher2/AllApps3D.java b/src/com/android/launcher2/AllApps3D.java
index b8aa8ec..308ad28 100644
--- a/src/com/android/launcher2/AllApps3D.java
+++ b/src/com/android/launcher2/AllApps3D.java
@@ -726,7 +726,12 @@
     }
 
     public boolean onLongClick(View v) {
-        if (mLocks != 0 || !isVisible()) {
+        // We don't accept long click events in these cases
+        // - If the workspace isn't ready to accept a drop
+        // - If we're not done loading (because we might be confused about which item
+        //   to pick up
+        // - If we're not visible
+        if (!isVisible() || mLauncher.isWorkspaceLocked() || mLocks != 0) {
             return true;
         }
         if (sRollo.checkClickOK() && mCurrentIconIndex == mDownIconIndex
diff --git a/src/com/android/launcher2/AllAppsList.java b/src/com/android/launcher2/AllAppsList.java
index 41aa6ca..3a5baea 100644
--- a/src/com/android/launcher2/AllAppsList.java
+++ b/src/com/android/launcher2/AllAppsList.java
@@ -150,6 +150,17 @@
                     modified.add(applicationInfo);
                 }
             }
+        } else {
+            // Remove all data for this package.
+            for (int i = data.size() - 1; i >= 0; i--) {
+                final ApplicationInfo applicationInfo = data.get(i);
+                final ComponentName component = applicationInfo.intent.getComponent();
+                if (packageName.equals(component.getPackageName())) {
+                    removed.add(applicationInfo);
+                    mIconCache.remove(component);
+                    data.remove(i);
+                }
+            }
         }
     }
 
@@ -161,23 +172,10 @@
 
         final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
         mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+        mainIntent.setPackage(packageName);
 
         final List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0);
-        final List<ResolveInfo> matches = new ArrayList<ResolveInfo>();
-
-        if (apps != null) {
-            // Find all activities that match the packageName
-            int count = apps.size();
-            for (int i = 0; i < count; i++) {
-                final ResolveInfo info = apps.get(i);
-                final ActivityInfo activityInfo = info.activityInfo;
-                if (packageName.equals(activityInfo.packageName)) {
-                    matches.add(info);
-                }
-            }
-        }
-
-        return matches;
+        return apps != null ? apps : new ArrayList<ResolveInfo>();
     }
 
     /**
diff --git a/src/com/android/launcher2/DeleteZone.java b/src/com/android/launcher2/DeleteZone.java
index 3a6c63d..4e8b204 100644
--- a/src/com/android/launcher2/DeleteZone.java
+++ b/src/com/android/launcher2/DeleteZone.java
@@ -116,7 +116,14 @@
             final LauncherAppWidgetInfo launcherAppWidgetInfo = (LauncherAppWidgetInfo) item;
             final LauncherAppWidgetHost appWidgetHost = mLauncher.getAppWidgetHost();
             if (appWidgetHost != null) {
-                appWidgetHost.deleteAppWidgetId(launcherAppWidgetInfo.appWidgetId);
+                final int appWidgetId = launcherAppWidgetInfo.appWidgetId;
+                // Deleting an app widget ID is a void call but writes to disk before returning
+                // to the caller...
+                new Thread("deleteAppWidgetId") {
+                    public void run() {
+                        appWidgetHost.deleteAppWidgetId(launcherAppWidgetInfo.appWidgetId);
+                    }
+                }.start();
             }
         }
         LauncherModel.deleteItemFromDatabase(mLauncher, item);
diff --git a/src/com/android/launcher2/DragController.java b/src/com/android/launcher2/DragController.java
index b4f972b..b453061 100644
--- a/src/com/android/launcher2/DragController.java
+++ b/src/com/android/launcher2/DragController.java
@@ -33,6 +33,8 @@
 
 import java.util.ArrayList;
 
+import com.android.launcher.R;
+
 /**
  * Class for initiating a drag within a view or across multiple views.
  */
@@ -47,7 +49,6 @@
     public static int DRAG_ACTION_COPY = 1;
 
     private static final int SCROLL_DELAY = 600;
-    private static final int SCROLL_ZONE = 20;
     private static final int VIBRATE_DURATION = 35;
 
     private static final boolean PROFILE_DRAWING_DURING_DRAG = false;
@@ -87,6 +88,11 @@
     /** Y offset from the upper-left corner of the cell to where we touched.  */
     private float mTouchOffsetY;
 
+    /** the area at the edge of the screen that makes the workspace go left
+     *   or right while you're dragging.
+     */
+    private int mScrollZone;
+
     /** Where the drag originated */
     private DragSource mDragSource;
 
@@ -147,6 +153,7 @@
     public DragController(Context context) {
         mContext = context;
         mHandler = new Handler();
+        mScrollZone = context.getResources().getDimensionPixelSize(R.dimen.scroll_zone);
     }
 
     /**
@@ -378,7 +385,7 @@
             mMotionDownX = screenX;
             mMotionDownY = screenY;
 
-            if ((screenX < SCROLL_ZONE) || (screenX > scrollView.getWidth() - SCROLL_ZONE)) {
+            if ((screenX < mScrollZone) || (screenX > scrollView.getWidth() - mScrollZone)) {
                 mScrollState = SCROLL_WAITING_IN_ZONE;
                 mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
             } else {
@@ -419,13 +426,15 @@
             if (mDeleteRegion != null) {
                 inDeleteRegion = mDeleteRegion.contains(screenX, screenY);
             }
-            if (!inDeleteRegion && screenX < SCROLL_ZONE) {
+            //Log.d(Launcher.TAG, "inDeleteRegion=" + inDeleteRegion + " screenX=" + screenX
+            //        + " mScrollZone=" + mScrollZone);
+            if (!inDeleteRegion && screenX < mScrollZone) {
                 if (mScrollState == SCROLL_OUTSIDE_ZONE) {
                     mScrollState = SCROLL_WAITING_IN_ZONE;
                     mScrollRunnable.setDirection(SCROLL_LEFT);
                     mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
                 }
-            } else if (!inDeleteRegion && screenX > scrollView.getWidth() - SCROLL_ZONE) {
+            } else if (!inDeleteRegion && screenX > scrollView.getWidth() - mScrollZone) {
                 if (mScrollState == SCROLL_OUTSIDE_ZONE) {
                     mScrollState = SCROLL_WAITING_IN_ZONE;
                     mScrollRunnable.setDirection(SCROLL_RIGHT);
diff --git a/src/com/android/launcher2/IconCache.java b/src/com/android/launcher2/IconCache.java
index 4bb7d08..81a786c 100644
--- a/src/com/android/launcher2/IconCache.java
+++ b/src/com/android/launcher2/IconCache.java
@@ -124,6 +124,10 @@
         }
     }
 
+    public boolean isDefaultIcon(Bitmap icon) {
+        return mDefaultIcon == icon;
+    }
+
     private CacheEntry cacheLocked(ComponentName componentName, ResolveInfo info) {
         CacheEntry entry = mCache.get(componentName);
         if (entry == null) {
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index 2742f2f..96934e8 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -46,6 +46,7 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.ColorDrawable;
 import android.net.Uri;
+import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
@@ -105,7 +106,8 @@
     private static final int MENU_GROUP_WALLPAPER = MENU_GROUP_ADD + 1;
 
     private static final int MENU_ADD = Menu.FIRST + 1;
-    private static final int MENU_WALLPAPER_SETTINGS = MENU_ADD + 1;
+    private static final int MENU_MANAGE_APPS = MENU_ADD + 1;
+    private static final int MENU_WALLPAPER_SETTINGS = MENU_MANAGE_APPS + 1;
     private static final int MENU_SEARCH = MENU_WALLPAPER_SETTINGS + 1;
     private static final int MENU_NOTIFICATIONS = MENU_SEARCH + 1;
     private static final int MENU_SETTINGS = MENU_NOTIFICATIONS + 1;
@@ -199,8 +201,10 @@
     private LauncherModel mModel;
     private IconCache mIconCache;
 
+    private static LocaleConfiguration sLocaleConfiguration = null;
+
     private ArrayList<ItemInfo> mDesktopItems = new ArrayList<ItemInfo>();
-    private static HashMap<Long, FolderInfo> mFolders = new HashMap<Long, FolderInfo>();
+    private static HashMap<Long, FolderInfo> sFolders = new HashMap<Long, FolderInfo>();
 
     private ImageView mPreviousView;
     private ImageView mNextView;
@@ -262,31 +266,51 @@
     }
 
     private void checkForLocaleChange() {
-        final LocaleConfiguration localeConfiguration = new LocaleConfiguration();
-        readConfiguration(this, localeConfiguration);
+        if (sLocaleConfiguration == null) {
+            new AsyncTask<Void, Void, LocaleConfiguration>() {
+                @Override
+                protected LocaleConfiguration doInBackground(Void... unused) {
+                    LocaleConfiguration localeConfiguration = new LocaleConfiguration();
+                    readConfiguration(Launcher.this, localeConfiguration);
+                    return localeConfiguration;
+                }
+
+                @Override
+                protected void onPostExecute(LocaleConfiguration result) {
+                    sLocaleConfiguration = result;
+                    checkForLocaleChange();  // recursive, but now with a locale configuration
+                }
+            }.execute();
+            return;
+        }
 
         final Configuration configuration = getResources().getConfiguration();
 
-        final String previousLocale = localeConfiguration.locale;
+        final String previousLocale = sLocaleConfiguration.locale;
         final String locale = configuration.locale.toString();
 
-        final int previousMcc = localeConfiguration.mcc;
+        final int previousMcc = sLocaleConfiguration.mcc;
         final int mcc = configuration.mcc;
 
-        final int previousMnc = localeConfiguration.mnc;
+        final int previousMnc = sLocaleConfiguration.mnc;
         final int mnc = configuration.mnc;
 
         boolean localeChanged = !locale.equals(previousLocale) || mcc != previousMcc || mnc != previousMnc;
 
         if (localeChanged) {
-            localeConfiguration.locale = locale;
-            localeConfiguration.mcc = mcc;
-            localeConfiguration.mnc = mnc;
+            sLocaleConfiguration.locale = locale;
+            sLocaleConfiguration.mcc = mcc;
+            sLocaleConfiguration.mnc = mnc;
 
-            writeConfiguration(this, localeConfiguration);
             mIconCache.flush();
-
             loadHotseats();
+
+            final LocaleConfiguration localeConfiguration = sLocaleConfiguration;
+            new Thread("WriteLocaleConfiguration") {
+                public void run() {
+                    writeConfiguration(Launcher.this, localeConfiguration);
+                }
+            }.start();
         }
     }
 
@@ -689,7 +713,7 @@
         boolean renameFolder = savedState.getBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, false);
         if (renameFolder) {
             long id = savedState.getLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID);
-            mFolderInfo = mModel.getFolderById(this, mFolders, id);
+            mFolderInfo = mModel.getFolderById(this, sFolders, id);
             mRestoring = true;
         }
     }
@@ -1088,6 +1112,9 @@
         menu.add(MENU_GROUP_ADD, MENU_ADD, 0, R.string.menu_add)
                 .setIcon(android.R.drawable.ic_menu_add)
                 .setAlphabeticShortcut('A');
+        menu.add(0, MENU_MANAGE_APPS, 0, R.string.menu_manage_apps)
+                .setIcon(android.R.drawable.ic_menu_manage)
+                .setAlphabeticShortcut('M');
         menu.add(MENU_GROUP_WALLPAPER, MENU_WALLPAPER_SETTINGS, 0, R.string.menu_wallpaper)
                  .setIcon(android.R.drawable.ic_menu_gallery)
                  .setAlphabeticShortcut('W');
@@ -1139,6 +1166,9 @@
             case MENU_ADD:
                 addItems();
                 return true;
+            case MENU_MANAGE_APPS:
+                manageApps();
+                return true;
             case MENU_WALLPAPER_SETTINGS:
                 startWallpaper();
                 return true;
@@ -1173,6 +1203,10 @@
         showAddDialog(mMenuAddInfo);
     }
 
+    private void manageApps() {
+        startActivity(new Intent(android.provider.Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS));
+    }
+
     void addAppWidget(Intent data) {
         // TODO: catch bad widget exception when sent
         int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
@@ -1202,9 +1236,10 @@
 
             Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
             pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent);
-            startActivityForResult(pickIntent, REQUEST_PICK_APPLICATION);
+            pickIntent.putExtra(Intent.EXTRA_TITLE, getText(R.string.title_select_application));
+            startActivityForResultSafely(pickIntent, REQUEST_PICK_APPLICATION);
         } else {
-            startActivityForResult(intent, REQUEST_CREATE_SHORTCUT);
+            startActivityForResultSafely(intent, REQUEST_CREATE_SHORTCUT);
         }
     }
 
@@ -1216,7 +1251,7 @@
         if (folderName != null && folderName.equals(shortcutName)) {
             addFolder();
         } else {
-            startActivityForResult(intent, REQUEST_CREATE_LIVE_FOLDER);
+            startActivityForResultSafely(intent, REQUEST_CREATE_LIVE_FOLDER);
         }
     }
 
@@ -1232,7 +1267,7 @@
         LauncherModel.addItemToDatabase(this, folderInfo,
                 LauncherSettings.Favorites.CONTAINER_DESKTOP,
                 mWorkspace.getCurrentScreen(), cellInfo.cellX, cellInfo.cellY, false);
-        mFolders.put(folderInfo.id, folderInfo);
+        sFolders.put(folderInfo.id, folderInfo);
 
         // Create the view
         FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
@@ -1242,7 +1277,7 @@
     }
 
     void removeFolder(FolderInfo folder) {
-        mFolders.remove(folder.id);
+        sFolders.remove(folder.id);
     }
 
     private void completeAddLiveFolder(Intent data, CellLayout.CellInfo cellInfo) {
@@ -1297,7 +1332,7 @@
 
         LauncherModel.addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
                 cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify);
-        mFolders.put(info.id, info);
+        sFolders.put(info.id, info);
 
         return info;
     }
@@ -1847,7 +1882,7 @@
             final String name = mInput.getText().toString();
             if (!TextUtils.isEmpty(name)) {
                 // Make sure we have the right folder info
-                mFolderInfo = mFolders.get(mFolderInfo.id);
+                mFolderInfo = sFolders.get(mFolderInfo.id);
                 mFolderInfo.title = name;
                 LauncherModel.updateItemInDatabase(Launcher.this, mFolderInfo);
 
@@ -2172,8 +2207,8 @@
      * Implementation of the method from LauncherModel.Callbacks.
      */
     public void bindFolders(HashMap<Long, FolderInfo> folders) {
-        mFolders.clear();
-        mFolders.putAll(folders);
+        sFolders.clear();
+        sFolders.putAll(folders);
     }
 
     /**
@@ -2226,7 +2261,7 @@
             final long[] userFolders = mSavedState.getLongArray(RUNTIME_STATE_USER_FOLDERS);
             if (userFolders != null) {
                 for (long folderId : userFolders) {
-                    final FolderInfo info = mFolders.get(folderId);
+                    final FolderInfo info = sFolders.get(folderId);
                     if (info != null) {
                         openFolder(info);
                     }
@@ -2283,9 +2318,11 @@
      *
      * Implementation of the method from LauncherModel.Callbacks.
      */
-    public void bindAppsRemoved(ArrayList<ApplicationInfo> apps) {
+    public void bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent) {
         removeDialog(DIALOG_CREATE_SHORTCUT);
-        mWorkspace.removeItems(apps);
+        if (permanent) {
+            mWorkspace.removeItems(apps);
+        }
         mAllAppsGrid.removeApps(apps);
     }
 
@@ -2300,7 +2337,7 @@
         Log.d(TAG, "mWaitingForResult=" + mWaitingForResult);
         Log.d(TAG, "mSavedInstanceState=" + mSavedInstanceState);
         Log.d(TAG, "mDesktopItems.size=" + mDesktopItems.size());
-        Log.d(TAG, "mFolders.size=" + mFolders.size());
+        Log.d(TAG, "sFolders.size=" + sFolders.size());
         mModel.dumpState();
         mAllAppsGrid.dumpState();
         Log.d(TAG, "END launcher2 dump state");
diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java
index 023264d..cb19fe3 100644
--- a/src/com/android/launcher2/LauncherModel.java
+++ b/src/com/android/launcher2/LauncherModel.java
@@ -35,7 +35,8 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.net.Uri;
-import android.os.Environment;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.util.Log;
@@ -47,8 +48,8 @@
 import java.text.Collator;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Comparator;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 
@@ -61,16 +62,22 @@
  */
 public class LauncherModel extends BroadcastReceiver {
     static final boolean DEBUG_LOADERS = false;
-    static final boolean PROFILE_LOADERS = false;
     static final String TAG = "Launcher.Model";
 
+    private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
     private int mBatchSize; // 0 is all apps at once
     private int mAllAppsLoadDelay; // milliseconds between batches
 
     private final LauncherApplication mApp;
     private final Object mLock = new Object();
     private DeferredHandler mHandler = new DeferredHandler();
-    private Loader mLoader = new Loader();
+    private LoaderTask mLoaderTask;
+
+    private static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
+    static {
+        sWorkerThread.start();
+    }
+    private static final Handler sWorker = new Handler(sWorkerThread.getLooper());
 
     // We start off with everything not loaded.  After that, we assume that
     // our monitoring of the package manager provides all updates and we never
@@ -78,12 +85,13 @@
     private boolean mWorkspaceLoaded;
     private boolean mAllAppsLoaded;
 
-    private boolean mBeforeFirstLoad = true; // only access this from main thread
     private WeakReference<Callbacks> mCallbacks;
 
-    private final Object mAllAppsListLock = new Object();
-    private AllAppsList mAllAppsList;
+    private AllAppsList mAllAppsList; // only access in worker thread
     private IconCache mIconCache;
+    final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();
+    final ArrayList<LauncherAppWidgetInfo> mAppWidgets = new ArrayList<LauncherAppWidgetInfo>();
+    final HashMap<Long, FolderInfo> mFolders = new HashMap<Long, FolderInfo>();
 
     private Bitmap mDefaultIcon;
 
@@ -97,7 +105,7 @@
         public void bindAllApplications(ArrayList<ApplicationInfo> apps);
         public void bindAppsAdded(ArrayList<ApplicationInfo> apps);
         public void bindAppsUpdated(ArrayList<ApplicationInfo> apps);
-        public void bindAppsRemoved(ArrayList<ApplicationInfo> apps);
+        public void bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent);
         public boolean isAllAppsVisible();
     }
 
@@ -143,6 +151,7 @@
         item.cellX = cellX;
         item.cellY = cellY;
 
+        final Uri uri = LauncherSettings.Favorites.getContentUri(item.id, false);
         final ContentValues values = new ContentValues();
         final ContentResolver cr = context.getContentResolver();
 
@@ -151,7 +160,11 @@
         values.put(LauncherSettings.Favorites.CELLY, item.cellY);
         values.put(LauncherSettings.Favorites.SCREEN, item.screen);
 
-        cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
+        sWorker.post(new Runnable() {
+                public void run() {
+                    cr.update(uri, values, null, null);
+                }
+            });
     }
 
     /**
@@ -261,8 +274,12 @@
      */
     static void deleteItemFromDatabase(Context context, ItemInfo item) {
         final ContentResolver cr = context.getContentResolver();
-
-        cr.delete(LauncherSettings.Favorites.getContentUri(item.id, false), null, null);
+        final Uri uriToDelete = LauncherSettings.Favorites.getContentUri(item.id, false);
+        sWorker.post(new Runnable() {
+                public void run() {
+                    cr.delete(uriToDelete, null, null);
+                }
+            });
     }
 
     /**
@@ -285,295 +302,179 @@
         }
     }
 
-    public void startLoader(Context context, boolean isLaunching) {
-        mLoader.startLoader(context, isLaunching);
-    }
-
-    public void stopLoader() {
-        mLoader.stopLoader();
-    }
-
     /**
      * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
      * ACTION_PACKAGE_CHANGED.
      */
     public void onReceive(Context context, Intent intent) {
-        // Use the app as the context.
-        context = mApp;
+        if (DEBUG_LOADERS) Log.d(TAG, "onReceive intent=" + intent);
+        
+        final String action = intent.getAction();
 
-        ArrayList<ApplicationInfo> added = null;
-        ArrayList<ApplicationInfo> removed = null;
-        ArrayList<ApplicationInfo> modified = null;
+        if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
+                || Intent.ACTION_PACKAGE_REMOVED.equals(action)
+                || Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+            final String packageName = intent.getData().getSchemeSpecificPart();
+            final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
 
-        if (mBeforeFirstLoad) {
-            // If we haven't even loaded yet, don't bother, since we'll just pick
-            // up the changes.
-            return;
+            int op = PackageUpdatedTask.OP_NONE;
+
+            if (packageName == null || packageName.length() == 0) {
+                // they sent us a bad intent
+                return;
+            }
+
+            if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
+                op = PackageUpdatedTask.OP_UPDATE;
+            } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                if (!replacing) {
+                    op = PackageUpdatedTask.OP_REMOVE;
+                }
+                // else, we are replacing the package, so a PACKAGE_ADDED will be sent
+                // later, we will update the package at this time
+            } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+                if (!replacing) {
+                    op = PackageUpdatedTask.OP_ADD;
+                } else {
+                    op = PackageUpdatedTask.OP_UPDATE;
+                }
+            }
+
+            if (op != PackageUpdatedTask.OP_NONE) {
+                enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName }));
+            }
+
+        } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
+            // First, schedule to add these apps back in.
+            String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+            enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packages));
+            // Then, rebind everything.
+            startLoader(mApp, false);
+
+        } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+            String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+            enqueuePackageUpdated(new PackageUpdatedTask(
+                        PackageUpdatedTask.OP_UNAVAILABLE, packages));
+
         }
+    }
 
-        synchronized (mAllAppsListLock) {
-            final String action = intent.getAction();
+    public void startLoader(Context context, boolean isLaunching) {
+        synchronized (mLock) {
+            if (DEBUG_LOADERS) {
+                Log.d(TAG, "startLoader isLaunching=" + isLaunching);
+            }
 
-            if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
-                    || Intent.ACTION_PACKAGE_REMOVED.equals(action)
-                    || Intent.ACTION_PACKAGE_ADDED.equals(action)) {
-                final String packageName = intent.getData().getSchemeSpecificPart();
-                final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
-
-                if (packageName == null || packageName.length() == 0) {
-                    // they sent us a bad intent
-                    return;
-                }
-
-                if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
-                    mAllAppsList.updatePackage(context, packageName);
-                } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
-                    if (!replacing) {
-                        mAllAppsList.removePackage(packageName);
+            // Don't bother to start the thread if we know it's not going to do anything
+            if (mCallbacks != null && mCallbacks.get() != null) {
+                // If there is already one running, tell it to stop.
+                LoaderTask oldTask = mLoaderTask;
+                if (oldTask != null) {
+                    if (oldTask.isLaunching()) {
+                        // don't downgrade isLaunching if we're already running
+                        isLaunching = true;
                     }
-                    // else, we are replacing the package, so a PACKAGE_ADDED will be sent
-                    // later, we will update the package at this time
-                } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
-                    if (!replacing) {
-                        mAllAppsList.addPackage(context, packageName);
-                    } else {
-                        mAllAppsList.updatePackage(context, packageName);
-                    }
+                    oldTask.stopLocked();
                 }
-
-                if (mAllAppsList.added.size() > 0) {
-                    added = mAllAppsList.added;
-                    mAllAppsList.added = new ArrayList<ApplicationInfo>();
-                }
-                if (mAllAppsList.removed.size() > 0) {
-                    removed = mAllAppsList.removed;
-                    mAllAppsList.removed = new ArrayList<ApplicationInfo>();
-                    for (ApplicationInfo info: removed) {
-                        mIconCache.remove(info.intent.getComponent());
-                    }
-                }
-                if (mAllAppsList.modified.size() > 0) {
-                    modified = mAllAppsList.modified;
-                    mAllAppsList.modified = new ArrayList<ApplicationInfo>();
-                }
-
-                final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
-                if (callbacks == null) {
-                    Log.w(TAG, "Nobody to tell about the new app.  Launcher is probably loading.");
-                    return;
-                }
-
-                if (added != null) {
-                    final ArrayList<ApplicationInfo> addedFinal = added;
-                    mHandler.post(new Runnable() {
-                        public void run() {
-                            callbacks.bindAppsAdded(addedFinal);
-                        }
-                    });
-                }
-                if (modified != null) {
-                    final ArrayList<ApplicationInfo> modifiedFinal = modified;
-                    mHandler.post(new Runnable() {
-                        public void run() {
-                            callbacks.bindAppsUpdated(modifiedFinal);
-                        }
-                    });
-                }
-                if (removed != null) {
-                    final ArrayList<ApplicationInfo> removedFinal = removed;
-                    mHandler.post(new Runnable() {
-                        public void run() {
-                            callbacks.bindAppsRemoved(removedFinal);
-                        }
-                    });
-                }
-            } else {
-                if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
-                     String packages[] = intent.getStringArrayExtra(
-                             Intent.EXTRA_CHANGED_PACKAGE_LIST);
-                     if (packages == null || packages.length == 0) {
-                         return;
-                     }
-                     synchronized (this) {
-                         mAllAppsLoaded = mWorkspaceLoaded = false;
-                     }
-                     startLoader(context, false);
-                } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
-                     String packages[] = intent.getStringArrayExtra(
-                             Intent.EXTRA_CHANGED_PACKAGE_LIST);
-                     if (packages == null || packages.length == 0) {
-                         return;
-                     }
-                     synchronized (this) {
-                         mAllAppsLoaded = mWorkspaceLoaded = false;
-                     }
-                     startLoader(context, false);
-                }
+                mLoaderTask = new LoaderTask(context, isLaunching);
+                sWorker.post(mLoaderTask);
             }
         }
     }
 
-    public class Loader {
-        private static final int ITEMS_CHUNK = 6;
-
-        private LoaderThread mLoaderThread;
-
-        final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();
-        final ArrayList<LauncherAppWidgetInfo> mAppWidgets = new ArrayList<LauncherAppWidgetInfo>();
-        final HashMap<Long, FolderInfo> mFolders = new HashMap<Long, FolderInfo>();
-                
-        /**
-         * Call this from the ui thread so the handler is initialized on the correct thread.
-         */
-        public Loader() {
-        }
-
-        public void startLoader(Context context, boolean isLaunching) {
-            synchronized (mLock) {
-                if (DEBUG_LOADERS) {
-                    Log.d(TAG, "startLoader isLaunching=" + isLaunching);
-                }
-
-                // Don't bother to start the thread if we know it's not going to do anything
-                if (mCallbacks != null && mCallbacks.get() != null) {
-                    LoaderThread oldThread = mLoaderThread;
-                    if (oldThread != null) {
-                        if (oldThread.isLaunching()) {
-                            // don't downgrade isLaunching if we're already running
-                            isLaunching = true;
-                        }
-                        oldThread.stopLocked();
-                    }
-                    mLoaderThread = new LoaderThread(context, oldThread, isLaunching);
-                    mLoaderThread.start();
-                }
+    public void stopLoader() {
+        synchronized (mLock) {
+            if (mLoaderTask != null) {
+                mLoaderTask.stopLocked();
             }
         }
+    }
 
-        public void stopLoader() {
-            synchronized (mLock) {
-                if (mLoaderThread != null) {
-                    mLoaderThread.stopLocked();
-                }
-            }
+    /**
+     * Runnable for the thread that loads the contents of the launcher:
+     *   - workspace icons
+     *   - widgets
+     *   - all apps icons
+     */
+    private class LoaderTask implements Runnable {
+        private Context mContext;
+        private Thread mWaitThread;
+        private boolean mIsLaunching;
+        private boolean mStopped;
+        private boolean mLoadAndBindStepFinished;
+
+        LoaderTask(Context context, boolean isLaunching) {
+            mContext = context;
+            mIsLaunching = isLaunching;
         }
 
-        /**
-         * Runnable for the thread that loads the contents of the launcher:
-         *   - workspace icons
-         *   - widgets
-         *   - all apps icons
-         */
-        private class LoaderThread extends Thread {
-            private Context mContext;
-            private Thread mWaitThread;
-            private boolean mIsLaunching;
-            private boolean mStopped;
-            private boolean mLoadAndBindStepFinished;
+        boolean isLaunching() {
+            return mIsLaunching;
+        }
 
-            LoaderThread(Context context, Thread waitThread, boolean isLaunching) {
-                mContext = context;
-                mWaitThread = waitThread;
-                mIsLaunching = isLaunching;
+        private void loadAndBindWorkspace() {
+            // Load the workspace
+
+            // For now, just always reload the workspace.  It's ~100 ms vs. the
+            // binding which takes many hundreds of ms.
+            // We can reconsider.
+            if (DEBUG_LOADERS) {
+                Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded);
             }
-
-            boolean isLaunching() {
-                return mIsLaunching;
-            }
-
-            /**
-             * If another LoaderThread was supplied, we need to wait for that to finish before
-             * we start our processing.  This keeps the ordering of the setting and clearing
-             * of the dirty flags correct by making sure we don't start processing stuff until
-             * they've had a chance to re-set them.  We do this waiting the worker thread, not
-             * the ui thread to avoid ANRs.
-             */
-            private void waitForOtherThread() {
-                if (mWaitThread != null) {
-                    boolean done = false;
-                    while (!done) {
-                        try {
-                            mWaitThread.join();
-                            done = true;
-                        } catch (InterruptedException ex) {
-                            // Ignore
-                        }
-                    }
-                    mWaitThread = null;
+            if (true || !mWorkspaceLoaded) {
+                loadWorkspace();
+                if (mStopped) {
+                    return;
                 }
+                mWorkspaceLoaded = true;
             }
 
-            private void loadAndBindWorkspace() {
-                // Load the workspace
+            // Bind the workspace
+            bindWorkspace();
+        }
 
-                // Other other threads can unset mWorkspaceLoaded, so atomically set it,
-                // and then if they unset it, or we unset it because of mStopped, it will
-                // be unset.
-                boolean loaded;
-                synchronized (this) {
-                    loaded = mWorkspaceLoaded;
-                    mWorkspaceLoaded = true;
-                }
+        private void waitForIdle() {
+            // Wait until the either we're stopped or the other threads are done.
+            // This way we don't start loading all apps until the workspace has settled
+            // down.
+            synchronized (LoaderTask.this) {
+                final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
 
-                // For now, just always reload the workspace.  It's ~100 ms vs. the
-                // binding which takes many hundreds of ms.
-                // We can reconsider.
-                if (DEBUG_LOADERS) Log.d(TAG, "loadAndBindWorkspace loaded=" + loaded);
-                if (true || !loaded) {
-                    loadWorkspace();
-                    if (mStopped) {
-                        mWorkspaceLoaded = false;
-                        return;
-                    }
-                }
-
-                // Bind the workspace
-                bindWorkspace();
-            }
-
-            private void waitForIdle() {
-                // Wait until the either we're stopped or the other threads are done.
-                // This way we don't start loading all apps until the workspace has settled
-                // down.
-                synchronized (LoaderThread.this) {
-                    final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
-
-                    mHandler.postIdle(new Runnable() {
-                            public void run() {
-                                synchronized (LoaderThread.this) {
-                                    mLoadAndBindStepFinished = true;
-                                    if (DEBUG_LOADERS) {
-                                        Log.d(TAG, "done with previous binding step");
-                                    }
-                                    LoaderThread.this.notify();
+                mHandler.postIdle(new Runnable() {
+                        public void run() {
+                            synchronized (LoaderTask.this) {
+                                mLoadAndBindStepFinished = true;
+                                if (DEBUG_LOADERS) {
+                                    Log.d(TAG, "done with previous binding step");
                                 }
+                                LoaderTask.this.notify();
                             }
-                        });
-
-                    while (!mStopped && !mLoadAndBindStepFinished) {
-                        try {
-                            this.wait();
-                        } catch (InterruptedException ex) {
-                            // Ignore
                         }
-                    }
-                    if (DEBUG_LOADERS) {
-                        Log.d(TAG, "waited "
-                                + (SystemClock.uptimeMillis()-workspaceWaitTime) 
-                                + "ms for previous step to finish binding");
+                    });
+
+                while (!mStopped && !mLoadAndBindStepFinished) {
+                    try {
+                        this.wait();
+                    } catch (InterruptedException ex) {
+                        // Ignore
                     }
                 }
+                if (DEBUG_LOADERS) {
+                    Log.d(TAG, "waited "
+                            + (SystemClock.uptimeMillis()-workspaceWaitTime) 
+                            + "ms for previous step to finish binding");
+                }
             }
+        }
 
-            public void run() {
-                waitForOtherThread();
+        public void run() {
+            // Optimize for end-user experience: if the Launcher is up and // running with the
+            // All Apps interface in the foreground, load All Apps first. Otherwise, load the
+            // workspace first (default).
+            final Callbacks cbk = mCallbacks.get();
+            final boolean loadWorkspaceFirst = cbk != null ? (!cbk.isAllAppsVisible()) : true;
 
-                // Optimize for end-user experience: if the Launcher is up and // running with the
-                // All Apps interface in the foreground, load All Apps first. Otherwise, load the
-                // workspace first (default).
-                final Callbacks cbk = mCallbacks.get();
-                final boolean loadWorkspaceFirst = cbk != null ? (!cbk.isAllAppsVisible()) : true;
-
+            keep_running: {
                 // Elevate priority when Home launches for the first time to avoid
                 // starving at boot time. Staring at a blank home is not cool.
                 synchronized (mLock) {
@@ -581,11 +482,6 @@
                             ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
                 }
 
-                if (PROFILE_LOADERS) {
-                    android.os.Debug.startMethodTracing(
-                            Environment.getExternalStorageDirectory() + "/launcher-loaders");
-                }
-                
                 if (loadWorkspaceFirst) {
                     if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
                     loadAndBindWorkspace();
@@ -594,12 +490,18 @@
                     loadAndBindAllApps();
                 }
 
-                // Whew! Hard work done.
+                if (mStopped) {
+                    break keep_running;
+                }
+
+                // Whew! Hard work done.  Slow us down, and wait until the UI thread has
+                // settled down.
                 synchronized (mLock) {
                     if (mIsLaunching) {
                         android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                     }
                 }
+                waitForIdle();
 
                 // second step
                 if (loadWorkspaceFirst) {
@@ -609,671 +511,760 @@
                     if (DEBUG_LOADERS) Log.d(TAG, "step 2: special: loading workspace");
                     loadAndBindWorkspace();
                 }
+            }
 
-                // Clear out this reference, otherwise we end up holding it until all of the
-                // callback runnables are done.
-                mContext = null;
+            // Clear out this reference, otherwise we end up holding it until all of the
+            // callback runnables are done.
+            mContext = null;
 
-                synchronized (mLock) {
-                    // Setting the reference is atomic, but we can't do it inside the other critical
-                    // sections.
-                    mLoaderThread = null;
+            synchronized (mLock) {
+                // If we are still the last one to be scheduled, remove ourselves.
+                if (mLoaderTask == this) {
+                    mLoaderTask = null;
                 }
+            }
 
-                if (PROFILE_LOADERS) {
-                    android.os.Debug.stopMethodTracing();
-                }
-
-                // Trigger a gc to try to clean up after the stuff is done, since the
-                // renderscript allocations aren't charged to the java heap.
+            // Trigger a gc to try to clean up after the stuff is done, since the
+            // renderscript allocations aren't charged to the java heap.
+            if (mStopped) {
                 mHandler.post(new Runnable() {
                         public void run() {
                             System.gc();
                         }
                     });
-            }
-
-            public void stopLocked() {
-                synchronized (LoaderThread.this) {
-                    mStopped = true;
-                    this.notify();
-                }
-            }
-
-            /**
-             * Gets the callbacks object.  If we've been stopped, or if the launcher object
-             * has somehow been garbage collected, return null instead.  Pass in the Callbacks
-             * object that was around when the deferred message was scheduled, and if there's
-             * a new Callbacks object around then also return null.  This will save us from
-             * calling onto it with data that will be ignored.
-             */
-            Callbacks tryGetCallbacks(Callbacks oldCallbacks) {
-                synchronized (mLock) {
-                    if (mStopped) {
-                        return null;
-                    }
-
-                    if (mCallbacks == null) {
-                        return null;
-                    }
-
-                    final Callbacks callbacks = mCallbacks.get();
-                    if (callbacks != oldCallbacks) {
-                        return null;
-                    }
-                    if (callbacks == null) {
-                        Log.w(TAG, "no mCallbacks");
-                        return null;
-                    }
-
-                    return callbacks;
-                }
-            }
-
-            // check & update map of what's occupied; used to discard overlapping/invalid items
-            private boolean checkItemPlacement(ItemInfo occupied[][][], ItemInfo item) {
-                if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-                    return true;
-                }
-
-                for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
-                    for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
-                        if (occupied[item.screen][x][y] != null) {
-                            Log.e(TAG, "Error loading shortcut " + item
-                                + " into cell (" + item.screen + ":" 
-                                + x + "," + y
-                                + ") occupied by " 
-                                + occupied[item.screen][x][y]);
-                            return false;
+            } else {
+                mHandler.postIdle(new Runnable() {
+                        public void run() {
+                            System.gc();
                         }
-                    }
+                    });
+            }
+        }
+
+        public void stopLocked() {
+            synchronized (LoaderTask.this) {
+                mStopped = true;
+                this.notify();
+            }
+        }
+
+        /**
+         * Gets the callbacks object.  If we've been stopped, or if the launcher object
+         * has somehow been garbage collected, return null instead.  Pass in the Callbacks
+         * object that was around when the deferred message was scheduled, and if there's
+         * a new Callbacks object around then also return null.  This will save us from
+         * calling onto it with data that will be ignored.
+         */
+        Callbacks tryGetCallbacks(Callbacks oldCallbacks) {
+            synchronized (mLock) {
+                if (mStopped) {
+                    return null;
                 }
-                for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
-                    for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
-                        occupied[item.screen][x][y] = item;
-                    }
+
+                if (mCallbacks == null) {
+                    return null;
                 }
+
+                final Callbacks callbacks = mCallbacks.get();
+                if (callbacks != oldCallbacks) {
+                    return null;
+                }
+                if (callbacks == null) {
+                    Log.w(TAG, "no mCallbacks");
+                    return null;
+                }
+
+                return callbacks;
+            }
+        }
+
+        // check & update map of what's occupied; used to discard overlapping/invalid items
+        private boolean checkItemPlacement(ItemInfo occupied[][][], ItemInfo item) {
+            if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
                 return true;
             }
 
-            private void loadWorkspace() {
-                final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
+            for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
+                for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
+                    if (occupied[item.screen][x][y] != null) {
+                        Log.e(TAG, "Error loading shortcut " + item
+                            + " into cell (" + item.screen + ":" 
+                            + x + "," + y
+                            + ") occupied by " 
+                            + occupied[item.screen][x][y]);
+                        return false;
+                    }
+                }
+            }
+            for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
+                for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
+                    occupied[item.screen][x][y] = item;
+                }
+            }
+            return true;
+        }
 
-                final Context context = mContext;
-                final ContentResolver contentResolver = context.getContentResolver();
-                final PackageManager manager = context.getPackageManager();
-                final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
-                final boolean isSafeMode = manager.isSafeMode();
+        private void loadWorkspace() {
+            final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
 
-                mItems.clear();
-                mAppWidgets.clear();
-                mFolders.clear();
+            final Context context = mContext;
+            final ContentResolver contentResolver = context.getContentResolver();
+            final PackageManager manager = context.getPackageManager();
+            final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
+            final boolean isSafeMode = manager.isSafeMode();
 
-                final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
+            mItems.clear();
+            mAppWidgets.clear();
+            mFolders.clear();
 
-                final Cursor c = contentResolver.query(
-                        LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
+            final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
 
-                final ItemInfo occupied[][][] = new ItemInfo[Launcher.SCREEN_COUNT][Launcher.NUMBER_CELLS_X][Launcher.NUMBER_CELLS_Y];
+            final Cursor c = contentResolver.query(
+                    LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
 
-                try {
-                    final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
-                    final int intentIndex = c.getColumnIndexOrThrow
-                            (LauncherSettings.Favorites.INTENT);
-                    final int titleIndex = c.getColumnIndexOrThrow
-                            (LauncherSettings.Favorites.TITLE);
-                    final int iconTypeIndex = c.getColumnIndexOrThrow(
-                            LauncherSettings.Favorites.ICON_TYPE);
-                    final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
-                    final int iconPackageIndex = c.getColumnIndexOrThrow(
-                            LauncherSettings.Favorites.ICON_PACKAGE);
-                    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);
+            final ItemInfo occupied[][][] = new ItemInfo[Launcher.SCREEN_COUNT][Launcher.NUMBER_CELLS_X][Launcher.NUMBER_CELLS_Y];
 
-                    ShortcutInfo info;
-                    String intentDescription;
-                    LauncherAppWidgetInfo appWidgetInfo;
-                    int container;
-                    long id;
-                    Intent intent;
+            try {
+                final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
+                final int intentIndex = c.getColumnIndexOrThrow
+                        (LauncherSettings.Favorites.INTENT);
+                final int titleIndex = c.getColumnIndexOrThrow
+                        (LauncherSettings.Favorites.TITLE);
+                final int iconTypeIndex = c.getColumnIndexOrThrow(
+                        LauncherSettings.Favorites.ICON_TYPE);
+                final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
+                final int iconPackageIndex = c.getColumnIndexOrThrow(
+                        LauncherSettings.Favorites.ICON_PACKAGE);
+                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);
 
-                    while (!mStopped && c.moveToNext()) {
-                        try {
-                            int itemType = c.getInt(itemTypeIndex);
+                ShortcutInfo info;
+                String intentDescription;
+                LauncherAppWidgetInfo appWidgetInfo;
+                int container;
+                long id;
+                Intent intent;
 
-                            switch (itemType) {
-                            case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
-                            case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
-                                intentDescription = c.getString(intentIndex);
-                                try {
-                                    intent = Intent.parseUri(intentDescription, 0);
-                                } catch (URISyntaxException e) {
-                                    continue;
-                                }
+                while (!mStopped && c.moveToNext()) {
+                    try {
+                        int itemType = c.getInt(itemTypeIndex);
 
-                                if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
-                                    info = getShortcutInfo(manager, intent, context, c, iconIndex,
-                                            titleIndex);
-                                } else {
-                                    info = getShortcutInfo(c, context, iconTypeIndex,
-                                            iconPackageIndex, iconResourceIndex, iconIndex,
-                                            titleIndex);
-                                }
+                        switch (itemType) {
+                        case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
+                        case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+                            intentDescription = c.getString(intentIndex);
+                            try {
+                                intent = Intent.parseUri(intentDescription, 0);
+                            } catch (URISyntaxException e) {
+                                continue;
+                            }
 
-                                if (info != null) {
-                                    updateSavedIcon(context, info, c, iconIndex);
+                            if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+                                info = getShortcutInfo(manager, intent, context, c, iconIndex,
+                                        titleIndex);
+                            } else {
+                                info = getShortcutInfo(c, context, iconTypeIndex,
+                                        iconPackageIndex, iconResourceIndex, iconIndex,
+                                        titleIndex);
+                            }
 
-                                    info.intent = intent;
-                                    info.id = c.getLong(idIndex);
-                                    container = c.getInt(containerIndex);
-                                    info.container = container;
-                                    info.screen = c.getInt(screenIndex);
-                                    info.cellX = c.getInt(cellXIndex);
-                                    info.cellY = c.getInt(cellYIndex);
+                            if (info != null) {
+                                updateSavedIcon(context, info, c, iconIndex);
 
-                                    // check & update map of what's occupied
-                                    if (!checkItemPlacement(occupied, info)) {
-                                        break;
-                                    }
-
-                                    switch (container) {
-                                    case LauncherSettings.Favorites.CONTAINER_DESKTOP:
-                                        mItems.add(info);
-                                        break;
-                                    default:
-                                        // Item is in a user folder
-                                        UserFolderInfo folderInfo =
-                                                findOrMakeUserFolder(mFolders, container);
-                                        folderInfo.add(info);
-                                        break;
-                                    }
-                                } else {
-                                    // Failed to load the shortcut, probably because the
-                                    // activity manager couldn't resolve it (maybe the app
-                                    // was uninstalled), or the db row was somehow screwed up.
-                                    // Delete it.
-                                    id = c.getLong(idIndex);
-                                    Log.e(TAG, "Error loading shortcut " + id + ", removing it");
-                                    contentResolver.delete(LauncherSettings.Favorites.getContentUri(
-                                                id, false), null, null);
-                                }
-                                break;
-
-                            case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
-                                id = c.getLong(idIndex);
-                                UserFolderInfo folderInfo = findOrMakeUserFolder(mFolders, id);
-
-                                folderInfo.title = c.getString(titleIndex);
-
-                                folderInfo.id = id;
+                                info.intent = intent;
+                                info.id = c.getLong(idIndex);
                                 container = c.getInt(containerIndex);
-                                folderInfo.container = container;
-                                folderInfo.screen = c.getInt(screenIndex);
-                                folderInfo.cellX = c.getInt(cellXIndex);
-                                folderInfo.cellY = c.getInt(cellYIndex);
+                                info.container = container;
+                                info.screen = c.getInt(screenIndex);
+                                info.cellX = c.getInt(cellXIndex);
+                                info.cellY = c.getInt(cellYIndex);
 
                                 // check & update map of what's occupied
-                                if (!checkItemPlacement(occupied, folderInfo)) {
+                                if (!checkItemPlacement(occupied, info)) {
                                     break;
                                 }
 
                                 switch (container) {
+                                case LauncherSettings.Favorites.CONTAINER_DESKTOP:
+                                    mItems.add(info);
+                                    break;
+                                default:
+                                    // Item is in a user folder
+                                    UserFolderInfo folderInfo =
+                                            findOrMakeUserFolder(mFolders, container);
+                                    folderInfo.add(info);
+                                    break;
+                                }
+                            } else {
+                                // Failed to load the shortcut, probably because the
+                                // activity manager couldn't resolve it (maybe the app
+                                // was uninstalled), or the db row was somehow screwed up.
+                                // Delete it.
+                                id = c.getLong(idIndex);
+                                Log.e(TAG, "Error loading shortcut " + id + ", removing it");
+                                contentResolver.delete(LauncherSettings.Favorites.getContentUri(
+                                            id, false), null, null);
+                            }
+                            break;
+
+                        case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
+                            id = c.getLong(idIndex);
+                            UserFolderInfo folderInfo = findOrMakeUserFolder(mFolders, id);
+
+                            folderInfo.title = c.getString(titleIndex);
+
+                            folderInfo.id = id;
+                            container = c.getInt(containerIndex);
+                            folderInfo.container = container;
+                            folderInfo.screen = c.getInt(screenIndex);
+                            folderInfo.cellX = c.getInt(cellXIndex);
+                            folderInfo.cellY = c.getInt(cellYIndex);
+
+                            // check & update map of what's occupied
+                            if (!checkItemPlacement(occupied, folderInfo)) {
+                                break;
+                            }
+
+                            switch (container) {
+                                case LauncherSettings.Favorites.CONTAINER_DESKTOP:
+                                    mItems.add(folderInfo);
+                                    break;
+                            }
+
+                            mFolders.put(folderInfo.id, folderInfo);
+                            break;
+
+                        case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
+                            id = c.getLong(idIndex);
+                            Uri uri = Uri.parse(c.getString(uriIndex));
+
+                            // Make sure the live folder exists
+                            final ProviderInfo providerInfo =
+                                    context.getPackageManager().resolveContentProvider(
+                                            uri.getAuthority(), 0);
+
+                            if (providerInfo == null && !isSafeMode) {
+                                itemsToRemove.add(id);
+                            } else {
+                                LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(mFolders, id);
+
+                                intentDescription = c.getString(intentIndex);
+                                intent = null;
+                                if (intentDescription != null) {
+                                    try {
+                                        intent = Intent.parseUri(intentDescription, 0);
+                                    } catch (URISyntaxException e) {
+                                        // Ignore, a live folder might not have a base intent
+                                    }
+                                }
+
+                                liveFolderInfo.title = c.getString(titleIndex);
+                                liveFolderInfo.id = id;
+                                liveFolderInfo.uri = uri;
+                                container = c.getInt(containerIndex);
+                                liveFolderInfo.container = container;
+                                liveFolderInfo.screen = c.getInt(screenIndex);
+                                liveFolderInfo.cellX = c.getInt(cellXIndex);
+                                liveFolderInfo.cellY = c.getInt(cellYIndex);
+                                liveFolderInfo.baseIntent = intent;
+                                liveFolderInfo.displayMode = c.getInt(displayModeIndex);
+
+                                // check & update map of what's occupied
+                                if (!checkItemPlacement(occupied, liveFolderInfo)) {
+                                    break;
+                                }
+
+                                loadLiveFolderIcon(context, c, iconTypeIndex, iconPackageIndex,
+                                        iconResourceIndex, liveFolderInfo);
+
+                                switch (container) {
                                     case LauncherSettings.Favorites.CONTAINER_DESKTOP:
-                                        mItems.add(folderInfo);
+                                        mItems.add(liveFolderInfo);
                                         break;
                                 }
-
-                                mFolders.put(folderInfo.id, folderInfo);
-                                break;
-
-                            case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
-                                id = c.getLong(idIndex);
-                                Uri uri = Uri.parse(c.getString(uriIndex));
-
-                                // Make sure the live folder exists
-                                final ProviderInfo providerInfo =
-                                        context.getPackageManager().resolveContentProvider(
-                                                uri.getAuthority(), 0);
-
-                                if (providerInfo == null && !isSafeMode) {
-                                    itemsToRemove.add(id);
-                                } else {
-                                    LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(mFolders, id);
-    
-                                    intentDescription = c.getString(intentIndex);
-                                    intent = null;
-                                    if (intentDescription != null) {
-                                        try {
-                                            intent = Intent.parseUri(intentDescription, 0);
-                                        } catch (URISyntaxException e) {
-                                            // Ignore, a live folder might not have a base intent
-                                        }
-                                    }
-    
-                                    liveFolderInfo.title = c.getString(titleIndex);
-                                    liveFolderInfo.id = id;
-                                    liveFolderInfo.uri = uri;
-                                    container = c.getInt(containerIndex);
-                                    liveFolderInfo.container = container;
-                                    liveFolderInfo.screen = c.getInt(screenIndex);
-                                    liveFolderInfo.cellX = c.getInt(cellXIndex);
-                                    liveFolderInfo.cellY = c.getInt(cellYIndex);
-                                    liveFolderInfo.baseIntent = intent;
-                                    liveFolderInfo.displayMode = c.getInt(displayModeIndex);
-
-                                    // check & update map of what's occupied
-                                    if (!checkItemPlacement(occupied, liveFolderInfo)) {
-                                        break;
-                                    }
-
-                                    loadLiveFolderIcon(context, c, iconTypeIndex, iconPackageIndex,
-                                            iconResourceIndex, liveFolderInfo);
-    
-                                    switch (container) {
-                                        case LauncherSettings.Favorites.CONTAINER_DESKTOP:
-                                            mItems.add(liveFolderInfo);
-                                            break;
-                                    }
-                                    mFolders.put(liveFolderInfo.id, liveFolderInfo);
-                                }
-                                break;
-
-                            case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
-                                // Read all Launcher-specific widget details
-                                int appWidgetId = c.getInt(appWidgetIdIndex);
-                                id = c.getLong(idIndex);
-
-                                final AppWidgetProviderInfo provider =
-                                        widgets.getAppWidgetInfo(appWidgetId);
-                                
-                                if (!isSafeMode && (provider == null || provider.provider == null ||
-                                        provider.provider.getPackageName() == null)) {
-                                    Log.e(TAG, "Deleting widget that isn't installed anymore: id="
-                                            + id + " appWidgetId=" + appWidgetId);
-                                    itemsToRemove.add(id);
-                                } else {
-                                    appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
-                                    appWidgetInfo.id = id;
-                                    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(TAG, "Widget found where container "
-                                                + "!= CONTAINER_DESKTOP -- ignoring!");
-                                        continue;
-                                    }
-                                    appWidgetInfo.container = c.getInt(containerIndex);
-    
-                                    // check & update map of what's occupied
-                                    if (!checkItemPlacement(occupied, appWidgetInfo)) {
-                                        break;
-                                    }
-
-                                    mAppWidgets.add(appWidgetInfo);
-                                }
-                                break;
+                                mFolders.put(liveFolderInfo.id, liveFolderInfo);
                             }
-                        } catch (Exception e) {
-                            Log.w(TAG, "Desktop items loading interrupted:", e);
-                        }
-                    }
-                } finally {
-                    c.close();
-                }
+                            break;
 
-                if (itemsToRemove.size() > 0) {
-                    ContentProviderClient client = contentResolver.acquireContentProviderClient(
-                                    LauncherSettings.Favorites.CONTENT_URI);
-                    // Remove dead items
-                    for (long id : itemsToRemove) {
-                        if (DEBUG_LOADERS) {
-                            Log.d(TAG, "Removed id = " + id);
+                        case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+                            // Read all Launcher-specific widget details
+                            int appWidgetId = c.getInt(appWidgetIdIndex);
+                            id = c.getLong(idIndex);
+
+                            final AppWidgetProviderInfo provider =
+                                    widgets.getAppWidgetInfo(appWidgetId);
+                            
+                            if (!isSafeMode && (provider == null || provider.provider == null ||
+                                    provider.provider.getPackageName() == null)) {
+                                Log.e(TAG, "Deleting widget that isn't installed anymore: id="
+                                        + id + " appWidgetId=" + appWidgetId);
+                                itemsToRemove.add(id);
+                            } else {
+                                appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
+                                appWidgetInfo.id = id;
+                                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(TAG, "Widget found where container "
+                                            + "!= CONTAINER_DESKTOP -- ignoring!");
+                                    continue;
+                                }
+                                appWidgetInfo.container = c.getInt(containerIndex);
+
+                                // check & update map of what's occupied
+                                if (!checkItemPlacement(occupied, appWidgetInfo)) {
+                                    break;
+                                }
+
+                                mAppWidgets.add(appWidgetInfo);
+                            }
+                            break;
                         }
-                        // Don't notify content observers
-                        try {
-                            client.delete(LauncherSettings.Favorites.getContentUri(id, false),
-                                    null, null);
-                        } catch (RemoteException e) {
-                            Log.w(TAG, "Could not remove id = " + id);
-                        }
+                    } catch (Exception e) {
+                        Log.w(TAG, "Desktop items loading interrupted:", e);
                     }
                 }
+            } finally {
+                c.close();
+            }
 
-                if (DEBUG_LOADERS) {
-                    Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
-                    Log.d(TAG, "workspace layout: ");
-                    for (int y = 0; y < Launcher.NUMBER_CELLS_Y; y++) {
-                        String line = "";
-                        for (int s = 0; s < Launcher.SCREEN_COUNT; s++) {
-                            if (s > 0) {
-                                line += " | ";
-                            }
-                            for (int x = 0; x < Launcher.NUMBER_CELLS_X; x++) {
-                                line += ((occupied[s][x][y] != null) ? "#" : ".");
-                            }
-                        }
-                        Log.d(TAG, "[ " + line + " ]");
+            if (itemsToRemove.size() > 0) {
+                ContentProviderClient client = contentResolver.acquireContentProviderClient(
+                                LauncherSettings.Favorites.CONTENT_URI);
+                // Remove dead items
+                for (long id : itemsToRemove) {
+                    if (DEBUG_LOADERS) {
+                        Log.d(TAG, "Removed id = " + id);
+                    }
+                    // Don't notify content observers
+                    try {
+                        client.delete(LauncherSettings.Favorites.getContentUri(id, false),
+                                null, null);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "Could not remove id = " + id);
                     }
                 }
             }
 
-            /**
-             * Read everything out of our database.
-             */
-            private void bindWorkspace() {
-                final long t = SystemClock.uptimeMillis();
-
-                // Don't use these two variables in any of the callback runnables.
-                // Otherwise we hold a reference to them.
-                final Callbacks oldCallbacks = mCallbacks.get();
-                if (oldCallbacks == null) {
-                    // This launcher has exited and nobody bothered to tell us.  Just bail.
-                    Log.w(TAG, "LoaderThread running with no launcher");
-                    return;
+            if (DEBUG_LOADERS) {
+                Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
+                Log.d(TAG, "workspace layout: ");
+                for (int y = 0; y < Launcher.NUMBER_CELLS_Y; y++) {
+                    String line = "";
+                    for (int s = 0; s < Launcher.SCREEN_COUNT; s++) {
+                        if (s > 0) {
+                            line += " | ";
+                        }
+                        for (int x = 0; x < Launcher.NUMBER_CELLS_X; x++) {
+                            line += ((occupied[s][x][y] != null) ? "#" : ".");
+                        }
+                    }
+                    Log.d(TAG, "[ " + line + " ]");
                 }
+            }
+        }
 
-                int N;
-                // Tell the workspace that we're about to start firing items at it
+        /**
+         * Read everything out of our database.
+         */
+        private void bindWorkspace() {
+            final long t = SystemClock.uptimeMillis();
+
+            // Don't use these two variables in any of the callback runnables.
+            // Otherwise we hold a reference to them.
+            final Callbacks oldCallbacks = mCallbacks.get();
+            if (oldCallbacks == null) {
+                // This launcher has exited and nobody bothered to tell us.  Just bail.
+                Log.w(TAG, "LoaderTask running with no launcher");
+                return;
+            }
+
+            int N;
+            // Tell the workspace that we're about to start firing items at it
+            mHandler.post(new Runnable() {
+                public void run() {
+                    Callbacks callbacks = tryGetCallbacks(oldCallbacks);
+                    if (callbacks != null) {
+                        callbacks.startBinding();
+                    }
+                }
+            });
+            // Add the items to the workspace.
+            N = mItems.size();
+            for (int i=0; i<N; i+=ITEMS_CHUNK) {
+                final int start = i;
+                final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
                 mHandler.post(new Runnable() {
                     public void run() {
                         Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                         if (callbacks != null) {
-                            callbacks.startBinding();
+                            callbacks.bindItems(mItems, start, start+chunkSize);
                         }
                     }
                 });
-                // Add the items to the workspace.
-                N = mItems.size();
-                for (int i=0; i<N; i+=ITEMS_CHUNK) {
-                    final int start = i;
-                    final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
+            }
+            mHandler.post(new Runnable() {
+                public void run() {
+                    Callbacks callbacks = tryGetCallbacks(oldCallbacks);
+                    if (callbacks != null) {
+                        callbacks.bindFolders(mFolders);
+                    }
+                }
+            });
+            // Wait until the queue goes empty.
+            mHandler.post(new Runnable() {
+                public void run() {
+                    if (DEBUG_LOADERS) {
+                        Log.d(TAG, "Going to start binding widgets soon.");
+                    }
+                }
+            });
+            // Bind the widgets, one at a time.
+            // WARNING: this is calling into the workspace from the background thread,
+            // but since getCurrentScreen() just returns the int, we should be okay.  This
+            // is just a hint for the order, and if it's wrong, we'll be okay.
+            // TODO: instead, we should have that push the current screen into here.
+            final int currentScreen = oldCallbacks.getCurrentWorkspaceScreen();
+            N = mAppWidgets.size();
+            // once for the current screen
+            for (int i=0; i<N; i++) {
+                final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
+                if (widget.screen == currentScreen) {
                     mHandler.post(new Runnable() {
                         public void run() {
                             Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                             if (callbacks != null) {
-                                callbacks.bindItems(mItems, start, start+chunkSize);
+                                callbacks.bindAppWidget(widget);
                             }
                         }
                     });
                 }
-                mHandler.post(new Runnable() {
-                    public void run() {
-                        Callbacks callbacks = tryGetCallbacks(oldCallbacks);
-                        if (callbacks != null) {
-                            callbacks.bindFolders(mFolders);
-                        }
-                    }
-                });
-                // Wait until the queue goes empty.
-                mHandler.post(new Runnable() {
-                    public void run() {
-                        if (DEBUG_LOADERS) {
-                            Log.d(TAG, "Going to start binding widgets soon.");
-                        }
-                    }
-                });
-                // Bind the widgets, one at a time.
-                // WARNING: this is calling into the workspace from the background thread,
-                // but since getCurrentScreen() just returns the int, we should be okay.  This
-                // is just a hint for the order, and if it's wrong, we'll be okay.
-                // TODO: instead, we should have that push the current screen into here.
-                final int currentScreen = oldCallbacks.getCurrentWorkspaceScreen();
-                N = mAppWidgets.size();
-                // once for the current screen
-                for (int i=0; i<N; i++) {
-                    final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
-                    if (widget.screen == currentScreen) {
-                        mHandler.post(new Runnable() {
-                            public void run() {
-                                Callbacks callbacks = tryGetCallbacks(oldCallbacks);
-                                if (callbacks != null) {
-                                    callbacks.bindAppWidget(widget);
-                                }
-                            }
-                        });
-                    }
-                }
-                // once for the other screens
-                for (int i=0; i<N; i++) {
-                    final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
-                    if (widget.screen != currentScreen) {
-                        mHandler.post(new Runnable() {
-                            public void run() {
-                                Callbacks callbacks = tryGetCallbacks(oldCallbacks);
-                                if (callbacks != null) {
-                                    callbacks.bindAppWidget(widget);
-                                }
-                            }
-                        });
-                    }
-                }
-                // Tell the workspace that we're done.
-                mHandler.post(new Runnable() {
-                    public void run() {
-                        Callbacks callbacks = tryGetCallbacks(oldCallbacks);
-                        if (callbacks != null) {
-                            callbacks.finishBindingItems();
-                        }
-                    }
-                });
-                // If we're profiling, this is the last thing in the queue.
-                mHandler.post(new Runnable() {
-                    public void run() {
-                        if (DEBUG_LOADERS) {
-                            Log.d(TAG, "bound workspace in "
-                                + (SystemClock.uptimeMillis()-t) + "ms");
-                        }
-                    }
-                });
             }
-
-            private void loadAndBindAllApps() {
-                // Other other threads can unset mAllAppsLoaded, so atomically set it,
-                // and then if they unset it, or we unset it because of mStopped, it will
-                // be unset.
-                boolean loaded;
-                synchronized (this) {
-                    loaded = mAllAppsLoaded;
-                    mAllAppsLoaded = true;
-                }
-
-                if (DEBUG_LOADERS) Log.d(TAG, "loadAndBindAllApps loaded=" + loaded);
-                if (!loaded) {
-                    loadAllAppsByBatch();
-                    if (mStopped) {
-                        mAllAppsLoaded = false;
-                        return;
-                    }
-                } else {
-                    onlyBindAllApps();
+            // once for the other screens
+            for (int i=0; i<N; i++) {
+                final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
+                if (widget.screen != currentScreen) {
+                    mHandler.post(new Runnable() {
+                        public void run() {
+                            Callbacks callbacks = tryGetCallbacks(oldCallbacks);
+                            if (callbacks != null) {
+                                callbacks.bindAppWidget(widget);
+                            }
+                        }
+                    });
                 }
             }
+            // Tell the workspace that we're done.
+            mHandler.post(new Runnable() {
+                public void run() {
+                    Callbacks callbacks = tryGetCallbacks(oldCallbacks);
+                    if (callbacks != null) {
+                        callbacks.finishBindingItems();
+                    }
+                }
+            });
+            // If we're profiling, this is the last thing in the queue.
+            mHandler.post(new Runnable() {
+                public void run() {
+                    if (DEBUG_LOADERS) {
+                        Log.d(TAG, "bound workspace in "
+                            + (SystemClock.uptimeMillis()-t) + "ms");
+                    }
+                }
+            });
+        }
 
-            private void onlyBindAllApps() {
-                final Callbacks oldCallbacks = mCallbacks.get();
-                if (oldCallbacks == null) {
-                    // This launcher has exited and nobody bothered to tell us.  Just bail.
-                    Log.w(TAG, "LoaderThread running with no launcher (onlyBindAllApps)");
+        private void loadAndBindAllApps() {
+            if (DEBUG_LOADERS) {
+                Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded);
+            }
+            if (!mAllAppsLoaded) {
+                loadAllAppsByBatch();
+                if (mStopped) {
                     return;
                 }
+                mAllAppsLoaded = true;
+            } else {
+                onlyBindAllApps();
+            }
+        }
 
-                // shallow copy
-                final ArrayList<ApplicationInfo> list
-                        = (ArrayList<ApplicationInfo>)mAllAppsList.data.clone();
+        private void onlyBindAllApps() {
+            final Callbacks oldCallbacks = mCallbacks.get();
+            if (oldCallbacks == null) {
+                // This launcher has exited and nobody bothered to tell us.  Just bail.
+                Log.w(TAG, "LoaderTask running with no launcher (onlyBindAllApps)");
+                return;
+            }
+
+            // shallow copy
+            final ArrayList<ApplicationInfo> list
+                    = (ArrayList<ApplicationInfo>)mAllAppsList.data.clone();
+            mHandler.post(new Runnable() {
+                public void run() {
+                    final long t = SystemClock.uptimeMillis();
+                    final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
+                    if (callbacks != null) {
+                        callbacks.bindAllApplications(list);
+                    }
+                    if (DEBUG_LOADERS) {
+                        Log.d(TAG, "bound all " + list.size() + " apps from cache in "
+                                + (SystemClock.uptimeMillis()-t) + "ms");
+                    }
+                }
+            });
+
+        }
+
+        private void loadAllAppsByBatch() {
+            final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
+
+            // Don't use these two variables in any of the callback runnables.
+            // Otherwise we hold a reference to them.
+            final Callbacks oldCallbacks = mCallbacks.get();
+            if (oldCallbacks == null) {
+                // This launcher has exited and nobody bothered to tell us.  Just bail.
+                Log.w(TAG, "LoaderTask running with no launcher (loadAllAppsByBatch)");
+                return;
+            }
+
+            final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+            mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+
+            final PackageManager packageManager = mContext.getPackageManager();
+            List<ResolveInfo> apps = null;
+
+            int N = Integer.MAX_VALUE;
+
+            int startIndex;
+            int i=0;
+            int batchSize = -1;
+            while (i < N && !mStopped) {
+                if (i == 0) {
+                    mAllAppsList.clear();
+                    final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
+                    apps = packageManager.queryIntentActivities(mainIntent, 0);
+                    if (DEBUG_LOADERS) {
+                        Log.d(TAG, "queryIntentActivities took "
+                                + (SystemClock.uptimeMillis()-qiaTime) + "ms");
+                    }
+                    if (apps == null) {
+                        return;
+                    }
+                    N = apps.size();
+                    if (DEBUG_LOADERS) {
+                        Log.d(TAG, "queryIntentActivities got " + N + " apps");
+                    }
+                    if (N == 0) {
+                        // There are no apps?!?
+                        return;
+                    }
+                    if (mBatchSize == 0) {
+                        batchSize = N;
+                    } else {
+                        batchSize = mBatchSize;
+                    }
+
+                    final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
+                    Collections.sort(apps,
+                            new ResolveInfo.DisplayNameComparator(packageManager));
+                    if (DEBUG_LOADERS) {
+                        Log.d(TAG, "sort took "
+                                + (SystemClock.uptimeMillis()-sortTime) + "ms");
+                    }
+                }
+
+                final long t2 = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
+
+                startIndex = i;
+                for (int j=0; i<N && j<batchSize; j++) {
+                    // This builds the icon bitmaps.
+                    mAllAppsList.add(new ApplicationInfo(apps.get(i), mIconCache));
+                    i++;
+                }
+
+                final boolean first = i <= batchSize;
+                final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
+                final ArrayList<ApplicationInfo> added = mAllAppsList.added;
+                mAllAppsList.added = new ArrayList<ApplicationInfo>();
+
                 mHandler.post(new Runnable() {
                     public void run() {
                         final long t = SystemClock.uptimeMillis();
-                        final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                         if (callbacks != null) {
-                            callbacks.bindAllApplications(list);
-                        }
-                        if (DEBUG_LOADERS) {
-                            Log.d(TAG, "bound all " + list.size() + " apps from cache in "
-                                    + (SystemClock.uptimeMillis()-t) + "ms");
+                            if (first) {
+                                callbacks.bindAllApplications(added);
+                            } else {
+                                callbacks.bindAppsAdded(added);
+                            }
+                            if (DEBUG_LOADERS) {
+                                Log.d(TAG, "bound " + added.size() + " apps in "
+                                    + (SystemClock.uptimeMillis() - t) + "ms");
+                            }
+                        } else {
+                            Log.i(TAG, "not binding apps: no Launcher activity");
                         }
                     }
                 });
 
-            }
-
-            private void loadAllAppsByBatch() {
-                final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
-
-                // Don't use these two variables in any of the callback runnables.
-                // Otherwise we hold a reference to them.
-                final Callbacks oldCallbacks = mCallbacks.get();
-                if (oldCallbacks == null) {
-                    // This launcher has exited and nobody bothered to tell us.  Just bail.
-                    Log.w(TAG, "LoaderThread running with no launcher (loadAllAppsByBatch)");
-                    return;
-                }
-
-                final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
-                mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-
-                final PackageManager packageManager = mContext.getPackageManager();
-                List<ResolveInfo> apps = null;
-
-                int N = Integer.MAX_VALUE;
-
-                int startIndex;
-                int i=0;
-                int batchSize = -1;
-                while (i < N && !mStopped) {
-                    synchronized (mAllAppsListLock) {
-                        if (i == 0) {
-                            // This needs to happen inside the same lock block as when we
-                            // prepare the first batch for bindAllApplications.  Otherwise
-                            // the package changed receiver can come in and double-add
-                            // (or miss one?).
-                            mAllAppsList.clear();
-                            final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
-                            apps = packageManager.queryIntentActivities(mainIntent, 0);
-                            if (DEBUG_LOADERS) {
-                                Log.d(TAG, "queryIntentActivities took "
-                                        + (SystemClock.uptimeMillis()-qiaTime) + "ms");
-                            }
-                            if (apps == null) {
-                                return;
-                            }
-                            N = apps.size();
-                            if (DEBUG_LOADERS) {
-                                Log.d(TAG, "queryIntentActivities got " + N + " apps");
-                            }
-                            if (N == 0) {
-                                // There are no apps?!?
-                                return;
-                            }
-                            if (mBatchSize == 0) {
-                                batchSize = N;
-                            } else {
-                                batchSize = mBatchSize;
-                            }
-
-                            final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
-                            Collections.sort(apps,
-                                    new ResolveInfo.DisplayNameComparator(packageManager));
-                            if (DEBUG_LOADERS) {
-                                Log.d(TAG, "sort took "
-                                        + (SystemClock.uptimeMillis()-sortTime) + "ms");
-                            }
-                        }
-
-                        final long t2 = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
-
-                        startIndex = i;
-                        for (int j=0; i<N && j<batchSize; j++) {
-                            // This builds the icon bitmaps.
-                            mAllAppsList.add(new ApplicationInfo(apps.get(i), mIconCache));
-                            i++;
-                        }
-
-                        final boolean first = i <= batchSize;
-                        final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
-                        final ArrayList<ApplicationInfo> added = mAllAppsList.added;
-                        mAllAppsList.added = new ArrayList<ApplicationInfo>();
-
-                        mHandler.post(new Runnable() {
-                            public void run() {
-                                final long t = SystemClock.uptimeMillis();
-                                if (callbacks != null) {
-                                    if (first) {
-                                        mBeforeFirstLoad = false;
-                                        callbacks.bindAllApplications(added);
-                                    } else {
-                                        callbacks.bindAppsAdded(added);
-                                    }
-                                    if (DEBUG_LOADERS) {
-                                        Log.d(TAG, "bound " + added.size() + " apps in "
-                                            + (SystemClock.uptimeMillis() - t) + "ms");
-                                    }
-                                } else {
-                                    Log.i(TAG, "not binding apps: no Launcher activity");
-                                }
-                            }
-                        });
-
-                        if (DEBUG_LOADERS) {
-                            Log.d(TAG, "batch of " + (i-startIndex) + " icons processed in "
-                                    + (SystemClock.uptimeMillis()-t2) + "ms");
-                        }
-                    }
-
-                    if (mAllAppsLoadDelay > 0 && i < N) {
-                        try {
-                            if (DEBUG_LOADERS) {
-                                Log.d(TAG, "sleeping for " + mAllAppsLoadDelay + "ms");
-                            }
-                            Thread.sleep(mAllAppsLoadDelay);
-                        } catch (InterruptedException exc) { }
-                    }
-                }
-
                 if (DEBUG_LOADERS) {
-                    Log.d(TAG, "cached all " + N + " apps in "
-                            + (SystemClock.uptimeMillis()-t) + "ms"
-                            + (mAllAppsLoadDelay > 0 ? " (including delay)" : ""));
+                    Log.d(TAG, "batch of " + (i-startIndex) + " icons processed in "
+                            + (SystemClock.uptimeMillis()-t2) + "ms");
+                }
+
+                if (mAllAppsLoadDelay > 0 && i < N) {
+                    try {
+                        if (DEBUG_LOADERS) {
+                            Log.d(TAG, "sleeping for " + mAllAppsLoadDelay + "ms");
+                        }
+                        Thread.sleep(mAllAppsLoadDelay);
+                    } catch (InterruptedException exc) { }
                 }
             }
 
-            public void dumpState() {
-                Log.d(TAG, "mLoader.mLoaderThread.mContext=" + mContext);
-                Log.d(TAG, "mLoader.mLoaderThread.mWaitThread=" + mWaitThread);
-                Log.d(TAG, "mLoader.mLoaderThread.mIsLaunching=" + mIsLaunching);
-                Log.d(TAG, "mLoader.mLoaderThread.mStopped=" + mStopped);
-                Log.d(TAG, "mLoader.mLoaderThread.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished);
+            if (DEBUG_LOADERS) {
+                Log.d(TAG, "cached all " + N + " apps in "
+                        + (SystemClock.uptimeMillis()-t) + "ms"
+                        + (mAllAppsLoadDelay > 0 ? " (including delay)" : ""));
             }
         }
 
         public void dumpState() {
-            Log.d(TAG, "mLoader.mItems size=" + mLoader.mItems.size());
-            if (mLoaderThread != null) {
-                mLoaderThread.dumpState();
-            } else {
-                Log.d(TAG, "mLoader.mLoaderThread=null");
+            Log.d(TAG, "mLoaderTask.mContext=" + mContext);
+            Log.d(TAG, "mLoaderTask.mWaitThread=" + mWaitThread);
+            Log.d(TAG, "mLoaderTask.mIsLaunching=" + mIsLaunching);
+            Log.d(TAG, "mLoaderTask.mStopped=" + mStopped);
+            Log.d(TAG, "mLoaderTask.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished);
+        }
+    }
+
+    void enqueuePackageUpdated(PackageUpdatedTask task) {
+        sWorker.post(task);
+    }
+
+    private class PackageUpdatedTask implements Runnable {
+        int mOp;
+        String[] mPackages;
+
+        public static final int OP_NONE = 0;
+        public static final int OP_ADD = 1;
+        public static final int OP_UPDATE = 2;
+        public static final int OP_REMOVE = 3; // uninstlled
+        public static final int OP_UNAVAILABLE = 4; // external media unmounted
+
+
+        public PackageUpdatedTask(int op, String[] packages) {
+            mOp = op;
+            mPackages = packages;
+        }
+
+        public void run() {
+            final Context context = mApp;
+
+            final String[] packages = mPackages;
+            final int N = packages.length;
+            switch (mOp) {
+                case OP_ADD:
+                    for (int i=0; i<N; i++) {
+                        if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
+                        mAllAppsList.addPackage(context, packages[i]);
+                    }
+                    break;
+                case OP_UPDATE:
+                    for (int i=0; i<N; i++) {
+                        if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
+                        mAllAppsList.updatePackage(context, packages[i]);
+                    }
+                    break;
+                case OP_REMOVE:
+                case OP_UNAVAILABLE:
+                    for (int i=0; i<N; i++) {
+                        if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
+                        mAllAppsList.removePackage(packages[i]);
+                    }
+                    break;
+            }
+
+            ArrayList<ApplicationInfo> added = null;
+            ArrayList<ApplicationInfo> removed = null;
+            ArrayList<ApplicationInfo> modified = null;
+
+            if (mAllAppsList.added.size() > 0) {
+                added = mAllAppsList.added;
+                mAllAppsList.added = new ArrayList<ApplicationInfo>();
+            }
+            if (mAllAppsList.removed.size() > 0) {
+                removed = mAllAppsList.removed;
+                mAllAppsList.removed = new ArrayList<ApplicationInfo>();
+                for (ApplicationInfo info: removed) {
+                    mIconCache.remove(info.intent.getComponent());
+                }
+            }
+            if (mAllAppsList.modified.size() > 0) {
+                modified = mAllAppsList.modified;
+                mAllAppsList.modified = new ArrayList<ApplicationInfo>();
+            }
+
+            final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
+            if (callbacks == null) {
+                Log.w(TAG, "Nobody to tell about the new app.  Launcher is probably loading.");
+                return;
+            }
+
+            if (added != null) {
+                final ArrayList<ApplicationInfo> addedFinal = added;
+                mHandler.post(new Runnable() {
+                    public void run() {
+                        if (callbacks == mCallbacks.get()) {
+                            callbacks.bindAppsAdded(addedFinal);
+                        }
+                    }
+                });
+            }
+            if (modified != null) {
+                final ArrayList<ApplicationInfo> modifiedFinal = modified;
+                mHandler.post(new Runnable() {
+                    public void run() {
+                        if (callbacks == mCallbacks.get()) {
+                            callbacks.bindAppsUpdated(modifiedFinal);
+                        }
+                    }
+                });
+            }
+            if (removed != null) {
+                final boolean permanent = mOp != OP_UNAVAILABLE;
+                final ArrayList<ApplicationInfo> removedFinal = removed;
+                mHandler.post(new Runnable() {
+                    public void run() {
+                        if (callbacks == mCallbacks.get()) {
+                            callbacks.bindAppsRemoved(removedFinal, permanent);
+                        }
+                    }
+                });
             }
         }
     }
@@ -1511,7 +1502,7 @@
         // into the DB.  We do this so when we're loading, if the
         // package manager can't find an icon (for example because
         // the app is on SD) then we can use that instead.
-        if (info.onExternalStorage && !info.customIcon && !info.usingFallbackIcon) {
+        if (!info.customIcon && !info.usingFallbackIcon) {
             boolean needSave;
             byte[] data = c.getBlob(iconIndex);
             try {
@@ -1585,12 +1576,16 @@
     };
 
     public void dumpState() {
-        Log.d(TAG, "mBeforeFirstLoad=" + mBeforeFirstLoad);
         Log.d(TAG, "mCallbacks=" + mCallbacks);
         ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mAllAppsList.data);
         ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.added", mAllAppsList.added);
         ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.removed", mAllAppsList.removed);
         ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.modified", mAllAppsList.modified);
-        mLoader.dumpState();
+        Log.d(TAG, "mItems size=" + mItems.size());
+        if (mLoaderTask != null) {
+            mLoaderTask.dumpState();
+        } else {
+            Log.d(TAG, "mLoaderTask=null");
+        }
     }
 }
diff --git a/src/com/android/launcher2/ShortcutInfo.java b/src/com/android/launcher2/ShortcutInfo.java
index 5c322ba..2c5aec4 100644
--- a/src/com/android/launcher2/ShortcutInfo.java
+++ b/src/com/android/launcher2/ShortcutInfo.java
@@ -54,11 +54,6 @@
     boolean usingFallbackIcon;
 
     /**
-     * Indicates whether the shortcut is on external storage and may go away at any time.
-     */
-    boolean onExternalStorage;
-
-    /**
      * If isShortcut=true and customIcon=false, this contains a reference to the
      * shortcut icon as an application's resource.
      */
@@ -101,6 +96,7 @@
     public Bitmap getIcon(IconCache iconCache) {
         if (mIcon == null) {
             mIcon = iconCache.getIcon(this.intent);
+            this.usingFallbackIcon = iconCache.isDefaultIcon(mIcon);
         }
         return mIcon;
     }
@@ -135,7 +131,7 @@
                     LauncherSettings.BaseLauncherColumns.ICON_TYPE_BITMAP);
             writeBitmap(values, mIcon);
         } else {
-            if (onExternalStorage && !usingFallbackIcon) {
+            if (!usingFallbackIcon) {
                 writeBitmap(values, mIcon);
             }
             values.put(LauncherSettings.BaseLauncherColumns.ICON_TYPE,
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index c337c30..c182209 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -558,9 +558,7 @@
     public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
         int screen = indexOfChild(child);
         if (screen != mCurrentScreen || !mScroller.isFinished()) {
-            if (!mLauncher.isWorkspaceLocked()) {
-                snapToScreen(screen);
-            }
+            snapToScreen(screen);
             return true;
         }
         return false;
@@ -625,7 +623,7 @@
     @Override
     public boolean dispatchTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            if (mLauncher.isWorkspaceLocked() || mLauncher.isAllAppsVisible()) {
+            if (mLauncher.isAllAppsVisible()) {
                 return false;
             }
         }
@@ -634,9 +632,8 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        final boolean workspaceLocked = mLauncher.isWorkspaceLocked();
         final boolean allAppsVisible = mLauncher.isAllAppsVisible();
-        if (workspaceLocked || allAppsVisible) {
+        if (allAppsVisible) {
             return false; // We don't want the events.  Let them fall through to the all apps view.
         }
 
@@ -656,10 +653,7 @@
             return true;
         }
 
-        if (mVelocityTracker == null) {
-            mVelocityTracker = VelocityTracker.obtain();
-        }
-        mVelocityTracker.addMovement(ev);
+        acquireVelocityTrackerAndAddMovement(ev);
         
         switch (action & MotionEvent.ACTION_MASK) {
             case MotionEvent.ACTION_MOVE: {
@@ -744,12 +738,7 @@
                 mTouchState = TOUCH_STATE_REST;
                 mActivePointerId = INVALID_POINTER;
                 mAllowLongPress = false;
-                
-                if (mVelocityTracker != null) {
-                    mVelocityTracker.recycle();
-                    mVelocityTracker = null;
-                }
-
+                releaseVelocityTracker();
                 break;
                 
             case MotionEvent.ACTION_POINTER_UP:
@@ -840,9 +829,6 @@
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         
-        if (mLauncher.isWorkspaceLocked()) {
-            return false; // We don't want the events.  Let them fall through to the all apps view.
-        }
         if (mLauncher.isAllAppsVisible()) {
             // Cancel any scrolling that is in progress.
             if (!mScroller.isFinished()) {
@@ -852,10 +838,7 @@
             return false; // We don't want the events.  Let them fall through to the all apps view.
         }
 
-        if (mVelocityTracker == null) {
-            mVelocityTracker = VelocityTracker.obtain();
-        }
-        mVelocityTracker.addMovement(ev);
+        acquireVelocityTrackerAndAddMovement(ev);
 
         final int action = ev.getAction();
 
@@ -928,18 +911,20 @@
                 } else {
                     snapToScreen(whichScreen, 0, true);
                 }
-
-                if (mVelocityTracker != null) {
-                    mVelocityTracker.recycle();
-                    mVelocityTracker = null;
-                }
             }
             mTouchState = TOUCH_STATE_REST;
             mActivePointerId = INVALID_POINTER;
+            releaseVelocityTracker();
             break;
         case MotionEvent.ACTION_CANCEL:
+            if (mTouchState == TOUCH_STATE_SCROLLING) {
+                final int screenWidth = getWidth();
+                final int whichScreen = (mScrollX + (screenWidth / 2)) / screenWidth;
+                snapToScreen(whichScreen, 0, true);
+            }
             mTouchState = TOUCH_STATE_REST;
             mActivePointerId = INVALID_POINTER;
+            releaseVelocityTracker();
             break;
         case MotionEvent.ACTION_POINTER_UP:
             onSecondaryPointerUp(ev);
@@ -949,6 +934,20 @@
         return true;
     }
     
+    private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) {
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        }
+        mVelocityTracker.addMovement(ev);
+    }
+
+    private void releaseVelocityTracker() {
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+            mVelocityTracker = null;
+        }
+    }
+
     void snapToScreen(int whichScreen) {
         snapToScreen(whichScreen, 0, false);
     }
@@ -1351,7 +1350,6 @@
                             if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
                                 for (String packageName: packageNames) {
                                     if (packageName.equals(name.getPackageName())) {
-                                        // TODO: This should probably be done on a worker thread
                                         LauncherModel.deleteItemFromDatabase(mLauncher, info);
                                         childrenToRemove.add(view);
                                     }
@@ -1373,9 +1371,7 @@
                                     for (String packageName: packageNames) {
                                         if (packageName.equals(name.getPackageName())) {
                                             toRemove.add(appInfo);
-                                            // TODO: This should probably be done on a worker thread
-                                            LauncherModel.deleteItemFromDatabase(
-                                                    mLauncher, appInfo);
+                                            LauncherModel.deleteItemFromDatabase(mLauncher, appInfo);
                                             removedFromFolder = true;
                                         }
                                     }
@@ -1396,7 +1392,6 @@
                             if (providerInfo != null) {
                                 for (String packageName: packageNames) {
                                     if (packageName.equals(providerInfo.packageName)) {
-                                        // TODO: This should probably be done on a worker thread
                                         LauncherModel.deleteItemFromDatabase(mLauncher, info);
                                         childrenToRemove.add(view);                        
                                     }
@@ -1409,7 +1404,6 @@
                             if (provider != null) {
                                 for (String packageName: packageNames) {
                                     if (packageName.equals(provider.provider.getPackageName())) {
-                                        // TODO: This should probably be done on a worker thread
                                         LauncherModel.deleteItemFromDatabase(mLauncher, info);
                                         childrenToRemove.add(view);                                
                                     }