merge in ics-release history after reset to master
diff --git a/res/drawable-land-hdpi/bg_cling1.png b/res/drawable-land-hdpi/bg_cling1.png
new file mode 100644
index 0000000..7123c5c
--- /dev/null
+++ b/res/drawable-land-hdpi/bg_cling1.png
Binary files differ
diff --git a/res/drawable-land-hdpi/bg_cling2.png b/res/drawable-land-hdpi/bg_cling2.png
new file mode 100644
index 0000000..889b627
--- /dev/null
+++ b/res/drawable-land-hdpi/bg_cling2.png
Binary files differ
diff --git a/res/drawable-land-hdpi/bg_cling3.png b/res/drawable-land-hdpi/bg_cling3.png
new file mode 100644
index 0000000..4ff338c
--- /dev/null
+++ b/res/drawable-land-hdpi/bg_cling3.png
Binary files differ
diff --git a/res/drawable-land-hdpi/hotseat_bg_panel.9.png b/res/drawable-land-hdpi/hotseat_bg_panel.9.png
index c7602cd..90177f8 100644
--- a/res/drawable-land-hdpi/hotseat_bg_panel.9.png
+++ b/res/drawable-land-hdpi/hotseat_bg_panel.9.png
Binary files differ
diff --git a/res/drawable-land-mdpi/bg_cling1.png b/res/drawable-land-mdpi/bg_cling1.png
new file mode 100644
index 0000000..f5faeb4
--- /dev/null
+++ b/res/drawable-land-mdpi/bg_cling1.png
Binary files differ
diff --git a/res/drawable-land-mdpi/bg_cling2.png b/res/drawable-land-mdpi/bg_cling2.png
new file mode 100644
index 0000000..963967d
--- /dev/null
+++ b/res/drawable-land-mdpi/bg_cling2.png
Binary files differ
diff --git a/res/drawable-land-mdpi/bg_cling3.png b/res/drawable-land-mdpi/bg_cling3.png
new file mode 100644
index 0000000..921831a
--- /dev/null
+++ b/res/drawable-land-mdpi/bg_cling3.png
Binary files differ
diff --git a/res/drawable-land-mdpi/hotseat_bg_panel.9.png b/res/drawable-land-mdpi/hotseat_bg_panel.9.png
index d745bb3..83c4a55 100644
--- a/res/drawable-land-mdpi/hotseat_bg_panel.9.png
+++ b/res/drawable-land-mdpi/hotseat_bg_panel.9.png
Binary files differ
diff --git a/res/drawable-land-xhdpi/bg_cling1.png b/res/drawable-land-xhdpi/bg_cling1.png
new file mode 100644
index 0000000..2282117
--- /dev/null
+++ b/res/drawable-land-xhdpi/bg_cling1.png
Binary files differ
diff --git a/res/drawable-land-xhdpi/bg_cling2.png b/res/drawable-land-xhdpi/bg_cling2.png
new file mode 100644
index 0000000..5243889
--- /dev/null
+++ b/res/drawable-land-xhdpi/bg_cling2.png
Binary files differ
diff --git a/res/drawable-land-xhdpi/bg_cling3.png b/res/drawable-land-xhdpi/bg_cling3.png
new file mode 100644
index 0000000..08475f7
--- /dev/null
+++ b/res/drawable-land-xhdpi/bg_cling3.png
Binary files differ
diff --git a/res/drawable-land-xhdpi/hotseat_bg_panel.9.png b/res/drawable-land-xhdpi/hotseat_bg_panel.9.png
index acbe24a..8718040 100644
--- a/res/drawable-land-xhdpi/hotseat_bg_panel.9.png
+++ b/res/drawable-land-xhdpi/hotseat_bg_panel.9.png
Binary files differ
diff --git a/res/layout-land/folder_cling.xml b/res/layout-land/folder_cling.xml
index 91d34ad..3d7f4f4 100644
--- a/res/layout-land/folder_cling.xml
+++ b/res/layout-land/folder_cling.xml
@@ -20,8 +20,8 @@
     <FrameLayout
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:layout_marginLeft="40dp"
-        android:layout_marginTop="40dp">
+        android:layout_marginLeft="20dp"
+        android:layout_marginTop="20dp">
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -32,21 +32,12 @@
                 android:text="@string/folder_cling_title" />
             <TextView
                 style="@style/ClingText"
-                android:id="@+id/folder_cling_move_item"
+                android:id="@+id/folder_cling_create_folder"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:text="@string/folder_cling_move_item" />
+                android:text="@string/folder_cling_create_folder" />
         </LinearLayout>
     </FrameLayout>
-    <TextView
-        style="@style/ClingText"
-        android:id="@+id/folder_cling_create_folder"
-        android:layout_width="340dp"
-        android:layout_height="wrap_content"
-        android:layout_marginLeft="40dp"
-        android:layout_marginBottom="40dp"
-        android:layout_gravity="bottom"
-        android:text="@string/folder_cling_create_folder" />
     <Button
         style="@style/ClingButton"
         android:id="@+id/cling_dismiss"
diff --git a/res/layout-land/folder_icon.xml b/res/layout-land/folder_icon.xml
index 15867c7..4c92fca 100644
--- a/res/layout-land/folder_icon.xml
+++ b/res/layout-land/folder_icon.xml
@@ -19,7 +19,9 @@
     xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical">
+    android:orientation="vertical"
+    android:focusable="true"
+    android:background="@drawable/focusable_view_bg">
     <ImageView
         android:id="@+id/preview_background"
         android:layout_gravity="center_horizontal"
