Merge "Fixing memory leak in WallpaperCropActivity" into ub-now-master
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 67d86d2..49efc5d 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -188,6 +188,13 @@
             </intent-filter>
         </receiver>
 
+        <!-- Intent received used to initialize a restored widget -->
+        <receiver android:name="com.android.launcher3.AppWidgetsRestoredReceiver" >
+            <intent-filter>
+                <action android:name="android.appwidget.action.APPWIDGET_HOST_RESTORED"/>
+            </intent-filter>
+        </receiver>
+
         <!-- New user initialization; set up initial wallpaper -->
         <receiver
             android:name="com.android.launcher3.UserInitializeReceiver"
diff --git a/res/drawable-hdpi/ic_allapps.png b/res/drawable-hdpi/ic_allapps.png
index 4fe3bf0..072cf5b 100644
--- a/res/drawable-hdpi/ic_allapps.png
+++ b/res/drawable-hdpi/ic_allapps.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_pageindicator_add.png b/res/drawable-hdpi/ic_pageindicator_add.png
index c37d622..4bb8bfa 100644
--- a/res/drawable-hdpi/ic_pageindicator_add.png
+++ b/res/drawable-hdpi/ic_pageindicator_add.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_pageindicator_current.png b/res/drawable-hdpi/ic_pageindicator_current.png
index aac8d40..bd15fb4 100644
--- a/res/drawable-hdpi/ic_pageindicator_current.png
+++ b/res/drawable-hdpi/ic_pageindicator_current.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_pageindicator_default.png b/res/drawable-hdpi/ic_pageindicator_default.png
index bafd94b..2386cd8 100644
--- a/res/drawable-hdpi/ic_pageindicator_default.png
+++ b/res/drawable-hdpi/ic_pageindicator_default.png
Binary files differ
diff --git a/res/drawable-hdpi/quantum_panel.9.png b/res/drawable-hdpi/quantum_panel.9.png
index ae625be..d86d7bc 100644
--- a/res/drawable-hdpi/quantum_panel.9.png
+++ b/res/drawable-hdpi/quantum_panel.9.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_allapps.png b/res/drawable-mdpi/ic_allapps.png
index 09cd82a..f3887be 100644
--- a/res/drawable-mdpi/ic_allapps.png
+++ b/res/drawable-mdpi/ic_allapps.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_add.png b/res/drawable-mdpi/ic_pageindicator_add.png
index 8e05e64..e20ffa7 100644
--- a/res/drawable-mdpi/ic_pageindicator_add.png
+++ b/res/drawable-mdpi/ic_pageindicator_add.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_current.png b/res/drawable-mdpi/ic_pageindicator_current.png
index ab5f4c8..bb49d31 100644
--- a/res/drawable-mdpi/ic_pageindicator_current.png
+++ b/res/drawable-mdpi/ic_pageindicator_current.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_default.png b/res/drawable-mdpi/ic_pageindicator_default.png
index c919ee8..50d0989 100644
--- a/res/drawable-mdpi/ic_pageindicator_default.png
+++ b/res/drawable-mdpi/ic_pageindicator_default.png
Binary files differ
diff --git a/res/drawable-mdpi/quantum_panel.9.png b/res/drawable-mdpi/quantum_panel.9.png
index 1f9cd1a..061c80a 100644
--- a/res/drawable-mdpi/quantum_panel.9.png
+++ b/res/drawable-mdpi/quantum_panel.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_allapps.png b/res/drawable-xhdpi/ic_allapps.png
index eff3bea..6a528d5 100644
--- a/res/drawable-xhdpi/ic_allapps.png
+++ b/res/drawable-xhdpi/ic_allapps.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_add.png b/res/drawable-xhdpi/ic_pageindicator_add.png
index 28e164b..9659e6f 100644
--- a/res/drawable-xhdpi/ic_pageindicator_add.png
+++ b/res/drawable-xhdpi/ic_pageindicator_add.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_current.png b/res/drawable-xhdpi/ic_pageindicator_current.png
index aed3d71..07b3137 100644
--- a/res/drawable-xhdpi/ic_pageindicator_current.png
+++ b/res/drawable-xhdpi/ic_pageindicator_current.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_default.png b/res/drawable-xhdpi/ic_pageindicator_default.png
index 0887416..0351d57 100644
--- a/res/drawable-xhdpi/ic_pageindicator_default.png
+++ b/res/drawable-xhdpi/ic_pageindicator_default.png
Binary files differ
diff --git a/res/drawable-xhdpi/quantum_panel.9.png b/res/drawable-xhdpi/quantum_panel.9.png
index 562d758..8605cfd 100644
--- a/res/drawable-xhdpi/quantum_panel.9.png
+++ b/res/drawable-xhdpi/quantum_panel.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_allapps.png b/res/drawable-xxhdpi/ic_allapps.png
index 2461984..ae5545d 100644
--- a/res/drawable-xxhdpi/ic_allapps.png
+++ b/res/drawable-xxhdpi/ic_allapps.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_allapps_pressed.png b/res/drawable-xxhdpi/ic_allapps_pressed.png
index 929a0e6..77b45ae 100644
--- a/res/drawable-xxhdpi/ic_allapps_pressed.png
+++ b/res/drawable-xxhdpi/ic_allapps_pressed.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_add.png b/res/drawable-xxhdpi/ic_pageindicator_add.png
index d78c95a..591b189 100644
--- a/res/drawable-xxhdpi/ic_pageindicator_add.png
+++ b/res/drawable-xxhdpi/ic_pageindicator_add.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_current.png b/res/drawable-xxhdpi/ic_pageindicator_current.png
index 2a5eff6..4e4660f 100644
--- a/res/drawable-xxhdpi/ic_pageindicator_current.png
+++ b/res/drawable-xxhdpi/ic_pageindicator_current.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_default.png b/res/drawable-xxhdpi/ic_pageindicator_default.png
index 969ae8c..1cccb17 100644
--- a/res/drawable-xxhdpi/ic_pageindicator_default.png
+++ b/res/drawable-xxhdpi/ic_pageindicator_default.png
Binary files differ
diff --git a/res/drawable-xxhdpi/quantum_panel.9.png b/res/drawable-xxhdpi/quantum_panel.9.png
index bc818b1..9a1a79c 100644
--- a/res/drawable-xxhdpi/quantum_panel.9.png
+++ b/res/drawable-xxhdpi/quantum_panel.9.png
Binary files differ
diff --git a/res/drawable/bg_appwidget_not_ready.xml b/res/drawable/bg_appwidget_not_ready.xml
new file mode 100644
index 0000000..a8b56c2
--- /dev/null
+++ b/res/drawable/bg_appwidget_not_ready.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle" >
+
+    <corners android:radius="5dp" />
+
+    <solid android:color="@android:color/white" />
+
+</shape>
\ No newline at end of file
diff --git a/res/layout/appwidget_not_ready.xml b/res/layout/appwidget_not_ready.xml
new file mode 100644
index 0000000..f5f2aab
--- /dev/null
+++ b/res/layout/appwidget_not_ready.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingLeft="20dip"
+    android:paddingRight="20dip"
+    android:layout_margin="10dip"
+    android:gravity="center"
+    android:background="@drawable/bg_appwidget_not_ready"
+    android:textAppearance="?android:attr/textAppearanceMediumInverse"
+    android:textColor="@color/appwidget_not_ready_color"
+    android:text="@string/gadget_pending_text"
+    />
diff --git a/res/values/colors.xml b/res/values/colors.xml
index d1d33a0..27a5b61 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -26,6 +26,7 @@
     <color name="bubble_dark_background">#20000000</color>
 
     <color name="appwidget_error_color">#FCCC</color>
