am 6de1ba33: Merge "Fixing crash in empty folders. (Bug 11627191)" into jb-ub-now-jolly-elf

* commit '6de1ba3377dd407e83f682edf9ae742416302772':
  Fixing crash in empty folders. (Bug 11627191)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 901b638..1e418ec 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -160,6 +160,13 @@
             </intent-filter>
         </receiver>
 
+        <receiver
+            android:name="com.android.launcher3.WallpaperChangedReceiver">
+            <intent-filter>
+                <action android:name="android.intent.action.WALLPAPER_CHANGED" />
+            </intent-filter>
+        </receiver>
+
         <!-- Intent received used to install shortcuts from other applications -->
         <receiver
             android:name="com.android.launcher3.InstallShortcutReceiver"
diff --git a/res/xml/default_workspace_no_all_apps.xml b/res/xml/default_workspace_no_all_apps.xml
new file mode 100644
index 0000000..7e1301c
--- /dev/null
+++ b/res/xml/default_workspace_no_all_apps.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
+    <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
+    <!-- Dialer Hangouts Maps Chrome Camera -->
+    <favorite
+        launcher:packageName="com.google.android.dialer"
+        launcher:className="com.google.android.dialer.extensions.GoogleDialtactsActivity"
+        launcher:container="-101"
+        launcher:screen="0"
+        launcher:x="0"
+        launcher:y="0" />
+    <favorite
+        launcher:packageName="com.google.android.talk"
+        launcher:className="com.google.android.talk.SigningInActivity"
+        launcher:container="-101"
+        launcher:screen="1"
+        launcher:x="1"
+        launcher:y="0" />
+    <favorite
+        launcher:packageName="com.google.android.apps.maps"
+        launcher:className="com.google.android.maps.MapsActivity"
+        launcher:container="-101"
+        launcher:screen="2"
+        launcher:x="2"
+        launcher:y="0"/>
+    <favorite
+        launcher:packageName="com.android.chrome"
+        launcher:className="com.google.android.apps.chrome.Main"
+        launcher:container="-101"
+        launcher:screen="3"
+        launcher:x="3"
+        launcher:y="0" />
+    <favorite
+        launcher:packageName="com.google.android.GoogleCamera"
+        launcher:className="com.android.camera.CameraLauncher"
+        launcher:container="-101"
+        launcher:screen="4"
+        launcher:x="4"
+        launcher:y="0" />
+</favorites>
+
diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java
index 688ff82..2865bc5 100644
--- a/src/com/android/launcher3/AppsCustomizePagedView.java
+++ b/src/com/android/launcher3/AppsCustomizePagedView.java
@@ -916,6 +916,16 @@
     }
 
     @Override
