Allow Launcher to automatically restore widgets

This replaces PendingAppWidgetHostView with configured widget view when
OPTION_APPWIDGET_RESTORE_COMPLETED is set to True by a widget provider.

Bug: 63667276
Test: Manual
Change-Id: Ide21f5e9a7dac7e3d6a745660a38ad0b951b47d3
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 33f5a95..d6e8710 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2305,6 +2305,13 @@
                     item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
                     getModelWriter().updateItemInDatabase(item);
                 }
+                else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY)
+                        && appWidgetInfo.configure != null) {
+                    if (mAppWidgetManager.isAppWidgetRestored(item.appWidgetId)) {
+                        item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
+                        getModelWriter().updateItemInDatabase(item);
+                    }
+                }
             }
 
             if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
@@ -2318,6 +2325,11 @@
                 item.minSpanX = appWidgetInfo.minSpanX;
                 item.minSpanY = appWidgetInfo.minSpanY;
                 view = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo);
+            } else if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)
+                    && appWidgetInfo != null) {
+                mAppWidgetHost.addPendingView(item.appWidgetId,
+                        new PendingAppWidgetHostView(this, item, mIconCache, false));
+                view = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo);
             } else {
                 view = new PendingAppWidgetHostView(this, item, mIconCache, false);
             }
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index 4e29a95..9921f76 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -32,6 +32,7 @@
 import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.widget.DeferredAppWidgetHostView;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.PendingAppWidgetHostView;
 import com.android.launcher3.widget.custom.CustomWidgetManager;
 
 import java.util.ArrayList;
@@ -53,12 +54,14 @@
 
     private final ArrayList<ProviderChangedListener> mProviderChangeListeners = new ArrayList<>();
     private final SparseArray<LauncherAppWidgetHostView> mViews = new SparseArray<>();
+    private final SparseArray<PendingAppWidgetHostView> mPendingViews = new SparseArray<>();
 
     private final Context mContext;
     private int mFlags = FLAG_RESUMED;
 
     private IntConsumer mAppWidgetRemovedCallback = null;
 
+
     public LauncherAppWidgetHost(Context context) {
         this(context, null);
     }
@@ -73,7 +76,13 @@
     @Override
     protected LauncherAppWidgetHostView onCreateView(Context context, int appWidgetId,
             AppWidgetProviderInfo appWidget) {
-        LauncherAppWidgetHostView view = new LauncherAppWidgetHostView(context);
+        final LauncherAppWidgetHostView view;
+        if (mPendingViews.get(appWidgetId) != null) {
+            view = mPendingViews.get(appWidgetId);
+            mPendingViews.remove(appWidgetId);
+        } else {
+            view = new LauncherAppWidgetHostView(context);
+        }
         mViews.put(appWidgetId, view);
         return view;
     }
@@ -189,6 +198,10 @@
         }
     }
 