diff --git a/res/layout-port/folder_icon.xml b/res/layout-port/folder_icon.xml
index 69e51e8..0df16b8 100644
--- a/res/layout-port/folder_icon.xml
+++ b/res/layout-port/folder_icon.xml
@@ -19,7 +19,9 @@
     xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical">
+    android:orientation="vertical"
+    android:focusable="true"
+    android:background="@drawable/focusable_view_bg">
     <ImageView
         android:id="@+id/preview_background"
         android:layout_gravity="center_horizontal"
diff --git a/res/layout-port/search_bar.xml b/res/layout-port/search_bar.xml
index 468e945..dbacd3d 100644
--- a/res/layout-port/search_bar.xml
+++ b/res/layout-port/search_bar.xml
@@ -28,7 +28,7 @@
         android:layout_height="match_parent"
         android:layout_alignParentLeft="true"
         android:layout_alignParentTop="true"
-        android:layout_toLeftOf="@+id/voice_button"
+        android:layout_toLeftOf="@+id/voice_button_container"
         android:background="?android:attr/selectableItemBackground"
         android:onClick="onClickSearchButton"
         android:focusable="true"
@@ -43,18 +43,24 @@
     </LinearLayout>
 
     <!-- Voice search icon -->
-    <ImageView
+    <LinearLayout
         style="@style/SearchButton.Voice"
-        android:id="@+id/voice_button"
-        android:layout_width="wrap_content"
+        android:id="@+id/voice_button_container"
+        android:layout_width="@dimen/search_bar_height"
         android:layout_height="match_parent"
         android:layout_alignParentRight="true"
         android:layout_alignParentTop="true"
-        android:src="@drawable/ic_home_voice_search_holo"
-        android:adjustViewBounds="true"
+        android:gravity="right"
         android:background="?android:attr/selectableItemBackground"
         android:onClick="onClickVoiceButton"
         android:focusable="true"
         android:clickable="true"
-        android:contentDescription="@string/accessibility_voice_search_button" />
+        android:contentDescription="@string/accessibility_voice_search_button">
+        <ImageView
+            android:id="@+id/voice_button"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:src="@drawable/ic_home_voice_search_holo"
+            android:adjustViewBounds="true" />
+    </LinearLayout>
 </RelativeLayout>
diff --git a/res/layout/user_folder.xml b/res/layout/user_folder.xml
index 814c968..208390d 100644
--- a/res/layout/user_folder.xml
+++ b/res/layout/user_folder.xml
@@ -37,7 +37,7 @@
         launcher:cellWidth="@dimen/folder_cell_width"
         launcher:cellHeight="@dimen/folder_cell_height" />
 
-    <EditText
+    <com.android.launcher2.FolderEditText
         android:id="@+id/folder_name"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
@@ -50,5 +50,6 @@
         android:textColor="#ff33b5e5"
         android:textColorHighlight="#ff333333"
         android:gravity="center_horizontal"
-        android:singleLine="true"/>
+        android:singleLine="true"
+        android:imeOptions="flagNoExtractUi"/>
 </com.android.launcher2.Folder>
diff --git a/res/values-sw340dp-port/dimens.xml b/res/values-sw340dp-port/dimens.xml
index 3fea47b..ab8cfec 100644
--- a/res/values-sw340dp-port/dimens.xml
+++ b/res/values-sw340dp-port/dimens.xml
@@ -32,7 +32,8 @@
     <dimen name="hotseat_left_padding">-1dp</dimen>
     <dimen name="hotseat_right_padding">-1dp</dimen>
 
-    <dimen name="qsb_bar_height">50dp</dimen>
+    <dimen name="qsb_bar_height">54dp</dimen>
+    <dimen name="search_bar_height">48dp</dimen>
     <!-- height of the bottom row of controls -->
     <dimen name="button_bar_height">80dip</dimen>
     <!-- Because portal animations go beyond the bounds of an icon, we need
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index e5e1b3f..ab24d5b 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -20,14 +20,14 @@
 
 <!-- Workspace -->
     <dimen name="qsb_bar_height">40dp</dimen>
-    <dimen name="search_bar_height">44dp</dimen>
+    <dimen name="search_bar_height">40dp</dimen>
     <dimen name="workspace_cell_width">80dp</dimen>
     <dimen name="workspace_cell_height">100dp</dimen>
     <dimen name="workspace_width_gap">-1dp</dimen>
     <dimen name="workspace_height_gap">-1dp</dimen>
     <dimen name="workspace_max_gap">16dp</dimen>
     <dimen name="folder_cell_width">74dp</dimen>
-    <dimen name="folder_cell_height">80dp</dimen>
+    <dimen name="folder_cell_height">82dp</dimen>
     <dimen name="workspace_divider_padding_left">12dp</dimen>
     <dimen name="workspace_divider_padding_right">12dp</dimen>
     <dimen name="workspace_divider_padding_top">0dp</dimen>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index de438be..c8fe024 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -77,7 +77,7 @@
     </style>
 
     <style name="WorkspaceIcon.Landscape">
-        <item name="android:drawablePadding">4dp</item>
+        <item name="android:drawablePadding">2dp</item>
         <item name="android:paddingLeft">4dp</item>
         <item name="android:paddingRight">4dp</item>
         <item name="android:paddingTop">6dp</item>
@@ -91,7 +91,7 @@
     <style name="WorkspaceIcon.Landscape.Folder">
         <item name="android:drawablePadding">0dp</item>
         <item name="android:paddingTop">0dp</item>
