Merge "Adding workaround for crashes (5087036, 4556344) now that widget previews are loaded asynchronously."
diff --git a/res/xml/default_workspace.xml b/res/xml/default_workspace.xml
index 0e96a67..d509490 100644
--- a/res/xml/default_workspace.xml
+++ b/res/xml/default_workspace.xml
@@ -61,5 +61,4 @@
         launcher:screen="3"
         launcher:x="3"
         launcher:y="0" />
-
 </favorites>
diff --git a/src/com/android/launcher2/Folder.java b/src/com/android/launcher2/Folder.java
index 014356b..780d0ed 100644
--- a/src/com/android/launcher2/Folder.java
+++ b/src/com/android/launcher2/Folder.java
@@ -100,6 +100,10 @@
     private Rect mTempRect = new Rect();
     private boolean mFirstOpen = true;
 
+    // Internal variable to track whether the folder was destroyed due to only a single
+    // item remaining
+    private boolean mDestroyed = false;
+
     private boolean mIsEditingName = false;
     private InputMethodManager mInputMethodManager;
 
@@ -514,7 +518,8 @@
         // by another item. If it is, we need to find the next available slot and assign
         // it that position. This is an issue when upgrading from the old Folders implementation
         // which could contain an unlimited number of items.
-        if (mContent.getChildAt(item.cellX, item.cellY) != null) {
+        if (mContent.getChildAt(item.cellX, item.cellY) != null ||
+                item.cellX < 0 || item.cellY < 0) {
             if (!findAndSetEmptyCells(item)) {
                 return false;
             }
@@ -658,17 +663,16 @@
         mCurrentDragInfo = null;
         mCurrentDragView = null;
         mSuppressOnAdd = false;
+        if (target != this) {
+            mOnExitAlarm.cancelAlarm();
+            completeDragExit();
+        }
+
         if (!success) {
-            if (d.dragView != null) {
-                if (target instanceof CellLayout) {
-                    // TODO: we should animate the item back to the folder in this case
-                }
-            }
-            // TODO: if the drag fails, we need to re-add the item
-        } else {
-            if (target != this) {
-                mOnExitAlarm.cancelAlarm();
-                completeDragExit();
+            if (!mDestroyed) {
+                mFolderIcon.onDrop(d);
+            } else {
+                // TODO: if the folder was removed, recreate it
             }
         }
     }
@@ -842,6 +846,7 @@
     private void replaceFolderWithFinalItem() {
         ItemInfo finalItem = null;
 
+        mDestroyed = true;
         if (getItemCount() == 1) {
             finalItem = mInfo.contents.get(0);
         }
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index 0f302a0..cd62ee1 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -51,6 +51,7 @@
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.AsyncTask;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
@@ -212,12 +213,6 @@
 
     private static HashMap<Long, FolderInfo> sFolders = new HashMap<Long, FolderInfo>();
 
-    // Hotseats (quick-launch icons next to AllApps)
-    private String[] mHotseatConfig = null;
-    private Intent[] mHotseats = null;
-    private Drawable[] mHotseatIcons = null;
-    private CharSequence[] mHotseatLabels = null;
-
     private Intent mAppMarketIntent = null;
 
     // Related to the auto-advancing of widgets
@@ -436,16 +431,6 @@
         }
     }
 
-    // Note: This doesn't do all the client-id magic that BrowserProvider does
-    // in Browser. (http://b/2425179)
-    private Uri getDefaultBrowserUri() {
-        String url = getString(R.string.default_browser_url);
-        if (url.indexOf("{CID}") != -1) {
-            url = url.replace("{CID}", "android-google");
-        }
-        return Uri.parse(url);
-    }
-
     /**
      * Returns whether we should delay spring loaded mode -- for shortcuts and widgets that have
      * a configuration step, this allows the proper animations to run after other transitions.
@@ -657,7 +642,6 @@
         final long pendingAddContainer = savedState.getLong(RUNTIME_STATE_PENDING_ADD_CONTAINER, -1);
         final int pendingAddScreen = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SCREEN, -1);
 
-
         if (pendingAddContainer != ItemInfo.NO_ID && pendingAddScreen > -1) {
             mPendingAddInfo.container = pendingAddContainer;
             mPendingAddInfo.screen = pendingAddScreen;
@@ -1922,27 +1906,6 @@
         showDialog(DIALOG_CREATE_SHORTCUT);
     }
 
-    private void pickShortcut() {
-        // Insert extra item to handle picking application
-        Bundle bundle = new Bundle();
-
-        ArrayList<String> shortcutNames = new ArrayList<String>();
-        shortcutNames.add(getString(R.string.group_applications));
-        bundle.putStringArrayList(Intent.EXTRA_SHORTCUT_NAME, shortcutNames);
-
-        ArrayList<ShortcutIconResource> shortcutIcons = new ArrayList<ShortcutIconResource>();
-        shortcutIcons.add(ShortcutIconResource.fromContext(Launcher.this,
-                        R.drawable.ic_launcher_application));
-        bundle.putParcelableArrayList(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, shortcutIcons);
-
-        Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
-        pickIntent.putExtra(Intent.EXTRA_INTENT, new Intent(Intent.ACTION_CREATE_SHORTCUT));
-        pickIntent.putExtra(Intent.EXTRA_TITLE, getText(R.string.title_select_shortcut));
-        pickIntent.putExtras(bundle);
-
-        startActivityForResult(pickIntent, REQUEST_PICK_SHORTCUT);
-    }
-
     private class RenameFolder {
         private EditText mInput;
 
diff --git a/src/com/android/launcher2/LauncherProvider.java b/src/com/android/launcher2/LauncherProvider.java
index 96cc63c..6ca16de 100644
--- a/src/com/android/launcher2/LauncherProvider.java
+++ b/src/com/android/launcher2/LauncherProvider.java
@@ -20,46 +20,45 @@
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
-import android.content.ContentProvider;
-import android.content.Context;
-import android.content.ContentValues;
-import android.content.Intent;
 import android.content.ComponentName;
-import android.content.ContentUris;
+import android.content.ContentProvider;
 import android.content.ContentResolver;
-import android.content.res.Resources;
-import android.content.res.XmlResourceParser;
-import android.content.res.TypedArray;
-import android.content.pm.PackageManager;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ActivityInfo;
-import android.content.SharedPreferences;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteStatement;
-import android.database.sqlite.SQLiteQueryBuilder;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
 import android.database.Cursor;
 import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.database.sqlite.SQLiteStatement;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Xml;
-import android.util.AttributeSet;
-import android.net.Uri;
-import android.text.TextUtils;
-import android.provider.Settings;
+
+import com.android.internal.util.XmlUtils;
+import com.android.launcher.R;
+import com.android.launcher2.LauncherSettings.Favorites;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
 import java.net.URISyntaxException;
+import java.util.ArrayList;
 import java.util.List;
 
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlPullParser;
-
-import com.android.internal.util.XmlUtils;
-import com.android.launcher2.LauncherSettings.Favorites;
-
-import com.android.launcher.R;
-
 public class LauncherProvider extends ContentProvider {
     private static final String TAG = "Launcher.LauncherProvider";
     private static final boolean LOGD = false;
@@ -123,6 +122,12 @@
         return db.insert(table, nullColumnHack, values);
     }
 
+    private static void deleteId(SQLiteDatabase db, long id) {
+        Uri uri = LauncherSettings.Favorites.getContentUri(id, false);
+        SqlArguments args = new SqlArguments(uri, null, null);
+        db.delete(args.table, args.where, args.args);
+    }
+
     @Override
     public Uri insert(Uri uri, ContentValues initialValues) {
         SqlArguments args = new SqlArguments(uri);
@@ -199,6 +204,7 @@
         private static final String TAG_SEARCH = "search";
         private static final String TAG_APPWIDGET = "appwidget";
         private static final String TAG_SHORTCUT = "shortcut";
+        private static final String TAG_FOLDER = "folder";
 
         private final Context mContext;
         private final AppWidgetHost mAppWidgetHost;
@@ -741,10 +747,9 @@
                         values.put(LauncherSettings.Favorites.CELLX, x);
                         values.put(LauncherSettings.Favorites.CELLY, y);
 
-                        System.out.println("Adding item to container: " + container);
-
                         if (TAG_FAVORITE.equals(name)) {
-                            added = addAppShortcut(db, values, a, packageManager, intent);
+                            long id = addAppShortcut(db, values, a, packageManager, intent);
+                            added = id >= 0;
                         } else if (TAG_SEARCH.equals(name)) {
                             added = addSearchWidget(db, values);
                         } else if (TAG_CLOCK.equals(name)) {
@@ -752,12 +757,66 @@
                         } else if (TAG_APPWIDGET.equals(name)) {
                             added = addAppWidget(db, values, a, packageManager);
                         } else if (TAG_SHORTCUT.equals(name)) {
-                            added = addUriShortcut(db, values, a);
+                            long id = addUriShortcut(db, values, a);
+                            added = id >= 0;
+                        } else if (TAG_FOLDER.equals(name)) {
+                            String title;
+                            int titleResId =  a.getResourceId(R.styleable.Favorite_title, -1);
+                            if (titleResId != -1) {
+                                title = mContext.getResources().getString(titleResId);
+                            } else {
+                                title = mContext.getResources().getString(R.string.folder_name);
+                            }
+                            values.put(LauncherSettings.Favorites.TITLE, title);
+                            long folderId = addFolder(db, values);
+                            added = folderId >= 0;
+
+                            ArrayList<Long> folderItems = new ArrayList<Long>();
+
+                            int folderDepth = parser.getDepth();
+                            while ((type = parser.next()) != XmlPullParser.END_TAG ||
+                                    parser.getDepth() > folderDepth) {
+                                if (type != XmlPullParser.START_TAG) {
+                                    continue;
+                                }
+                                final String folder_item_name = parser.getName();
+
+                                TypedArray ar = mContext.obtainStyledAttributes(attrs,
+                                        R.styleable.Favorite);
+                                values.clear();
+                                values.put(LauncherSettings.Favorites.CONTAINER, folderId);
+
+                                if (TAG_FAVORITE.equals(folder_item_name) && folderId >= 0) {
+                                    long id =
+                                        addAppShortcut(db, values, ar, packageManager, intent);
+                                    if (id >= 0) {
+                                        folderItems.add(id);
+                                    }
+                                } else if (TAG_SHORTCUT.equals(folder_item_name) && folderId >= 0) {
+                                    long id = addUriShortcut(db, values, ar);
+                                    if (id >= 0) {
+                                        folderItems.add(id);
+                                    }
+                                } else {
+                                    throw new RuntimeException("Folders can " +
+                                            "contain only shortcuts");
+                                }
+                                ar.recycle();
+                            }
+                            // We can only have folders with >= 2 items, so we need to remove the
+                            // folder and clean up if less than 2 items were included, or some
+                            // failed to add, and less than 2 were actually added
+                            if (folderItems.size() < 2 && folderId >= 0) {
+                                // We just delete the folder and any items that made it
+                                deleteId(db, folderId);
+                                if (folderItems.size() > 0) {
+                                    deleteId(db, folderItems.get(0));
+                                }
+                                added = false;
+                            }
                         }
                     }
-
                     if (added) i++;
-
                     a.recycle();
                 }
             } catch (XmlPullParserException e) {
@@ -771,9 +830,9 @@
             return i;
         }
 
-        private boolean addAppShortcut(SQLiteDatabase db, ContentValues values, TypedArray a,
+        private long addAppShortcut(SQLiteDatabase db, ContentValues values, TypedArray a,
                 PackageManager packageManager, Intent intent) {
-
+            long id = -1;
             ActivityInfo info;
             String packageName = a.getString(R.styleable.Favorite_packageName);
             String className = a.getString(R.styleable.Favorite_className);
@@ -788,7 +847,7 @@
                     cn = new ComponentName(packages[0], className);
                     info = packageManager.getActivityInfo(cn, 0);
                 }
-
+                id = generateNewId();
                 intent.setComponent(cn);
                 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                         Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
@@ -798,13 +857,27 @@
                 values.put(Favorites.SPANX, 1);
                 values.put(Favorites.SPANY, 1);
                 values.put(Favorites._ID, generateNewId());
-                dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
+                if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
+                    return -1;
+                }
             } catch (PackageManager.NameNotFoundException e) {
                 Log.w(TAG, "Unable to add favorite: " + packageName +
                         "/" + className, e);
-                return false;
             }
-            return true;
+            return id;
+        }
+
+        private long addFolder(SQLiteDatabase db, ContentValues values) {
+            values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_FOLDER);
+            values.put(Favorites.SPANX, 1);
+            values.put(Favorites.SPANY, 1);
+            long id = generateNewId();
+            values.put(Favorites._ID, id);
+            if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) <= 0) {
+                return -1;
+            } else {
+                return id;
+            }
         }
 
         private ComponentName getSearchWidgetProvider() {
@@ -843,7 +916,7 @@
                     "com.android.alarmclock.AnalogAppWidgetProvider");
             return addAppWidget(db, values, cn, 2, 2);
         }
-        
+
         private boolean addAppWidget(SQLiteDatabase db, ContentValues values, TypedArray a,
                 PackageManager packageManager) {
 
@@ -902,8 +975,8 @@
             
             return allocatedAppWidgets;
         }
-        
-        private boolean addUriShortcut(SQLiteDatabase db, ContentValues values,
+
+        private long addUriShortcut(SQLiteDatabase db, ContentValues values,
                 TypedArray a) {
             Resources r = mContext.getResources();
 
@@ -917,14 +990,15 @@
                 intent = Intent.parseUri(uri, 0);
             } catch (URISyntaxException e) {
                 Log.w(TAG, "Shortcut has malformed uri: " + uri);
-                return false; // Oh well
+                return -1; // Oh well
             }
 
             if (iconResId == 0 || titleResId == 0) {
                 Log.w(TAG, "Shortcut is missing title or icon resource ID");
-                return false;
+                return -1;
             }
 
+            long id = generateNewId();
             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             values.put(Favorites.INTENT, intent.toUri(0));
             values.put(Favorites.TITLE, r.getString(titleResId));
@@ -934,10 +1008,12 @@
             values.put(Favorites.ICON_TYPE, Favorites.ICON_TYPE_RESOURCE);
             values.put(Favorites.ICON_PACKAGE, mContext.getPackageName());
             values.put(Favorites.ICON_RESOURCE, r.getResourceName(iconResId));
-            values.put(Favorites._ID, generateNewId());
-            dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
+            values.put(Favorites._ID, id);
 
-            return true;
+            if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
+                return -1;
+            }
+            return id;
         }
     }
     
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index e4865c2..10b218a 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -142,7 +142,6 @@
     private int[] mTempEstimate = new int[2];
     private float[] mDragViewVisualCenter = new float[2];
     private float[] mTempDragCoordinates = new float[2];
-    private float[] mTempTouchCoordinates = new float[2];
     private float[] mTempCellLayoutCenterCoordinates = new float[2];
     private float[] mTempDragBottomRightCoordinates = new float[2];
     private Matrix mTempInverseMatrix = new Matrix();
@@ -1290,12 +1289,6 @@
         }
     }
 
-    private boolean childLayersEnabled() {
-        boolean isSmallOrSpringloaded =
-            isSmall() || mIsSwitchingState || mState == State.SPRING_LOADED;
-        return isSmallOrSpringloaded || isPageMoving() || mIsDragOccuring;
-    }
-
     private void updateChildrenLayersEnabled() {
         boolean small =
             isSmall() || mIsSwitchingState || mState == State.SPRING_LOADED;
@@ -1713,6 +1706,14 @@
     }
 
     void unshrink(boolean animated, boolean springLoaded) {
+        if (mFirstLayout) {
+            // (mFirstLayout == "first layout has not happened yet")
+            // cancel any pending shrinks that were set earlier
+            mSwitchStateAfterFirstLayout = false;
+            mStateAfterFirstLayout = State.NORMAL;
+            return;
+        }
+
         if (isSmall()) {
             float finalScaleFactor = 1.0f;
             float finalBackgroundAlpha = 0.0f;
@@ -2058,7 +2059,6 @@
         final Bitmap b = createDragBitmap(child, new Canvas(), bitmapPadding);
 
         final int bmpWidth = b.getWidth();
-        final int bmpHeight = b.getHeight();
 
         mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY);
         final int dragLayerX = (int) mTempXY[0] + (child.getWidth() - bmpWidth) / 2;
@@ -2778,8 +2778,6 @@
 
         // Identify whether we have dragged over a side page
         if (isSmall()) {
-            int left = d.x - d.xOffset;
-            int top = d.y - d.yOffset;
             layout = findMatchingPageForDragOver(d.dragView, mDragViewVisualCenter[0],
                     mDragViewVisualCenter[1], true);
             if (layout != mDragTargetLayout) {
@@ -3109,7 +3107,6 @@
     }
     public void resetTransitionTransform(CellLayout layout) {
         if (isSwitchingState()) {
-            int index = indexOfChild(layout);
             mCurrentScaleX = layout.getScaleX();
             mCurrentScaleY = layout.getScaleY();
             mCurrentTranslationX = layout.getTranslationX();
@@ -3340,7 +3337,6 @@
             int count = layout.getChildCount();
             for (int i = 0; i < count; i++) {
                 View child = layout.getChildAt(i);
-                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
                 if (child instanceof Folder) {
                     Folder f = (Folder) child;
                     if (f.getInfo() == tag && f.getInfo().opened) {
@@ -3380,7 +3376,6 @@
     }
 
     void removeItems(final ArrayList<ApplicationInfo> apps) {
-        final int screenCount = getChildCount();
         final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext());
 
         final HashSet<String> packageNames = new HashSet<String>();