+    public boolean supportsAppInfoDropTarget() {
+        return true;
+    }
+
+    @Override
+    public boolean supportsDeleteDropTarget() {
+        return false;
+    }
+
+    @Override
     public float getIntrinsicIconScaleFactor() {
         LauncherAppState app = LauncherAppState.getInstance();
         DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index e3a154b..a8ac074 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -96,7 +96,7 @@
     }
 
     private boolean isAllAppsApplication(DragSource source, Object info) {
-        return (source instanceof AppsCustomizePagedView) && (info instanceof AppInfo);
+        return source.supportsAppInfoDropTarget() && (info instanceof AppInfo);
     }
     private boolean isAllAppsWidget(DragSource source, Object info) {
         if (source instanceof AppsCustomizePagedView) {
@@ -176,6 +176,7 @@
         boolean isVisible = true;
         boolean useUninstallLabel = !AppsCustomizePagedView.DISABLE_ALL_APPS &&
                 isAllAppsApplication(source, info);
+        boolean useDeleteLabel = !useUninstallLabel && source.supportsDeleteDropTarget();
 
         // If we are dragging an application from AppsCustomize, only show the control if we can
         // delete the app (it was downloaded), and rename the string to "uninstall" in such a case.
@@ -186,15 +187,17 @@
 
         if (useUninstallLabel) {
             setCompoundDrawablesRelativeWithIntrinsicBounds(mUninstallDrawable, null, null, null);
-        } else {
+        } else if (useDeleteLabel) {
             setCompoundDrawablesRelativeWithIntrinsicBounds(mRemoveDrawable, null, null, null);
+        } else {
+            isVisible = false;
         }
         mCurrentDrawable = (TransitionDrawable) getCurrentDrawable();
 
         mActive = isVisible;
         resetHoverColor();
         ((ViewGroup) getParent()).setVisibility(isVisible ? View.VISIBLE : View.GONE);
-        if (getText().length() > 0) {
+        if (isVisible && getText().length() > 0) {
             setText(useUninstallLabel ? R.string.delete_target_uninstall_label
                 : R.string.delete_target_label);
         }
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 9e1f1a7..511b718 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -73,6 +73,7 @@
     boolean isLandscape;
     boolean isTablet;
     boolean isLargeTablet;
+    boolean isLayoutRtl;
     boolean transposeLayoutWithOrientation;
 
     int desiredWorkspaceLeftRightMarginPx;
@@ -356,10 +357,11 @@
 
     void updateFromConfiguration(Context context, Resources resources, int wPx, int hPx,
                                  int awPx, int ahPx) {
-        isLandscape = (resources.getConfiguration().orientation ==
-                Configuration.ORIENTATION_LANDSCAPE);
+        Configuration configuration = resources.getConfiguration();
+        isLandscape = (configuration.orientation == Configuration.ORIENTATION_LANDSCAPE);
         isTablet = resources.getBoolean(R.bool.is_tablet);
         isLargeTablet = resources.getBoolean(R.bool.is_large_tablet);
+        isLayoutRtl = (configuration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
         widthPx = wPx;
         heightPx = hPx;
         availableWidthPx = awPx;
@@ -437,7 +439,13 @@
         Rect bounds = new Rect();
         if (orientation == CellLayout.LANDSCAPE &&
                 transposeLayoutWithOrientation) {
-            bounds.set(0, edgeMarginPx, searchBarSpaceHeightPx, availableHeightPx - edgeMarginPx);
+            if (isLayoutRtl) {
+                bounds.set(availableWidthPx - searchBarSpaceHeightPx, edgeMarginPx,
+                        availableWidthPx, availableHeightPx - edgeMarginPx);
+            } else {
+                bounds.set(0, edgeMarginPx, searchBarSpaceHeightPx,
+                        availableHeightPx - edgeMarginPx);
+            }
         } else {
             if (isTablet()) {
                 // Pad the left and right of the workspace to ensure consistent spacing
@@ -472,8 +480,13 @@
         if (orientation == CellLayout.LANDSCAPE &&
                 transposeLayoutWithOrientation) {
             // Pad the left and right of the workspace with search/hotseat bar sizes
-            padding.set(searchBarBounds.right, edgeMarginPx,
-                    hotseatBarHeightPx, edgeMarginPx);
+            if (isLayoutRtl) {
+                padding.set(hotseatBarHeightPx, edgeMarginPx,
+                        searchBarBounds.width(), edgeMarginPx);
+            } else {
+                padding.set(searchBarBounds.width(), edgeMarginPx,
+                        hotseatBarHeightPx, edgeMarginPx);
+            }
         } else {
             if (isTablet()) {
                 // Pad the left and right of the workspace to ensure consistent spacing
@@ -627,7 +640,7 @@
         lp = (FrameLayout.LayoutParams) hotseat.getLayoutParams();
         if (hasVerticalBarLayout) {
             // Vertical hotseat
-            lp.gravity = Gravity.RIGHT;
+            lp.gravity = Gravity.END;
             lp.width = hotseatBarHeightPx;
             lp.height = LayoutParams.MATCH_PARENT;
             hotseat.findViewById(R.id.layout).setPadding(0, 2 * edgeMarginPx, 0, 2 * edgeMarginPx);
@@ -703,7 +716,8 @@
                 paddingTB = Math.min(paddingTB, (int)((paddingLR + paddingTB) * 0.75f));
                 int maxAllAppsWidth = (allAppsNumCols * (allAppsCellWidthPx + 2 * paddingLR));
                 int gridPaddingLR = (availableWidthPx - maxAllAppsWidth) / 2;
-                if (gridPaddingLR > (allAppsCellWidthPx / 4)) {
+                // Only adjust the side paddings on landscape phones, or tablets
+                if ((isTablet() || isLandscape) && gridPaddingLR > (allAppsCellWidthPx / 4)) {
                     padding.left = padding.right = gridPaddingLR;
                 }
                 // The icons are centered, so we can't just offset by the page indicator height
diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java
index dc0ba90..56886a1 100644
--- a/src/com/android/launcher3/DragLayer.java
+++ b/src/com/android/launcher3/DragLayer.java
@@ -24,11 +24,13 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Canvas;
-import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
-import android.view.*;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.animation.DecelerateInterpolator;
@@ -106,21 +108,31 @@
         final int n = getChildCount();
         for (int i = 0; i < n; i++) {
             final View child = getChildAt(i);
-            final FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams) child.getLayoutParams();
-            if (child instanceof Insettable) {
-                ((Insettable)child).setInsets(insets);
-            } else {
-                flp.topMargin += (insets.top - mInsets.top);
-                flp.leftMargin += (insets.left - mInsets.left);
-                flp.rightMargin += (insets.right - mInsets.right);
-                flp.bottomMargin += (insets.bottom - mInsets.bottom);
-            }
-            child.setLayoutParams(flp);
+            setInsets(child, insets, mInsets);
         }
         mInsets.set(insets);
         return true; // I'll take it from here
     }
 
+    @Override
+    public void addView(View child, int index, android.view.ViewGroup.LayoutParams params) {
+        super.addView(child, index, params);
+        setInsets(child, mInsets, new Rect());
+    }
+
+    private void setInsets(View child, Rect newInsets, Rect oldInsets) {
+        final FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams) child.getLayoutParams();
+        if (child instanceof Insettable) {
+            ((Insettable) child).setInsets(newInsets);
+        } else {
+            flp.topMargin += (newInsets.top - oldInsets.top);
+            flp.leftMargin += (newInsets.left - oldInsets.left);
+            flp.rightMargin += (newInsets.right - oldInsets.right);
+            flp.bottomMargin += (newInsets.bottom - oldInsets.bottom);
+        }
+        child.setLayoutParams(flp);
+    }
+
     private boolean isEventOverFolderTextRegion(Folder folder, MotionEvent ev) {
         getDescendantRectRelativeToSelf(folder.getEditTextRegion(), mHitRect);
         if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
diff --git a/src/com/android/launcher3/DragSource.java b/src/com/android/launcher3/DragSource.java
index cca9ab1..7369eea 100644
--- a/src/com/android/launcher3/DragSource.java
+++ b/src/com/android/launcher3/DragSource.java
@@ -30,6 +30,17 @@
      */
     boolean supportsFlingToDelete();
 
+    /**
+     * @return whether items dragged from this source supports 'App Info'
+     */
+    boolean supportsAppInfoDropTarget();
+
+    /**
+     * @return whether items dragged from this source supports 'Delete' drop target (e.g. to remove
+     * a shortcut.
+     */
+    boolean supportsDeleteDropTarget();
+
     /*
      * @return the scale of the icons over the workspace icon size
      */
diff --git a/src/com/android/launcher3/DynamicGrid.java b/src/com/android/launcher3/DynamicGrid.java
index ce78553..dc01c69 100644
--- a/src/com/android/launcher3/DynamicGrid.java
+++ b/src/com/android/launcher3/DynamicGrid.java
@@ -60,17 +60,17 @@
         DEFAULT_ICON_SIZE_PX = pxFromDp(DEFAULT_ICON_SIZE_DP, dm);
         // Our phone profiles include the bar sizes in each orientation
         deviceProfiles.add(new DeviceProfile("Super Short Stubby",
-                255, 300,  2, 3,  48, 13, (hasAA ? 5 : 4), 48));
+                255, 300,  2, 3,  48, 13, (hasAA ? 5 : 5), 48));
         deviceProfiles.add(new DeviceProfile("Shorter Stubby",
-                255, 400,  3, 3,  48, 13, (hasAA ? 5 : 4), 48));
+                255, 400,  3, 3,  48, 13, (hasAA ? 5 : 5), 48));
         deviceProfiles.add(new DeviceProfile("Short Stubby",
-                275, 420,  3, 4,  48, 13, (hasAA ? 5 : 4), 48));
+                275, 420,  3, 4,  48, 13, (hasAA ? 5 : 5), 48));
         deviceProfiles.add(new DeviceProfile("Stubby",
-                255, 450,  3, 4,  48, 13, (hasAA ? 5 : 4), 48));
+                255, 450,  3, 4,  48, 13, (hasAA ? 5 : 5), 48));
         deviceProfiles.add(new DeviceProfile("Nexus S",
-                296, 491.33f,  4, 4,  48, 13, (hasAA ? 5 : 4), 48));
+                296, 491.33f,  4, 4,  48, 13, (hasAA ? 5 : 5), 48));
         deviceProfiles.add(new DeviceProfile("Nexus 4",
-                359, 518,  4, 4,  DEFAULT_ICON_SIZE_DP, 13, (hasAA ? 5 : 4), 56));
+                359, 518,  4, 4,  DEFAULT_ICON_SIZE_DP, 13, (hasAA ? 5 : 5), 56));
         // The tablet profile is odd in that the landscape orientation
         // also includes the nav bar on the side
         deviceProfiles.add(new DeviceProfile("Nexus 7",
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index 2fb8f92..9637bbb 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -82,7 +82,7 @@
     private int mState = STATE_NONE;
     private static final int REORDER_ANIMATION_DURATION = 230;
     private static final int REORDER_DELAY = 250;
-    private static final int ON_EXIT_CLOSE_DELAY = 800;
+    private static final int ON_EXIT_CLOSE_DELAY = 400;
     private boolean mRearrangeOnClose = false;
     private FolderIcon mFolderIcon;
     private int mMaxCountX;
@@ -810,6 +810,16 @@
         return true;
     }
 
+    @Override
+    public boolean supportsAppInfoDropTarget() {
+        return false;
+    }
+
+    @Override
+    public boolean supportsDeleteDropTarget() {
+        return true;
+    }
+
     public void onFlingToDelete(DragObject d, int x, int y, PointF vec) {
         // Do nothing
     }
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index 2ad43b6..374238c 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -62,10 +62,6 @@
         }
     }
 
