diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 33f5a9591b..d6e8710120 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -2305,6 +2305,13 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, 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 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, 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 4e29a95fb2..9921f76f0c 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHost.java +++ b/src/com/android/launcher3/LauncherAppWidgetHost.java @@ -32,6 +32,7 @@ import android.widget.Toast; 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 @@ public class LauncherAppWidgetHost extends AppWidgetHost { private final ArrayList mProviderChangeListeners = new ArrayList<>(); private final SparseArray mViews = new SparseArray<>(); + private final SparseArray 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 @@ public class LauncherAppWidgetHost extends AppWidgetHost { @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 @@ public class LauncherAppWidgetHost extends AppWidgetHost { } } + 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 @@ public class LauncherAppWidgetHost extends AppWidgetHost { /** * 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 6038873c42..895f8de5a9 100644 --- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java +++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java @@ -33,6 +33,7 @@ import android.util.TypedValue; 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; @@ -93,6 +94,15 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView } } + @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) { diff --git a/src/com/android/launcher3/widget/WidgetManagerHelper.java b/src/com/android/launcher3/widget/WidgetManagerHelper.java index 8b08d7757c..f3c7822d0b 100644 --- a/src/com/android/launcher3/widget/WidgetManagerHelper.java +++ b/src/com/android/launcher3/widget/WidgetManagerHelper.java @@ -49,6 +49,9 @@ import java.util.stream.Stream; */ 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 @@ public class WidgetManagerHelper { 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 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 21a654f43b..793af48e29 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 com.android.launcher3.widget.WidgetHostViewLoader.getDefaultOption 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.PackageInstaller.SessionParams; 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.LauncherAppWidgetHost; 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 @@ public class BindWidgetTest extends AbstractLauncherUiTest { // 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 @@ public class BindWidgetTest extends AbstractLauncherUiTest { 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 @@ public class BindWidgetTest extends AbstractLauncherUiTest { /** * 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 @@ public class BindWidgetTest extends AbstractLauncherUiTest { .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 @@ public class BindWidgetTest extends AbstractLauncherUiTest { } } pkg = invalidPackage + count; - count ++; + count++; } LauncherAppWidgetInfo item = new LauncherAppWidgetInfo(10, new ComponentName(pkg, "com.test.widgetprovider"));