+    <color name="appwidget_not_ready_color">#F48F</color>
 
     <color name="workspace_all_apps_and_delete_zone_text_color">#CCFFFFFF</color>
     <color name="workspace_all_apps_and_delete_zone_text_shadow_color">#A0000000</color>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 7c5ee9c..1ee1be6 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -190,6 +190,9 @@
     <!-- Text to show user in place of a gadget when we can't display it properly -->
     <string name="gadget_error_text">Problem loading widget</string>
 
+    <!-- Text to show user in place of a gadget when it is not yet ready/initialized. -->
+    <string name="gadget_pending_text" translatable="false">Widget not ready</string>
+
     <!-- Text to inform the user that they can't uninstall a system application -->
     <string name="uninstall_system_app_text">This is a system app and can\'t be uninstalled.</string>
 
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
new file mode 100644
index 0000000..9fef7f9
--- /dev/null
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -0,0 +1,90 @@
+package com.android.launcher3;
+
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.os.AsyncTask;
+import android.util.Log;
+
+import com.android.launcher3.LauncherSettings.Favorites;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
+
+    private static final String TAG = "AppWidgetsRestoredReceiver";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED.equals(intent.getAction())) {
+            int[] oldIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS);
+            int[] newIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
+            if (oldIds.length == newIds.length) {
+                restoreAppWidgetIds(context, oldIds, newIds);
+            } else {
+                Log.e(TAG, "Invalid host restored received");
+            }
+        }
+    }
+
+    /**
+     * Updates the app widgets whose id has changed during the restore process.
+     */
+    static void restoreAppWidgetIds(Context context, int[] oldWidgetIds, int[] newWidgetIds) {
+        final ContentResolver cr = context.getContentResolver();
+        final List<Integer> idsToRemove = new ArrayList<>();
+        final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
+
+        for (int i = 0; i < oldWidgetIds.length; i++) {
+            Log.i(TAG, "Widget state restore id " + oldWidgetIds[i] + " => " + newWidgetIds[i]);
+
+            final AppWidgetProviderInfo provider = widgets.getAppWidgetInfo(newWidgetIds[i]);
+
+            ContentValues values = new ContentValues();
+            values.put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i]);
+            values.put(LauncherSettings.Favorites.RESTORED, LauncherModel.isValidProvider(provider)
+                    ? LauncherAppWidgetInfo.RESTORE_COMPLETED
+                            : LauncherAppWidgetInfo.RESTORE_PROVIDER_PENDING);
+
+            String[] widgetIdParams = new String[] { Integer.toString(oldWidgetIds[i]) };
+
+            int result = cr.update(Favorites.CONTENT_URI, values,
+                    "appWidgetId=? and restored=1", widgetIdParams);
+            if (result == 0) {
+                Cursor cursor = cr.query(Favorites.CONTENT_URI,
+                        new String[] {Favorites.APPWIDGET_ID},
+                        "appWidgetId=?", widgetIdParams, null);
+                try {
+                    if (!cursor.moveToFirst()) {
+                        // The widget no long exists.
+                        idsToRemove.add(newWidgetIds[i]);
+                    }
+                } finally {
+                    cursor.close();
+                }
+            }
+        }
+        // Unregister the widget IDs which are not present on the workspace. This could happen
+        // when a widget place holder is removed from workspace, before this method is called.
+        if (!idsToRemove.isEmpty()) {
+            final AppWidgetHost appWidgetHost =
+                    new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID);
+            new AsyncTask<Void, Void, Void>() {
+                public Void doInBackground(Void ... args) {
+                    for (Integer id : idsToRemove) {
+                        appWidgetHost.deleteAppWidgetId(id);
+                        Log.e(TAG, "Widget no longer present, appWidgetId=" + id);
+                    }
+                    return null;
+                }
+            }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 3d45432..4c3388e 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -326,7 +326,7 @@
 
             final LauncherAppWidgetInfo launcherAppWidgetInfo = (LauncherAppWidgetInfo) item;
             final LauncherAppWidgetHost appWidgetHost = mLauncher.getAppWidgetHost();