-    private boolean isFromAllApps(DragSource source) {
-        return (source instanceof AppsCustomizePagedView);
-    }
-
     @Override
     public boolean acceptDrop(DragObject d) {
         // acceptDrop is called just before onDrop. We do the work here, rather than
@@ -93,7 +89,7 @@
         boolean isVisible = true;
 
         // Hide this button unless we are dragging something from AllApps
-        if (!isFromAllApps(source)) {
+        if (!source.supportsAppInfoDropTarget()) {
             isVisible = false;
         }
 
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index 8c4cefd..36ba6c1 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -27,7 +27,7 @@
 /**
  * Represents an item in the launcher.
  */
-class ItemInfo {
+public class ItemInfo {
     
     static final int NO_ID = -1;
     
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 4d802bd..e2301ba 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -45,6 +45,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.ContentObserver;
@@ -1764,7 +1765,7 @@
             }
 
             // Reset the apps customize page
-            if (mAppsCustomizeTabHost != null) {
+            if (!alreadyOnHome && mAppsCustomizeTabHost != null) {
                 mAppsCustomizeTabHost.reset();
             }
 
@@ -2374,7 +2375,7 @@
      * @param v The view that was clicked.
      */
     public void onClickAllAppsButton(View v) {
-        showAllApps(true, AppsCustomizePagedView.ContentType.Applications, true);
+        showAllApps(true, AppsCustomizePagedView.ContentType.Applications, false);
     }
 
     public void onTouchDownAllAppsButton(View v) {
@@ -4509,6 +4510,25 @@
                 DISMISS_CLING_DURATION, true);
     }
 
+    public ItemInfo createAppDragInfo(Intent appLaunchIntent) {
+        ResolveInfo ri = getPackageManager().resolveActivity(appLaunchIntent, 0);
+        if (ri == null) {
+            return null;
+        }
+        return new AppInfo(getPackageManager(), ri, mIconCache, null);
+    }
+
+    public ItemInfo createShortcutDragInfo(Intent shortcutIntent, CharSequence caption,
+            Bitmap icon) {
+        return new ShortcutInfo(shortcutIntent, caption, icon);
+    }
+
+    public void startDrag(View dragView, ItemInfo dragInfo, DragSource source) {
+        dragView.setTag(dragInfo);
+        mWorkspace.onDragStartedWithItem(dragView);
+        mWorkspace.beginDragShared(dragView, source);
+    }
+
     /**
      * Prints out out state for debugging.
      */
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 9a47eaa..5daa845 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -39,6 +39,7 @@
     private boolean mIsScreenLarge;
     private float mScreenDensity;
     private int mLongPressTimeout = 300;
+    private boolean mWallpaperChangedSinceLastCheck;
 
     private static WeakReference<LauncherProvider> sLauncherProvider;
     private static Context sContext;
@@ -217,6 +218,16 @@
         return mLongPressTimeout;
     }
 
+    public void onWallpaperChanged() {
+        mWallpaperChangedSinceLastCheck = true;
+    }
+
+    public boolean hasWallpaperChangedSinceLastCheck() {
+        boolean result = mWallpaperChangedSinceLastCheck;
+        mWallpaperChangedSinceLastCheck = false;
+        return result;
+    }
+
     @Override
     public void onAvailableSizeChanged(DeviceProfile grid) {
         Utilities.setIconSize(grid.iconSizePx);
diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java
index 2b5059b..83e4a60 100644
--- a/src/com/android/launcher3/LauncherBackupAgentHelper.java
+++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java
@@ -18,10 +18,14 @@
 
 import android.app.backup.BackupAgentHelper;
 import android.app.backup.BackupManager;
+import android.app.backup.SharedPreferencesBackupHelper;
 import android.content.Context;
+import android.content.SharedPreferences;
 
 public class LauncherBackupAgentHelper extends BackupAgentHelper {
 
+    private static final String TAG = "LauncherBackupAgentHelper";
+
     private static BackupManager sBackupManager;
 
     /**
@@ -38,9 +42,21 @@
         sBackupManager.dataChanged();
     }
 
+    @Override
+    public void onDestroy() {
+        // There is only one process accessing this preference file, but the restore
+        // modifies the file outside the normal codepaths, so it looks like another
+        // process.  This forces a reload of the file, in case this process persists.
+        String spKey = LauncherAppState.getSharedPreferencesKey();
+        SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS);
+        super.onDestroy();
+    }
 
     @Override
     public void onCreate() {
+        addHelper(LauncherBackupHelper.LAUNCHER_PREFS_PREFIX,
+                new SharedPreferencesBackupHelper(this,
+                        LauncherAppState.getSharedPreferencesKey()));
         addHelper(LauncherBackupHelper.LAUNCHER_PREFIX, new LauncherBackupHelper(this));
     }
 }
diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java
index 8023fcd..2a5ed69 100644
--- a/src/com/android/launcher3/LauncherBackupHelper.java
+++ b/src/com/android/launcher3/LauncherBackupHelper.java
@@ -82,6 +82,8 @@
 
     public static final String LAUNCHER_PREFIX = "L";
 
+    public static final String LAUNCHER_PREFS_PREFIX = "LP";
+
     private static final Bitmap.CompressFormat IMAGE_FORMAT =
             android.graphics.Bitmap.CompressFormat.PNG;
 
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 27b7dae..28efd01 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -270,7 +270,8 @@
 
             // Use default workspace resource if none provided
             if (workspaceResId == 0) {
-                workspaceResId = sp.getInt(DEFAULT_WORKSPACE_RESOURCE_ID, R.xml.default_workspace);
+                workspaceResId =
+                        sp.getInt(DEFAULT_WORKSPACE_RESOURCE_ID, getDefaultWorkspaceResourceId());
             }
 
             // Populate favorites table with initial favorites
@@ -286,6 +287,14 @@
         }
     }
 
+    private static int getDefaultWorkspaceResourceId() {
+        if (AppsCustomizePagedView.DISABLE_ALL_APPS) {
+            return R.xml.default_workspace_no_all_apps;
+        } else {
+            return R.xml.default_workspace;
+        }
+    }
+
     private static interface ContentValuesCallback {
         public void onRow(ContentValues values);
     }
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index dafabb8..fc087a8 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -71,7 +71,14 @@
     protected Intent getIntent() {
         return intent;
     }
-    
+
+    ShortcutInfo(Intent intent, CharSequence title, Bitmap icon) {
+        this();
+        this.intent = intent;
+        this.title = title;
+        mIcon = icon;
+    }
+
     public ShortcutInfo(Context context, ShortcutInfo info) {
         super(info);
         title = info.title.toString();
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 21c546d..9ffd044 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -43,7 +43,7 @@
 /**
  * Various utilities shared amongst the Launcher's classes.
  */
-final class Utilities {
+public final class Utilities {
     private static final String TAG = "Launcher.Utilities";
 
     private static int sIconWidth = -1;
@@ -111,7 +111,7 @@
     /**
      * Returns a bitmap suitable for the all apps view.
      */
-    static Bitmap createIconBitmap(Drawable icon, Context context) {
+    public static Bitmap createIconBitmap(Drawable icon, Context context) {
         synchronized (sCanvas) { // we share the statics :-(
             if (sIconWidth == -1) {
                 initStatics(context);
diff --git a/src/com/android/launcher3/WallpaperChangedReceiver.java b/src/com/android/launcher3/WallpaperChangedReceiver.java
new file mode 100644
index 0000000..28e41d8
--- /dev/null
+++ b/src/com/android/launcher3/WallpaperChangedReceiver.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class WallpaperChangedReceiver extends BroadcastReceiver {
+    public void onReceive(Context context, Intent data) {
+        LauncherAppState appState = LauncherAppState.getInstance();
+        appState.onWallpaperChanged();
+    }
+}
diff --git a/src/com/android/launcher3/WallpaperCropActivity.java b/src/com/android/launcher3/WallpaperCropActivity.java
index 2102599..b3ef073 100644
--- a/src/com/android/launcher3/WallpaperCropActivity.java
+++ b/src/com/android/launcher3/WallpaperCropActivity.java
@@ -71,6 +71,8 @@
     public static final int MAX_BMAP_IN_INTENT = 750000;
     private static final float WALLPAPER_SCREENS_SPAN = 2f;
 
+    protected static Point sDefaultWallpaperSize;
+
     protected CropView mCropView;
     protected Uri mUri;
 
@@ -204,32 +206,34 @@
     }
 
     static protected Point getDefaultWallpaperSize(Resources res, WindowManager windowManager) {
-        Point minDims = new Point();
-        Point maxDims = new Point();
-        windowManager.getDefaultDisplay().getCurrentSizeRange(minDims, maxDims);
+        if (sDefaultWallpaperSize == null) {
+            Point minDims = new Point();
+            Point maxDims = new Point();
+            windowManager.getDefaultDisplay().getCurrentSizeRange(minDims, maxDims);
 
-        int maxDim = Math.max(maxDims.x, maxDims.y);
-        int minDim = Math.max(minDims.x, minDims.y);
+            int maxDim = Math.max(maxDims.x, maxDims.y);
+            int minDim = Math.max(minDims.x, minDims.y);
 
-        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
-            Point realSize = new Point();
-            windowManager.getDefaultDisplay().getRealSize(realSize);
-            maxDim = Math.max(realSize.x, realSize.y);
-            minDim = Math.min(realSize.x, realSize.y);
+            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
+                Point realSize = new Point();
+                windowManager.getDefaultDisplay().getRealSize(realSize);
+                maxDim = Math.max(realSize.x, realSize.y);
+                minDim = Math.min(realSize.x, realSize.y);
+            }
+
+            // We need to ensure that there is enough extra space in the wallpaper
+            // for the intended parallax effects
+            final int defaultWidth, defaultHeight;
+            if (isScreenLarge(res)) {
+                defaultWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim));
+                defaultHeight = maxDim;
+            } else {
+                defaultWidth = Math.max((int) (minDim * WALLPAPER_SCREENS_SPAN), maxDim);
+                defaultHeight = maxDim;
+            }
+            sDefaultWallpaperSize = new Point(defaultWidth, defaultHeight);
         }
-
-        // We need to ensure that there is enough extra space in the wallpaper
-        // for the intended
-        // parallax effects
-        final int defaultWidth, defaultHeight;
-        if (isScreenLarge(res)) {
-            defaultWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim));
-            defaultHeight = maxDim;
-        } else {
-            defaultWidth = Math.max((int) (minDim * WALLPAPER_SCREENS_SPAN), maxDim);
-            defaultHeight = maxDim;
-        }
-        return new Point(defaultWidth, defaultHeight);
+        return sDefaultWallpaperSize;
     }
 
     public static int getRotationFromExif(String path) {
@@ -785,16 +789,13 @@
             WindowManager windowManager,
             final WallpaperManager wallpaperManager) {
         final Point defaultWallpaperSize = getDefaultWallpaperSize(res, windowManager);
-
-        new AsyncTask<Void, Void, Void>() {
-            public Void doInBackground(Void ... args) {
-                // If we have saved a wallpaper width/height, use that instead
-                int savedWidth = sharedPrefs.getInt(WALLPAPER_WIDTH_KEY, defaultWallpaperSize.x);
-                int savedHeight = sharedPrefs.getInt(WALLPAPER_HEIGHT_KEY, defaultWallpaperSize.y);
-                wallpaperManager.suggestDesiredDimensions(savedWidth, savedHeight);
-                return null;
-            }
-        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
+        // If we have saved a wallpaper width/height, use that instead
+        int savedWidth = sharedPrefs.getInt(WALLPAPER_WIDTH_KEY, defaultWallpaperSize.x);
+        int savedHeight = sharedPrefs.getInt(WALLPAPER_HEIGHT_KEY, defaultWallpaperSize.y);
+        if (savedWidth != wallpaperManager.getDesiredMinimumWidth() ||
+                savedHeight != wallpaperManager.getDesiredMinimumHeight()) {
+            wallpaperManager.suggestDesiredDimensions(savedWidth, savedHeight);
+        }
     }
 
     protected static RectF getMaxCropRect(
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index c6c6e13..bf4f561 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -43,6 +43,7 @@
 import android.graphics.Region.Op;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
+import android.os.AsyncTask;
 import android.os.IBinder;
 import android.os.Parcelable;
 import android.support.v4.view.ViewCompat;
@@ -437,6 +438,9 @@
 
         mMaxDistanceForFolderCreation = (0.55f * grid.iconSizePx);
         mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity);
+
+        // Set the wallpaper dimensions when Launcher starts up
+        setWallpaperDimension();
     }
 
     private void setupLayoutTransition() {
@@ -1232,10 +1236,16 @@
     }
 
     protected void setWallpaperDimension() {
-        String spKey = WallpaperCropActivity.getSharedPreferencesKey();
-        SharedPreferences sp = mLauncher.getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS);
-        WallpaperPickerActivity.suggestWallpaperDimension(mLauncher.getResources(),
-                sp, mLauncher.getWindowManager(), mWallpaperManager);
+        new AsyncTask<Void, Void, Void>() {
+            public Void doInBackground(Void ... args) {
+                String spKey = WallpaperCropActivity.getSharedPreferencesKey();
+                SharedPreferences sp =
+                        mLauncher.getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS);
+                WallpaperPickerActivity.suggestWallpaperDimension(mLauncher.getResources(),
+                        sp, mLauncher.getWindowManager(), mWallpaperManager);
+                return null;
+            }
+        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
     }
 
     protected void snapToPage(int whichPage, Runnable r) {
@@ -1693,6 +1703,12 @@
         AccessibilityManager am = (AccessibilityManager)
                 getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
         sAccessibilityEnabled = am.isEnabled();
+
+        // Update wallpaper dimensions if they were changed since last onResume
+        // (we also always set the wallpaper dimensions in the constructor)
+        if (LauncherAppState.getInstance().hasWallpaperChangedSinceLastCheck()) {
+            setWallpaperDimension();
+        }
     }
 
     @Override
@@ -4001,7 +4017,6 @@
         // hardware layers on children are enabled on startup, but should be disabled until
         // needed
         updateChildrenLayersEnabled(false);
-        setWallpaperDimension();
     }
 
     /**
@@ -4223,6 +4238,16 @@
     }
 
     @Override
+    public boolean supportsAppInfoDropTarget() {
+        return false;
+    }
+
+    @Override
+    public boolean supportsDeleteDropTarget() {
+        return true;
+    }
+
+    @Override
     public void onFlingToDelete(DragObject d, int x, int y, PointF vec) {
         // Do nothing
     }