+    void addPendingView(int appWidgetId, PendingAppWidgetHostView view) {
+        mPendingViews.put(appWidgetId, view);
+    }
+
     public AppWidgetHostView createView(Context context, int appWidgetId,
             LauncherAppWidgetProviderInfo appWidget) {
         if (appWidget.isCustomWidget()) {
@@ -238,8 +251,8 @@
 
     /**
      * Called on an appWidget is removed for a widgetId
-     * @param appWidgetId
-     * TODO: make this override when SDK is updated
+     *
+     * @param appWidgetId TODO: make this override when SDK is updated
      */
     public void onAppWidgetRemoved(int appWidgetId) {
         if (mAppWidgetRemovedCallback == null) {
diff --git a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
index 6038873..895f8de 100644
--- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
@@ -33,6 +33,7 @@
 import android.view.ContextThemeWrapper;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.widget.RemoteViews;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.FastBitmapDrawable;
@@ -94,6 +95,15 @@
     }
 
     @Override
+    public void updateAppWidget(RemoteViews remoteViews) {
+        super.updateAppWidget(remoteViews);
+        WidgetManagerHelper widgetManagerHelper = new WidgetManagerHelper(getContext());
+        if (widgetManagerHelper.isAppWidgetRestored(mInfo.appWidgetId)) {
+            reInflate();
+        }
+    }
+
+    @Override
     public void updateAppWidgetSize(Bundle newOptions, int minWidth, int minHeight, int maxWidth,
             int maxHeight) {
         // No-op
diff --git a/src/com/android/launcher3/widget/WidgetManagerHelper.java b/src/com/android/launcher3/widget/WidgetManagerHelper.java
index 8b08d77..f3c7822 100644
--- a/src/com/android/launcher3/widget/WidgetManagerHelper.java
+++ b/src/com/android/launcher3/widget/WidgetManagerHelper.java
@@ -49,6 +49,9 @@
  */
 public class WidgetManagerHelper {
 
+    //TODO: replace this with OPTION_APPWIDGET_RESTORE_COMPLETED b/63667276
+    public static final String WIDGET_OPTION_RESTORE_COMPLETED = "appWidgetRestoreCompleted";
+
     final AppWidgetManager mAppWidgetManager;
     final Context mContext;
 
@@ -127,6 +130,14 @@
         return null;
     }
 
+    /**
+     * Returns if a AppWidgetProvider has marked a widget restored
+     */
+    public boolean isAppWidgetRestored(int appWidgetId) {
+        return !WidgetsModel.GO_DISABLE_WIDGETS && mAppWidgetManager.getAppWidgetOptions(
+                appWidgetId).getBoolean(WIDGET_OPTION_RESTORE_COMPLETED);
+    }
+
     public static Map<ComponentKey, AppWidgetProviderInfo> getAllProvidersMap(Context context) {
         if (WidgetsModel.GO_DISABLE_WIDGETS) {
             return Collections.emptyMap();
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index 21a654f..793af48 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -21,6 +21,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import android.appwidget.AppWidgetHost;
@@ -33,6 +34,7 @@
 import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.os.Bundle;
+import android.widget.RemoteViews;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -41,6 +43,7 @@
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.R;
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.tapl.Workspace;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
@@ -86,7 +89,8 @@
 
         // Clear all existing data
         LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
-        LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
+        LauncherSettings.Settings.call(mResolver,
+                LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
     }
 
     @After
@@ -172,6 +176,26 @@
         assertNotNull(AppWidgetManager.getInstance(mTargetContext)
                 .getAppWidgetInfo(mCursor.getInt(mCursor.getColumnIndex(
                         LauncherSettings.Favorites.APPWIDGET_ID))));
+
+        // send OPTION_APPWIDGET_RESTORE_COMPLETED
+        int appWidgetId = mCursor.getInt(
+                mCursor.getColumnIndex(LauncherSettings.Favorites.APPWIDGET_ID));
+        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mTargetContext);
+
+        Bundle b = new Bundle();
+        b.putBoolean(WidgetManagerHelper.WIDGET_OPTION_RESTORE_COMPLETED, true);
+        RemoteViews remoteViews = new RemoteViews(mTargetPackage, R.layout.appwidget_not_ready);
+        appWidgetManager.updateAppWidgetOptions(appWidgetId, b);
+        appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
+
+
+        // verify changes are reflected
+        waitForLauncherCondition("App widget options did not update",
+                l -> appWidgetManager.getAppWidgetOptions(appWidgetId).getBoolean(
+                        WidgetManagerHelper.WIDGET_OPTION_RESTORE_COMPLETED));
+        executeOnLauncher(l -> l.getAppWidgetHost().startListening());
+        verifyWidgetPresent(info);
+        assertNull(mLauncher.getWorkspace().tryGetPendingWidget(DEFAULT_UI_TIMEOUT));
     }
 
     @Test
@@ -254,6 +278,7 @@
 
     /**
      * Creates a LauncherAppWidgetInfo corresponding to {@param info}
+     *
      * @param bindWidget if true the info is bound and a valid widgetId is assigned to
      *                   the LauncherAppWidgetInfo
      */
@@ -306,7 +331,7 @@
                     .keySet().forEach(packageUserKey -> packages.add(packageUserKey.mPackageName));
             return packages;
         });
-        while(true) {
+        while (true) {
             try {
                 mTargetContext.getPackageManager().getPackageInfo(
                         pkg, PackageManager.GET_UNINSTALLED_PACKAGES);
@@ -316,7 +341,7 @@
                 }
             }
             pkg = invalidPackage + count;
-            count ++;
+            count++;
         }
         LauncherAppWidgetInfo item = new LauncherAppWidgetInfo(10,
                 new ComponentName(pkg, "com.test.widgetprovider"));