-            if (appWidgetHost != null) {
+            if ((appWidgetHost != null) && launcherAppWidgetInfo.isWidgetIdValid()) {
                 // Deleting an app widget ID is a void call but writes to disk before returning
                 // to the caller...
                 new AsyncTask<Void, Void, Void>() {
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 9934f66..79cac8f 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -167,7 +167,7 @@
             int y = getCellYFromOrder(mAllAppsButtonRank);
             CellLayout.LayoutParams lp = new CellLayout.LayoutParams(x,y,1,1);
             lp.canReorder = false;
-            mContent.addViewToCellLayout(allAppsButton, -1, 0, lp, true);
+            mContent.addViewToCellLayout(allAppsButton, -1, allAppsButton.getId(), lp, true);
         }
     }
 
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index d3a4362..5e05f92 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -89,6 +89,7 @@
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.view.Window;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AccelerateDecelerateInterpolator;
@@ -102,6 +103,8 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
+import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.PagedView.PageSwitchListener;
 import com.android.launcher3.compat.LauncherActivityInfoCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserHandleCompat;
@@ -4387,13 +4390,20 @@
         }
         final Workspace workspace = mWorkspace;
 
-        final int appWidgetId = item.appWidgetId;
-        final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
-        if (DEBUG_WIDGETS) {
-            Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + appWidgetInfo.provider);
-        }
+        final AppWidgetProviderInfo appWidgetInfo;
+        if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
+            final int appWidgetId = item.appWidgetId;
+            appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
+            if (DEBUG_WIDGETS) {
+                Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + appWidgetInfo.provider);
+            }
 