-        <item name="android:layout_marginTop">-6dp</item>
+        <item name="android:layout_marginTop">-2dp</item>
     </style>
 
     <style name="WorkspaceIcon.Portrait.AppsCustomize">
diff --git a/src/com/android/launcher2/AppsCustomizePagedView.java b/src/com/android/launcher2/AppsCustomizePagedView.java
index 6493e97..560c3c7 100644
--- a/src/com/android/launcher2/AppsCustomizePagedView.java
+++ b/src/com/android/launcher2/AppsCustomizePagedView.java
@@ -96,6 +96,25 @@
         doInBackgroundCallback = bgR;
         postExecuteCallback = postR;
     }
+    void cleanup(boolean cancelled) {
+        // Clean up any references to source/generated bitmaps
+        if (sourceImages != null) {
+            if (cancelled) {
+                for (Bitmap b : sourceImages) {
+                    b.recycle();
+                }
+            }
+            sourceImages.clear();
+        }
+        if (generatedImages != null) {
+            if (cancelled) {
+                for (Bitmap b : generatedImages) {
+                    b.recycle();
+                }
+            }
+            generatedImages.clear();
+        }
+    }
     int page;
     ArrayList<Object> items;
     ArrayList<Bitmap> sourceImages;
@@ -746,21 +765,31 @@
                 @Override
                 public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) {
                     try {
-                        Thread.sleep(sleepMs);
-                    } catch (Exception e) {}
-                    loadWidgetPreviewsInBackground(task, data);
+                        try {
+                            Thread.sleep(sleepMs);
+                        } catch (Exception e) {}
+                        loadWidgetPreviewsInBackground(task, data);
+                    } finally {
+                        if (task.isCancelled()) {
+                            data.cleanup(true);
+                        }
+                    }
                 }
             },
             new AsyncTaskCallback() {
                 @Override
                 public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) {
-                    mRunningTasks.remove(task);
-                    if (task.isCancelled()) return;
-                    if (task.page > getPageCount()) return;
-                    if (task.pageContentType != mContentType) return;
-                    onSyncWidgetPageItems(data);
+                    try {
+                        mRunningTasks.remove(task);
+                        if (task.isCancelled()) return;
+                        if (task.page > getPageCount()) return;
+                        if (task.pageContentType != mContentType) return;
+                        onSyncWidgetPageItems(data);
+                    } finally {
+                        data.cleanup(task.isCancelled());
+                    }
                 }
-        });
+            });
 
         // Ensure that the task is appropriately prioritized and runs in parallel
         AppsCustomizeAsyncTask t = new AppsCustomizeAsyncTask(page, mContentType,
@@ -790,40 +819,50 @@
             new AsyncTaskCallback() {
                 @Override
                 public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) {
-                    // Ensure that this task starts running at the correct priority
-                    task.syncThreadPriority();
-
-                    ArrayList<Bitmap> images = data.generatedImages;
-                    ArrayList<Bitmap> srcImages = data.sourceImages;
-                    int count = srcImages.size();
-                    Canvas c = new Canvas();
-                    for (int i = 0; i < count && !task.isCancelled(); ++i) {
-                        // Before work on each item, ensure that this task is running at the correct
-                        // priority
+                    try {
+                        // Ensure that this task starts running at the correct priority
                         task.syncThreadPriority();
 
-                        Bitmap b = srcImages.get(i);
-                        Bitmap outline = Bitmap.createBitmap(b.getWidth(), b.getHeight(),
-                                Bitmap.Config.ARGB_8888);
+                        ArrayList<Bitmap> images = data.generatedImages;
+                        ArrayList<Bitmap> srcImages = data.sourceImages;
+                        int count = srcImages.size();
+                        Canvas c = new Canvas();
+                        for (int i = 0; i < count && !task.isCancelled(); ++i) {
+                            // Before work on each item, ensure that this task is running at the correct
+                            // priority
+                            task.syncThreadPriority();
 
-                        c.setBitmap(outline);
-                        c.save();
-                        c.drawBitmap(b, 0, 0, null);
-                        c.restore();
-                        c.setBitmap(null);
+                            Bitmap b = srcImages.get(i);
+                            Bitmap outline = Bitmap.createBitmap(b.getWidth(), b.getHeight(),
+                                    Bitmap.Config.ARGB_8888);
 
-                        images.add(outline);
+                            c.setBitmap(outline);
+                            c.save();
+                            c.drawBitmap(b, 0, 0, null);
+                            c.restore();
+                            c.setBitmap(null);
+
+                            images.add(outline);
+                        }
+                    } finally {
+                        if (task.isCancelled()) {
+                            data.cleanup(true);
+                        }
                     }
                 }
             },
             new AsyncTaskCallback() {
                 @Override
                 public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) {
-                    mRunningTasks.remove(task);
-                    if (task.isCancelled()) return;
-                    if (task.page > getPageCount()) return;
-                    if (task.pageContentType != mContentType) return;
-                    onHolographicPageItemsLoaded(data);
+                    try {
+                        mRunningTasks.remove(task);
+                        if (task.isCancelled()) return;
+                        if (task.page > getPageCount()) return;
+                        if (task.pageContentType != mContentType) return;
+                        onHolographicPageItemsLoaded(data);
+                    } finally {
+                        data.cleanup(task.isCancelled());
+                    }
                 }
             });
 