-        item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
+            item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
+        } else {
+            appWidgetInfo = null;
+            item.hostView = new LauncherAppWidgetHostView(this, false);
+            item.hostView.updateAppWidget(null);
+        }
 
         item.hostView.setTag(item);
         item.onBindAppWidget(this);
@@ -4553,7 +4563,7 @@
         }
 
         if (mWorkspace != null) {
-            mWorkspace.updateShortcuts(apps);
+            mWorkspace.updateShortcutsAndWidgets(apps);
         }
 
         if (!LauncherAppState.isDisableAllApps() &&
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index f47fd13..7eb0052 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -18,6 +18,7 @@
 
 import android.appwidget.AppWidgetHostView;
 import android.content.Context;
+import android.os.Bundle;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
@@ -39,12 +40,28 @@
 
     private float mSlop;
 
+    private boolean mWidgetReady;
+
     public LauncherAppWidgetHostView(Context context) {
+        this(context, true);
+    }
+
+    public LauncherAppWidgetHostView(Context context, boolean widgetReady) {
         super(context);
         mContext = context;
         mLongPressHelper = new CheckLongPressHelper(this);
         mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         mDragLayer = ((Launcher) context).getDragLayer();
+        mWidgetReady = widgetReady;
+    }
+
+    @Override
+    public void updateAppWidgetSize(Bundle newOptions, int minWidth, int minHeight, int maxWidth,
+            int maxHeight) {
+        // If the widget is not yet ready, the app widget size cannot be updated.
+        if (mWidgetReady) {
+            super.updateAppWidgetSize(newOptions, minWidth, minHeight, maxWidth, maxHeight);
+        }
     }
 
     @Override
@@ -53,6 +70,15 @@
     }
 
     @Override
+    protected View getDefaultView() {
+        if (mWidgetReady) {
+            return super.getDefaultView();
+        } else {
+            return mInflater.inflate(R.layout.appwidget_not_ready, this, false);
+        }
+    }
+
+    @Override
     public void updateAppWidget(RemoteViews remoteViews) {
         // Store the orientation in which the widget was inflated
         mPreviousOrientation = mContext.getResources().getConfiguration().orientation;
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index bec1f9e..b3ac12b 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -28,6 +28,18 @@
  */
 public class LauncherAppWidgetInfo extends ItemInfo {
 
+    public static final int RESTORE_COMPLETED = 0;
+
+    /**
+     * This is set during the package backup creation.
+     */
+    public static final int RESTORE_REMAP_PENDING = 1;
+
+    /**
+     * Widget provider is not yet installed.
+     */
+    public static final int RESTORE_PROVIDER_PENDING = 2;
+
     /**
      * Indicates that the widget hasn't been instantiated yet.
      */
@@ -45,6 +57,11 @@
     int minWidth = -1;
     int minHeight = -1;
 
+    /**
+     * Indicates the restore status of the widget.
+     */
+    int restoreStatus;
+
     private boolean mHasNotifiedInitialWidgetSizeChanged;
 
     /**
@@ -64,6 +81,7 @@
         spanY = -1;
         // We only support app widgets on current user.
         user = UserHandleCompat.myUserHandle();
+        restoreStatus = RESTORE_COMPLETED;
     }
 
     @Override
@@ -101,4 +119,8 @@
         super.unbind();
         hostView = null;
     }
+
+    public final boolean isWidgetIdValid() {
+        return restoreStatus != RESTORE_REMAP_PENDING;
+    }
 }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index a8bace8..db5e7cf 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -2125,31 +2125,54 @@
                                 // Read all Launcher-specific widget details
                                 int appWidgetId = c.getInt(appWidgetIdIndex);
                                 String savedProvider = c.getString(appWidgetProviderIndex);
-
                                 id = c.getLong(idIndex);
 
-                                final AppWidgetProviderInfo provider =
-                                        widgets.getAppWidgetInfo(appWidgetId);
+                                final int restoreStatus = c.getInt(restoredIndex);
+                                final boolean restorePending = Utilities.isLmp()
+                                        && (restoreStatus ==
+                                            LauncherAppWidgetInfo.RESTORE_REMAP_PENDING);
+                                final boolean providerPending = Utilities.isLmp()
+                                        && (restoreStatus ==
+                                            LauncherAppWidgetInfo.RESTORE_PROVIDER_PENDING);
 
-                                if (!isSafeMode && (provider == null || provider.provider == null ||
-                                        provider.provider.getPackageName() == null)) {
-                                    String log = "Deleting widget that isn't installed anymore: id="
-                                        + id + " appWidgetId=" + appWidgetId;
+                                // Do not try to get the provider if restore is pending, as the
+                                // widget id is invalid, and it might point to some other provider.
+                                final AppWidgetProviderInfo provider = restorePending ? null
+                                        : widgets.getAppWidgetInfo(appWidgetId);
+                                boolean providerValid = isValidProvider(provider);
+
+                                // Skip provider check,
+                                //    1. when the widget id re-map is pending
+                                //    2. provider is pending install for a restored widget
+                                if (!isSafeMode && !providerPending && !restorePending
+                                        && !providerValid) {
+                                    String log = "Deleting widget that isn't installed anymore: "
+                                        + "id=" + id + " appWidgetId=" + appWidgetId;
                                     Log.e(TAG, log);
                                     Launcher.addDumpLog(TAG, log, false);
                                     itemsToRemove.add(id);
                                 } else {
-                                    appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
-                                            provider.provider);
+                                    if (providerValid) {
+                                        appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
+                                                provider.provider);
+                                        int[] minSpan =
+                                                Launcher.getMinSpanForWidget(context, provider);
+                                        appWidgetInfo.minSpanX = minSpan[0];
+                                        appWidgetInfo.minSpanY = minSpan[1];
+                                    } else {
+                                        Log.v(TAG, "Widget restore pending id=" + id
+                                                + " appWidgetId=" + appWidgetId
+                                                + " status =" + restoreStatus);
+                                        appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
+                                                ComponentName.unflattenFromString(savedProvider));
+                                        appWidgetInfo.restoreStatus = restoreStatus;
+                                    }
                                     appWidgetInfo.id = id;
                                     appWidgetInfo.screenId = c.getInt(screenIndex);
                                     appWidgetInfo.cellX = c.getInt(cellXIndex);
                                     appWidgetInfo.cellY = c.getInt(cellYIndex);
                                     appWidgetInfo.spanX = c.getInt(spanXIndex);
                                     appWidgetInfo.spanY = c.getInt(spanYIndex);
-                                    int[] minSpan = Launcher.getMinSpanForWidget(context, provider);
-                                    appWidgetInfo.minSpanX = minSpan[0];
-                                    appWidgetInfo.minSpanY = minSpan[1];
 
                                     container = c.getInt(containerIndex);
                                     if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
@@ -2169,14 +2192,20 @@
                                         }
                                         break;
                                     }
-                                    String providerName = provider.provider.flattenToString();
-                                    if (!providerName.equals(savedProvider)) {
-                                        ContentValues values = new ContentValues();
-                                        values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
-                                                providerName);
-                                        String where = BaseColumns._ID + "= ?";
-                                        String[] args = {Integer.toString(c.getInt(idIndex))};
-                                        contentResolver.update(contentUri, values, where, args);
+
+                                    if (providerValid) {
+                                        String providerName = provider.provider.flattenToString();
+
+                                        if (!providerName.equals(savedProvider) || providerPending) {
+                                            ContentValues values = new ContentValues();
+                                            values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
+                                                    providerName);
+                                            values.put(LauncherSettings.Favorites.RESTORED,
+                                                    LauncherAppWidgetInfo.RESTORE_COMPLETED);
+                                            String where = BaseColumns._ID + "= ?";
+                                            String[] args = {Long.toString(id)};
+                                            contentResolver.update(contentUri, values, where, args);
+                                        }
                                     }
                                     sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo);
                                     sBgAppWidgets.add(appWidgetInfo);
@@ -3605,6 +3634,11 @@
         }
     };
 
+    static boolean isValidProvider(AppWidgetProviderInfo provider) {
+        return (provider != null) && (provider.provider != null)
+                && (provider.provider.getPackageName() != null);
+    }
+
     public void dumpState() {
         Log.d(TAG, "mCallbacks=" + mCallbacks);
         AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mBgAllAppsList.data);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 267dde8..1e26458 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -30,6 +30,8 @@
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.ContentValues;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.res.Resources;
@@ -47,6 +49,7 @@
 import android.os.AsyncTask;
 import android.os.IBinder;
 import android.os.Parcelable;
+import android.provider.BaseColumns;
 import android.support.v4.view.ViewCompat;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -61,15 +64,16 @@
 import android.view.animation.Interpolator;
 import android.widget.TextView;
 
-import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.FolderIcon.FolderRingAnimator;
 import com.android.launcher3.Launcher.CustomContentCallbacks;
 import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.compat.UserHandleCompat;
 
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.Set;
 
 /**
  * The workspace is a wide area with a wallpaper and a finite number of pages.
@@ -4574,7 +4578,7 @@
 
     public Folder getFolderForTag(final Object tag) {
         final Folder[] value = new Folder[1];
-        mapOverShortcuts(MAP_NO_RECURSE, new ShortcutOperator() {
+        mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
             @Override
             public boolean evaluate(ItemInfo info, View v, View parent) {
                 if (v instanceof Folder) {
@@ -4592,7 +4596,7 @@
 
     public View getViewForTag(final Object tag) {
         final View[] value = new View[1];
-        mapOverShortcuts(MAP_NO_RECURSE, new ShortcutOperator() {
+        mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
             @Override
             public boolean evaluate(ItemInfo info, View v, View parent) {
                 if (v.getTag() == tag) {
@@ -4606,7 +4610,7 @@
     }
 
     void clearDropTargets() {
-        mapOverShortcuts(MAP_NO_RECURSE, new ShortcutOperator() {
+        mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
             @Override
             public boolean evaluate(ItemInfo info, View v, View parent) {
                 if (v instanceof DropTarget) {
@@ -4760,9 +4764,9 @@
         }
     }
 
-    interface ShortcutOperator {
+    interface ItemOperator {
         /**
-         * Process the next shortcut, possibly with side-effect on {@link ShortcutOperator#value}.
+         * Process the next itemInfo, possibly with side-effect on {@link ItemOperator#value}.
          *
          * @param info info for the shortcut
          * @param view view for the shortcut
@@ -4773,12 +4777,12 @@
     }
 
     /**
-     * Map the operator over the shortcuts, return the first-non-null value.
+     * Map the operator over the shortcuts and widgets, return the first-non-null value.
      *
      * @param recurse true: iterate over folder children. false: op get the folders themselves.
      * @param op the operator to map over the shortcuts
      */
-    void mapOverShortcuts(boolean recurse, ShortcutOperator op) {
+    void mapOverItems(boolean recurse, ItemOperator op) {
         ArrayList<ShortcutAndWidgetContainer> containers = getAllShortcutAndWidgetContainers();
         final int containerCount = containers.size();
         for (int containerIdx = 0; containerIdx < containerCount; containerIdx++) {
@@ -4809,14 +4813,16 @@
         }
     }
 
-    void updateShortcuts(ArrayList<AppInfo> apps) {
+    void updateShortcutsAndWidgets(ArrayList<AppInfo> apps) {
         // Create a map of the apps to test against
         final HashMap<ComponentName, AppInfo> appsMap = new HashMap<ComponentName, AppInfo>();
+        final HashSet<String> pkgNames = new HashSet<>();
         for (AppInfo ai : apps) {
             appsMap.put(ai.componentName, ai);
+            pkgNames.add(ai.componentName.getPackageName());
         }
 
-        mapOverShortcuts(MAP_RECURSE, new ShortcutOperator() {
+        mapOverItems(MAP_RECURSE, new ItemOperator() {
             @Override
             public boolean evaluate(ItemInfo info, View v, View parent) {
                 if (info instanceof ShortcutInfo) {
@@ -4829,6 +4835,8 @@
                 return false;
             }
         });
+
+        restorePendingWidgets(pkgNames);
     }
 
     public void removeAbandonedPromise(BubbleTextView abandonedIcon, UserHandleCompat user) {
@@ -4844,7 +4852,7 @@
     }
 
     public void updatePackageState(final String pkgName, final int state) {
-        mapOverShortcuts(MAP_RECURSE, new ShortcutOperator() {
+        mapOverItems(MAP_RECURSE, new ItemOperator() {
             @Override
             public boolean evaluate(ItemInfo info, View v, View parent) {
                 if (info instanceof ShortcutInfo
@@ -4857,6 +4865,40 @@
                 return false;
             }
         });
+
+        if (state == ShortcutInfo.PACKAGE_STATE_DEFAULT) {
+            // Update any pending widget
+            HashSet<String> packages = new HashSet<>();
+            packages.add(pkgName);
+            restorePendingWidgets(packages);
+        }
+    }
+
+    private void restorePendingWidgets(final Set<String> installedPackaged) {
+        final ContentResolver contentResolver = getContext().getContentResolver();
+        // Iterate non recursively as widgets can't be inside a folder.
+        mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
+
+            @Override
+            public boolean evaluate(ItemInfo info, View v, View parent) {
+                if (info instanceof LauncherAppWidgetInfo) {
+                    LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
+                    if (widgetInfo.restoreStatus == LauncherAppWidgetInfo.RESTORE_PROVIDER_PENDING
+                            && installedPackaged.contains(widgetInfo.providerName.getPackageName())) {
+                        // Package installed. Update pending widgets.
+                        ContentValues values = new ContentValues();
+                        values.put(LauncherSettings.Favorites.RESTORED,
+                                LauncherAppWidgetInfo.RESTORE_COMPLETED);
+                        String where = BaseColumns._ID + "= ?";
+                        String[] args = {Long.toString(widgetInfo.id)};
+                        contentResolver.update(LauncherSettings.Favorites.CONTENT_URI,
+                                values, where, args);
+                    }
+                }
+                // process all the widget
+                return false;
+            }
+        });
     }
 
     private void moveToScreen(int page, boolean animate) {