diff --git a/src/com/android/launcher2/Cling.java b/src/com/android/launcher2/Cling.java
index 0a9bc7f..9f27586 100644
--- a/src/com/android/launcher2/Cling.java
+++ b/src/com/android/launcher2/Cling.java
@@ -161,11 +161,14 @@
 
             // Draw the background
             if (mBackground == null) {
-                if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT)) {
+                if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) ||
+                    mDrawIdentifier.equals(WORKSPACE_LANDSCAPE)) {
                     mBackground = getResources().getDrawable(R.drawable.bg_cling1);
-                } else if (mDrawIdentifier.equals(ALLAPPS_PORTRAIT)) {
+                } else if (mDrawIdentifier.equals(ALLAPPS_PORTRAIT) ||
+                        mDrawIdentifier.equals(ALLAPPS_LANDSCAPE)) {
                     mBackground = getResources().getDrawable(R.drawable.bg_cling2);
-                } else if (mDrawIdentifier.equals(FOLDER_PORTRAIT)) {
+                } else if (mDrawIdentifier.equals(FOLDER_PORTRAIT) ||
+                        mDrawIdentifier.equals(FOLDER_LANDSCAPE)) {
                     mBackground = getResources().getDrawable(R.drawable.bg_cling3);
                 }
             }
diff --git a/src/com/android/launcher2/DragController.java b/src/com/android/launcher2/DragController.java
index afbf80d..ca7e55b 100644
--- a/src/com/android/launcher2/DragController.java
+++ b/src/com/android/launcher2/DragController.java
@@ -361,6 +361,7 @@
             if (mLastDropTarget != null) {
                 mLastDropTarget.onDragExit(mDragObject);
             }
+            mDragObject.cancelled = true;
             mDragObject.dragComplete = true;
             mDragObject.dragSource.onDropCompleted(null, mDragObject, false);
         }
diff --git a/src/com/android/launcher2/DropTarget.java b/src/com/android/launcher2/DropTarget.java
index 34fa893..4172da2 100644
--- a/src/com/android/launcher2/DropTarget.java
+++ b/src/com/android/launcher2/DropTarget.java
@@ -52,6 +52,9 @@
         /** Post drag animation runnable */
         public Runnable postAnimationRunnable = null;
 
+        /** Indicates that the drag operation was cancelled */
+        public boolean cancelled = false;
+
         public DragObject() {
         }
     }
diff --git a/src/com/android/launcher2/FocusHelper.java b/src/com/android/launcher2/FocusHelper.java
index f030739..967b02f 100644
--- a/src/com/android/launcher2/FocusHelper.java
+++ b/src/com/android/launcher2/FocusHelper.java
@@ -23,6 +23,7 @@
 import android.view.ViewParent;
 import android.widget.TabHost;
 import android.widget.TabWidget;
+import android.widget.TextView;
 
 import com.android.launcher.R;
 
@@ -33,18 +34,25 @@
 /**
  * A keyboard listener we set on all the workspace icons.
  */
-class BubbleTextViewKeyEventListener implements View.OnKeyListener {
-    @Override
+class IconKeyEventListener implements View.OnKeyListener {
     public boolean onKey(View v, int keyCode, KeyEvent event) {
-        return FocusHelper.handleBubbleTextViewKeyEvent((BubbleTextView) v, keyCode, event);
+        return FocusHelper.handleIconKeyEvent(v, keyCode, event);
+    }
+}
+
+/**
+ * A keyboard listener we set on all the workspace icons.
+ */
+class FolderKeyEventListener implements View.OnKeyListener {
+    public boolean onKey(View v, int keyCode, KeyEvent event) {
+        return FocusHelper.handleFolderKeyEvent(v, keyCode, event);
     }
 }
 
 /**
  * A keyboard listener we set on all the hotseat buttons.
  */
-class HotseatBubbleTextViewKeyEventListener implements View.OnKeyListener {
-    @Override
+class HotseatIconKeyEventListener implements View.OnKeyListener {
     public boolean onKey(View v, int keyCode, KeyEvent event) {
         final Configuration configuration = v.getResources().getConfiguration();
         return FocusHelper.handleHotseatButtonKeyEvent(v, keyCode, event, configuration.orientation);
@@ -56,7 +64,6 @@
  * market icon and vice versa.
  */
 class AppsCustomizeTabKeyEventListener implements View.OnKeyListener {
-    @Override
     public boolean onKey(View v, int keyCode, KeyEvent event) {
         return FocusHelper.handleAppsCustomizeTabKeyEvent(v, keyCode, event);
     }
@@ -480,7 +487,6 @@
         final int buttonIndex = parent.indexOfChild(v);
         final int buttonCount = parent.getChildCount();
         final int pageIndex = workspace.getCurrentPage();
-        final int pageCount = workspace.getChildCount();
 
         // NOTE: currently we don't special case for the phone UI in different
         // orientations, even though the hotseat is on the side in landscape mode.  This
@@ -517,7 +523,7 @@
                     // Select the first bubble text view in the current page of the workspace
                     final CellLayout layout = (CellLayout) workspace.getChildAt(pageIndex);
                     final CellLayoutChildren children = layout.getChildrenLayout();
-                    final View newIcon = getBubbleTextViewInDirection(layout, children, -1, 1);
+                    final View newIcon = getIconInDirection(layout, children, -1, 1);
                     if (newIcon != null) {
                         newIcon.requestFocus();
                     } else {
@@ -569,38 +575,41 @@
         return views;
     }
     /**
-     * Private helper method to find the index of the next BubbleTextView in the delta direction.
+     * Private helper method to find the index of the next BubbleTextView or FolderIcon in the 
+     * direction delta.
+     * 
      * @param delta either -1 or 1 depending on the direction we want to search
      */
-    private static View findIndexOfBubbleTextView(ArrayList<View> views, int i, int delta) {
+    private static View findIndexOfIcon(ArrayList<View> views, int i, int delta) {
         // Then we find the next BubbleTextView offset by delta from i
         final int count = views.size();
         int newI = i + delta;
         while (0 <= newI && newI < count) {
             View newV = views.get(newI);
-            if (newV instanceof BubbleTextView) {
+            if (newV instanceof BubbleTextView || newV instanceof FolderIcon) {
                 return newV;
             }
             newI += delta;
         }
         return null;
     }
-    private static View getBubbleTextViewInDirection(CellLayout layout, ViewGroup parent, int i,
+    private static View getIconInDirection(CellLayout layout, ViewGroup parent, int i,
             int delta) {
         final ArrayList<View> views = getCellLayoutChildrenSortedSpatially(layout, parent);
-        return findIndexOfBubbleTextView(views, i, delta);
+        return findIndexOfIcon(views, i, delta);
     }
-    private static View getBubbleTextViewInDirection(CellLayout layout, ViewGroup parent, View v,
+    private static View getIconInDirection(CellLayout layout, ViewGroup parent, View v,
             int delta) {
         final ArrayList<View> views = getCellLayoutChildrenSortedSpatially(layout, parent);
-        return findIndexOfBubbleTextView(views, views.indexOf(v), delta);
+        return findIndexOfIcon(views, views.indexOf(v), delta);
     }
     /**
-     * Private helper method to find the next closest BubbleTextView in the delta direction on the
-     * next line.
+     * Private helper method to find the next closest BubbleTextView or FolderIcon in the direction 
+     * delta on the next line.
+     * 
      * @param delta either -1 or 1 depending on the line and direction we want to search
      */
-    private static View getClosestBubbleTextViewOnLine(CellLayout layout, ViewGroup parent, View v,
+    private static View getClosestIconOnLine(CellLayout layout, ViewGroup parent, View v,
             int lineDelta) {
         final ArrayList<View> views = getCellLayoutChildrenSortedSpatially(layout, parent);
         final CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams();
@@ -617,7 +626,8 @@
                 View newV = views.get(index);
                 CellLayout.LayoutParams tmpLp = (CellLayout.LayoutParams) newV.getLayoutParams();
                 boolean satisfiesRow = (lineDelta < 0) ? (tmpLp.cellY < row) : (tmpLp.cellY > row);
-                if (satisfiesRow && newV instanceof BubbleTextView) {
+                if (satisfiesRow &&
+                        (newV instanceof BubbleTextView || newV instanceof FolderIcon)) {
                     float tmpDistance = (float) Math.sqrt(Math.pow(tmpLp.cellX - lp.cellX, 2) +
                             Math.pow(tmpLp.cellY - lp.cellY, 2));
                     if (tmpDistance < closestDistance) {
@@ -639,17 +649,15 @@
     }
 
     /**
-     * Handles key events in a Workspace containing BubbleTextView.
+     * Handles key events in a Workspace containing.
      */
-    static boolean handleBubbleTextViewKeyEvent(BubbleTextView v, int keyCode, KeyEvent e) {
+    static boolean handleIconKeyEvent(View v, int keyCode, KeyEvent e) {
         CellLayoutChildren parent = (CellLayoutChildren) v.getParent();
         final CellLayout layout = (CellLayout) parent.getParent();
         final Workspace workspace = (Workspace) layout.getParent();
         final ViewGroup launcher = (ViewGroup) workspace.getParent();
         final ViewGroup tabs = (ViewGroup) launcher.findViewById(R.id.qsb_bar);
         final ViewGroup hotseat = (ViewGroup) launcher.findViewById(R.id.hotseat);
-        int iconIndex = parent.indexOfChild(v);
-        int iconCount = parent.getChildCount();
         int pageIndex = workspace.indexOfChild(layout);
         int pageCount = workspace.getChildCount();
 
@@ -660,13 +668,13 @@
             case KeyEvent.KEYCODE_DPAD_LEFT:
                 if (handleKeyEvent) {
                     // Select the previous icon or the last icon on the previous page if possible
-                    View newIcon = getBubbleTextViewInDirection(layout, parent, v, -1);
+                    View newIcon = getIconInDirection(layout, parent, v, -1);
                     if (newIcon != null) {
                         newIcon.requestFocus();
                     } else {
                         if (pageIndex > 0) {
                             parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
-                            newIcon = getBubbleTextViewInDirection(layout, parent,
+                            newIcon = getIconInDirection(layout, parent,
                                     parent.getChildCount(), -1);
                             if (newIcon != null) {
                                 newIcon.requestFocus();
@@ -682,13 +690,13 @@
             case KeyEvent.KEYCODE_DPAD_RIGHT:
                 if (handleKeyEvent) {
                     // Select the next icon or the first icon on the next page if possible
-                    View newIcon = getBubbleTextViewInDirection(layout, parent, v, 1);
+                    View newIcon = getIconInDirection(layout, parent, v, 1);
                     if (newIcon != null) {
                         newIcon.requestFocus();
                     } else {
                         if (pageIndex < (pageCount - 1)) {
                             parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
-                            newIcon = getBubbleTextViewInDirection(layout, parent, -1, 1);
+                            newIcon = getIconInDirection(layout, parent, -1, 1);
                             if (newIcon != null) {
                                 newIcon.requestFocus();
                             } else {
@@ -703,7 +711,7 @@
             case KeyEvent.KEYCODE_DPAD_UP:
                 if (handleKeyEvent) {
                     // Select the closest icon in the previous line, otherwise select the tab bar
-                    View newIcon = getClosestBubbleTextViewOnLine(layout, parent, v, -1);
+                    View newIcon = getClosestIconOnLine(layout, parent, v, -1);
                     if (newIcon != null) {
                         newIcon.requestFocus();
                         wasHandled = true;
@@ -715,7 +723,7 @@
             case KeyEvent.KEYCODE_DPAD_DOWN:
                 if (handleKeyEvent) {
                     // Select the closest icon in the next line, otherwise select the button bar
-                    View newIcon = getClosestBubbleTextViewOnLine(layout, parent, v, 1);
+                    View newIcon = getClosestIconOnLine(layout, parent, v, 1);
                     if (newIcon != null) {
                         newIcon.requestFocus();
                         wasHandled = true;
@@ -730,7 +738,7 @@
                     // if there is no previous page
                     if (pageIndex > 0) {
                         parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
-                        View newIcon = getBubbleTextViewInDirection(layout, parent, -1, 1);
+                        View newIcon = getIconInDirection(layout, parent, -1, 1);
                         if (newIcon != null) {
                             newIcon.requestFocus();
                         } else {
@@ -738,7 +746,7 @@
                             workspace.snapToPage(pageIndex - 1);
                         }
                     } else {
-                        View newIcon = getBubbleTextViewInDirection(layout, parent, -1, 1);
+                        View newIcon = getIconInDirection(layout, parent, -1, 1);
                         if (newIcon != null) {
                             newIcon.requestFocus();
                         }
@@ -752,7 +760,7 @@
                     // if there is no previous page
                     if (pageIndex < (pageCount - 1)) {
                         parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
-                        View newIcon = getBubbleTextViewInDirection(layout, parent, -1, 1);
+                        View newIcon = getIconInDirection(layout, parent, -1, 1);
                         if (newIcon != null) {
                             newIcon.requestFocus();
                         } else {
@@ -760,7 +768,7 @@
                             workspace.snapToPage(pageIndex + 1);
                         }
                     } else {
-                        View newIcon = getBubbleTextViewInDirection(layout, parent,
+                        View newIcon = getIconInDirection(layout, parent,
                                 parent.getChildCount(), -1);
                         if (newIcon != null) {
                             newIcon.requestFocus();
@@ -772,7 +780,7 @@
             case KeyEvent.KEYCODE_MOVE_HOME:
                 if (handleKeyEvent) {
                     // Select the first icon on this page
-                    View newIcon = getBubbleTextViewInDirection(layout, parent, -1, 1);
+                    View newIcon = getIconInDirection(layout, parent, -1, 1);
                     if (newIcon != null) {
                         newIcon.requestFocus();
                     }
@@ -782,7 +790,90 @@
             case KeyEvent.KEYCODE_MOVE_END:
                 if (handleKeyEvent) {
                     // Select the last icon on this page
-                    View newIcon = getBubbleTextViewInDirection(layout, parent,
+                    View newIcon = getIconInDirection(layout, parent,
+                            parent.getChildCount(), -1);
+                    if (newIcon != null) {
+                        newIcon.requestFocus();
+                    }
+                }
+                wasHandled = true;
+                break;
+            default: break;
+        }
+        return wasHandled;
+    }
+
+    /**
+     * Handles key events for items in a Folder.
+     */
+    static boolean handleFolderKeyEvent(View v, int keyCode, KeyEvent e) {
+        CellLayoutChildren parent = (CellLayoutChildren) v.getParent();
+        final CellLayout layout = (CellLayout) parent.getParent();
+        final Folder folder = (Folder) layout.getParent();
+        View title = folder.mFolderName;
+
+        final int action = e.getAction();
+        final boolean handleKeyEvent = (action != KeyEvent.ACTION_UP);
+        boolean wasHandled = false;
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+                if (handleKeyEvent) {
+                    // Select the previous icon
+                    View newIcon = getIconInDirection(layout, parent, v, -1);
+                    if (newIcon != null) {
+                        newIcon.requestFocus();
+                    }
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+                if (handleKeyEvent) {
+                    // Select the next icon
+                    View newIcon = getIconInDirection(layout, parent, v, 1);
+                    if (newIcon != null) {
+                        newIcon.requestFocus();
+                    } else {
+                        title.requestFocus();
+                    }
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_DPAD_UP:
+                if (handleKeyEvent) {
+                    // Select the closest icon in the previous line
+                    View newIcon = getClosestIconOnLine(layout, parent, v, -1);
+                    if (newIcon != null) {
+                        newIcon.requestFocus();
+                    }
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+                if (handleKeyEvent) {
+                    // Select the closest icon in the next line
+                    View newIcon = getClosestIconOnLine(layout, parent, v, 1);
+                    if (newIcon != null) {
+                        newIcon.requestFocus();
+                    } else {
+                        title.requestFocus();
+                    }
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_MOVE_HOME:
+                if (handleKeyEvent) {
+                    // Select the first icon on this page
+                    View newIcon = getIconInDirection(layout, parent, -1, 1);
+                    if (newIcon != null) {
+                        newIcon.requestFocus();
+                    }
+                }
+                wasHandled = true;
+                break;
+            case KeyEvent.KEYCODE_MOVE_END:
+                if (handleKeyEvent) {
+                    // Select the last icon on this page
+                    View newIcon = getIconInDirection(layout, parent,
                             parent.getChildCount(), -1);
                     if (newIcon != null) {
                         newIcon.requestFocus();
diff --git a/src/com/android/launcher2/Folder.java b/src/com/android/launcher2/Folder.java
index 71b7081..1b863de 100644
--- a/src/com/android/launcher2/Folder.java
+++ b/src/com/android/launcher2/Folder.java
@@ -93,7 +93,6 @@
     private int[] mEmptyCell = new int[2];
     private Alarm mReorderAlarm = new Alarm();
     private Alarm mOnExitAlarm = new Alarm();
-    private TextView mFolderName;
     private int mFolderNameHeight;
     private Rect mHitRect = new Rect();
     private Rect mTempRect = new Rect();
@@ -101,6 +100,7 @@
     private boolean mDeleteFolderOnDropCompleted = false;
     private boolean mSuppressFolderDeletion = false;
     private boolean mItemAddedBackToSelfViaIcon = false;
+    FolderEditText mFolderName;
 
     private boolean mIsEditingName = false;
     private InputMethodManager mInputMethodManager;
@@ -134,8 +134,11 @@
         if (sHintText == null) {
             sHintText = res.getString(R.string.folder_hint_text);
         }
-
         mLauncher = (Launcher) context;
+        // We need this view to be focusable in touch mode so that when text editing of the folder
+        // name is complete, we have something to focus on, thus hiding the cursor and giving
+        // reliable behvior when clicking the text field (since it will always gain focus on click).
+        setFocusableInTouchMode(true);
     }
 
     @Override
@@ -143,7 +146,8 @@
         super.onFinishInflate();
         mContent = (CellLayout) findViewById(R.id.folder_content);
         mContent.setGridSize(0, 0);
-        mFolderName = (TextView) findViewById(R.id.folder_name);
+        mFolderName = (FolderEditText) findViewById(R.id.folder_name);
+        mFolderName.setFolder(this);
 
         // We find out how tall the text view wants to be (it is set to wrap_content), so that
         // we can allocate the appropriate amount of space for it.
@@ -153,7 +157,6 @@
 
         // We disable action mode for now since it messes up the view on phones
         mFolderName.setCustomSelectionActionModeCallback(mActionModeCallback);
-        mFolderName.setCursorVisible(false);
         mFolderName.setOnEditorActionListener(this);
         mFolderName.setSelectAllOnFocus(true);
         mFolderName.setInputType(mFolderName.getInputType() |
@@ -233,7 +236,6 @@
 
     public void startEditingFolderName() {
         mFolderName.setHint("");
-        mFolderName.setCursorVisible(true);
         mIsEditingName = true;
     }
 
@@ -248,8 +250,11 @@
         // gets saved.
         mInfo.setTitle(mFolderName.getText().toString());
         LauncherModel.updateItemInDatabase(mLauncher, mInfo);
-        mFolderName.setCursorVisible(false);
-        mFolderName.clearFocus();
+
+        // In order to clear the focus from the text field, we set the focus on ourself. This
+        // ensures that every time the field is clicked, focus is gained, giving reliable behavior.
+        requestFocus();
+
         Selection.setSelection((Spannable) mFolderName.getText(), 0, 0);
         mIsEditingName = false;
     }
@@ -320,6 +325,7 @@
         }
 
         mItemsInvalidated = true;
+        updateTextViewFocus();
         mInfo.addListener(this);
 
         if (!sDefaultFolderName.contentEquals(mInfo.title)) {
@@ -410,12 +416,20 @@
                 if (cling != null) {
                     cling.bringToFront();
                 }
+                setFocusOnFirstChild();
             }
         });
         oa.setDuration(mExpandDuration);
         oa.start();
     }
 
+    private void setFocusOnFirstChild() {
+        View firstChild = mContent.getChildAt(0, 0);
+        if (firstChild != null) {
+            firstChild.requestFocus();
+        }
+    }
+
     public void animateClosed() {
         if (!(getParent() instanceof DragLayer)) return;
 
@@ -512,6 +526,7 @@
         CellLayout.LayoutParams lp =
             new CellLayout.LayoutParams(item.cellX, item.cellY, item.spanX, item.spanY);
         boolean insert = false;
+        textView.setOnKeyListener(new FolderKeyEventListener());
         mContent.addViewToCellLayout(textView, insert ? 0 : -1, (int)item.id, lp, true);
         return true;
     }
@@ -842,6 +857,7 @@
         parent.removeView(this);
         mDragController.removeDropTarget((DropTarget) this);
         clearFocus();
+        mFolderIcon.requestFocus();
 
         if (mRearrangeOnClose) {
             setupContentForNumItems(getItemCount());
@@ -888,6 +904,19 @@
         }
     }
 
+    // This method keeps track of the last item in the folder for the purposes
+    // of keyboard focus
+    private void updateTextViewFocus() {
+        View lastChild = getItemAt(getItemCount() - 1);
+        getItemAt(getItemCount() - 1);
+        if (lastChild != null) {
+            mFolderName.setNextFocusDownId(lastChild.getId());
+            mFolderName.setNextFocusRightId(lastChild.getId());
+            mFolderName.setNextFocusLeftId(lastChild.getId());
+            mFolderName.setNextFocusUpId(lastChild.getId());
+        }
+    }
+
     public void onDrop(DragObject d) {
         ShortcutInfo item;
         if (d.dragInfo instanceof ApplicationInfo) {
@@ -963,7 +992,9 @@
     }
 
     public void onItemsChanged() {
+        updateTextViewFocus();
     }
+
     public void onTitleChanged(CharSequence title) {
     }
 
diff --git a/src/com/android/launcher2/FolderEditText.java b/src/com/android/launcher2/FolderEditText.java
new file mode 100644
index 0000000..13169bd
--- /dev/null
+++ b/src/com/android/launcher2/FolderEditText.java
@@ -0,0 +1,36 @@
+package com.android.launcher2;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.widget.EditText;
+
+public class FolderEditText extends EditText {
+
+    private Folder mFolder;
+
+    public FolderEditText(Context context) {
+        super(context);
+    }
+
+    public FolderEditText(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public FolderEditText(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public void setFolder(Folder folder) {
+        mFolder = folder;
+    }
+
+    @Override
+    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+        // Catch the back button on the soft keyboard so that we can just close the activity
+        if (event.getKeyCode() == android.view.KeyEvent.KEYCODE_BACK) {
+            mFolder.doneEditingFolderName(true);
+        }
+        return super.onKeyPreIme(keyCode, event);
+    }
+}
diff --git a/src/com/android/launcher2/FolderInfo.java b/src/com/android/launcher2/FolderInfo.java
index 3ae31d2..f597076 100644
--- a/src/com/android/launcher2/FolderInfo.java
+++ b/src/com/android/launcher2/FolderInfo.java
@@ -56,6 +56,7 @@
         for (int i = 0; i < listeners.size(); i++) {
             listeners.get(i).onAdd(item);
         }
+        itemsChanged();
     }
 
     /**
@@ -68,6 +69,7 @@
         for (int i = 0; i < listeners.size(); i++) {
             listeners.get(i).onRemove(item);
         }
+        itemsChanged();
     }
 
     public void setTitle(CharSequence title) {
diff --git a/src/com/android/launcher2/Hotseat.java b/src/com/android/launcher2/Hotseat.java
index b9be6dc..85412c6 100644
--- a/src/com/android/launcher2/Hotseat.java
+++ b/src/com/android/launcher2/Hotseat.java
@@ -58,7 +58,7 @@
 
     public void setup(Launcher launcher) {
         mLauncher = launcher;
-        setOnKeyListener(new HotseatBubbleTextViewKeyEventListener());
+        setOnKeyListener(new HotseatIconKeyEventListener());
     }
 
     CellLayout getLayout() {
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index 2997d68..02fccc5 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -1147,16 +1147,18 @@
             // also will cancel mWaitingForResult.
             closeSystemDialogs();
 
-            closeFolder();
-
             boolean alreadyOnHome = ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
                         != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
 
+            Folder openFolder = mWorkspace.getOpenFolder();
             // In all these cases, only animate if we're already on home
             mWorkspace.exitWidgetResizeMode();
-            if (alreadyOnHome && mState == State.WORKSPACE && !mWorkspace.isTouchActive()) {
+            if (alreadyOnHome && mState == State.WORKSPACE && !mWorkspace.isTouchActive() &&
+                    openFolder == null) {
                 mWorkspace.moveToDefaultScreen(true);
             }
+
+            closeFolder();
             exitSpringLoadedDragMode();
             showWorkspace(alreadyOnHome);
 
@@ -1876,6 +1878,9 @@
     public void closeFolder() {
         Folder folder = mWorkspace.getOpenFolder();
         if (folder != null) {
+            if (folder.isEditingName()) {
+                folder.dismissEditingName();
+            }
             closeFolder(folder);
 
             // Dismiss the folder cling
diff --git a/src/com/android/launcher2/PagedViewWidget.java b/src/com/android/launcher2/PagedViewWidget.java
index 053c2ea..4e06570 100644
--- a/src/com/android/launcher2/PagedViewWidget.java
+++ b/src/com/android/launcher2/PagedViewWidget.java
@@ -96,6 +96,20 @@
         setClipToPadding(false);
     }
 
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+
+        final ImageView image = (ImageView) findViewById(R.id.widget_preview);
+        if (image != null) {
+            FastBitmapDrawable preview = (FastBitmapDrawable) image.getDrawable();
+            if (preview != null && preview.getBitmap() != null) {
+                preview.getBitmap().recycle();
+            }
+            image.setImageDrawable(null);
+        }
+    }
+
     public void applyFromAppWidgetProviderInfo(AppWidgetProviderInfo info,
             FastBitmapDrawable preview, int maxWidth, int[] cellSpan,
             HolographicOutlineHelper holoOutlineHelper) {
@@ -109,11 +123,9 @@
         mPreviewImageView = image;
         final TextView name = (TextView) findViewById(R.id.widget_name);
         name.setText(info.label);
-        name.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
         final TextView dims = (TextView) findViewById(R.id.widget_dims);
         if (dims != null) {
             dims.setText(String.format(mDimensionsFormatString, cellSpan[0], cellSpan[1]));
-            dims.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
         }
     }
 
@@ -127,11 +139,9 @@
         mPreviewImageView = image;
         final TextView name = (TextView) findViewById(R.id.widget_name);
         name.setText(label);
-        name.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
         final TextView dims = (TextView) findViewById(R.id.widget_dims);
         if (dims != null) {
             dims.setText(String.format(mDimensionsFormatString, 1, 1));
-            dims.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
         }
     }
 
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index bf18456..873c038 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -469,7 +469,7 @@
             }
 
             layout = (CellLayout) getChildAt(screen);
-            child.setOnKeyListener(new BubbleTextViewKeyEventListener());
+            child.setOnKeyListener(new IconKeyEventListener());
         }
 
         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
@@ -3051,6 +3051,9 @@
             }
             cellLayout.onDropChild(mDragInfo.cell);
         }
+        if (d.cancelled &&  mDragInfo.cell != null) {
+                mDragInfo.cell.setVisibility(VISIBLE);
+        }
         mDragOutline = null;
         mDragInfo = null;
     }