Deleting any ghost widget from system service:
> A one-time DB update for removing any existing ghost widgets
> Handling widget cleanup when we bulk delete workspace items during loader
> Simplifying external delete
Bug: 35634653
Change-Id: Id0c520f57aee6d75d9c0e7bcd5786a464bf9f39f
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index f8c591c..579352d 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -1249,6 +1249,10 @@
sBgDataModel.folders.remove(folderId);
sBgDataModel.itemsIdMap.remove(folderId);
}
+
+ // Remove any ghost widgets
+ LauncherSettings.Settings.call(contentResolver,
+ LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS);
}
// Unpin shortcuts that don't exist on the workspace.
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 6302744..7d85ac1 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -47,7 +47,6 @@
import android.os.UserManager;
import android.text.TextUtils;
import android.util.Log;
-import android.view.ViewGroup;
import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -67,10 +66,11 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Locale;
+import java.util.HashSet;
public class LauncherProvider extends ContentProvider {
private static final String TAG = "LauncherProvider";
@@ -85,7 +85,7 @@
* overtime. These must be backwards compatible, else we risk breaking old devices during
* restore or binary version downgrade.
*/
- private static final int DATA_VERSION = 2;
+ private static final int DATA_VERSION = 3;
private static final String PREF_KEY_DATA_VERISON = "provider_data_version";
@@ -262,10 +262,11 @@
if (cn != null) {
try {
- int appWidgetId = new AppWidgetHost(getContext(), Launcher.APPWIDGET_HOST_ID)
- .allocateAppWidgetId();
+ AppWidgetHost widgetHost = mOpenHelper.newLauncherWidgetHost();
+ int appWidgetId = widgetHost.allocateAppWidgetId();
values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
if (!appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,cn)) {
+ widgetHost.deleteAppWidgetId(appWidgetId);
return false;
}
} catch (RuntimeException e) {
@@ -347,23 +348,7 @@
if (Binder.getCallingPid() != Process.myPid()
&& Favorites.TABLE_NAME.equalsIgnoreCase(args.table)) {
- String widgetSelection = TextUtils.isEmpty(args.where) ? "1=1" : args.where;
- widgetSelection = String.format(Locale.ENGLISH, "%1$s = %2$d AND ( %3$s )",
- Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET, widgetSelection);
- try (Cursor c = db.query(Favorites.TABLE_NAME, new String[] { Favorites.APPWIDGET_ID },
- widgetSelection, args.args, null, null, null)) {
- AppWidgetHost host = new AppWidgetHost(getContext(), Launcher.APPWIDGET_HOST_ID);
- while (c.moveToNext()) {
- int widgetId = c.getInt(0);
- if (widgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
- try {
- host.deleteAppWidgetId(widgetId);
- } catch (RuntimeException e) {
- Log.e(TAG, "Error deleting widget id " + widgetId, e);
- }
- }
- }
- }
+ mOpenHelper.removeGhostWidgets(mOpenHelper.getWritableDatabase());
}
int count = db.delete(args.table, args.where, args.args);
if (count > 0) {
@@ -441,6 +426,10 @@
loadDefaultFavoritesIfNecessary();
return null;
}
+ case LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS: {
+ mOpenHelper.removeGhostWidgets(mOpenHelper.getWritableDatabase());
+ return null;
+ }
}
return null;
}
@@ -509,7 +498,7 @@
if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) {
Log.d(TAG, "loading default workspace");
- AppWidgetHost widgetHost = new AppWidgetHost(getContext(), Launcher.APPWIDGET_HOST_ID);
+ AppWidgetHost widgetHost = mOpenHelper.newLauncherWidgetHost();
AutoInstallsLayout loader = createWorkspaceLoaderFromAppRestriction(widgetHost);
if (loader == null) {
loader = AutoInstallsLayout.get(getContext(),widgetHost, mOpenHelper);
@@ -659,7 +648,7 @@
protected void onEmptyDbCreated() {
// Database was just created, so wipe any previous widgets
if (mWidgetHostResetHandler != null) {
- new AppWidgetHost(mContext, Launcher.APPWIDGET_HOST_ID).deleteHost();
+ newLauncherWidgetHost().deleteHost();
mWidgetHostResetHandler.sendEmptyMessage(
ChangeListenerWrapper.MSG_APP_WIDGET_HOST_RESET);
}
@@ -765,6 +754,8 @@
}
}
case 2:
+ removeGhostWidgets(db);
+ case 3:
// data updated
return;
}
@@ -906,6 +897,47 @@
}
/**
+ * Removes widgets which are registered to the Launcher's host, but are not present
+ * in our model.
+ */
+ public void removeGhostWidgets(SQLiteDatabase db) {
+ // Get all existing widget ids.
+ final AppWidgetHost host = newLauncherWidgetHost();
+ final int[] allWidgets;
+ try {
+ Method getter = AppWidgetHost.class.getDeclaredMethod("getAppWidgetIds");
+ getter.setAccessible(true);
+ allWidgets = (int[]) getter.invoke(host);
+ } catch (Exception e) {
+ Log.e(TAG, "getAppWidgetIds not supported", e);
+ return;
+ }
+ try {
+ Cursor c = db.query(Favorites.TABLE_NAME,
+ new String[] {Favorites.APPWIDGET_ID },
+ "itemType=" + Favorites.ITEM_TYPE_APPWIDGET, null, null, null, null);
+ HashSet<Integer> validWidgets = new HashSet<>();
+ while (c.moveToNext()) {
+ validWidgets.add(c.getInt(0));
+ }
+ c.close();
+
+ for (int widgetId : allWidgets) {
+ if (!validWidgets.contains(widgetId)) {
+ try {
+ FileLog.d(TAG, "Deleting invalid widget " + widgetId);
+ host.deleteAppWidgetId(widgetId);
+ } catch (RuntimeException e) {
+ // Ignore
+ }
+ }
+ }
+ } catch (SQLException ex) {
+ Log.w(TAG, "Error getting widgets list", ex);
+ }
+ }
+
+ /**
* Replaces all shortcuts of type {@link Favorites#ITEM_TYPE_SHORTCUT} which have a valid
* launcher activity target with {@link Favorites#ITEM_TYPE_APPLICATION}.
*/
@@ -1074,6 +1106,10 @@
return mMaxItemId;
}
+ public AppWidgetHost newLauncherWidgetHost() {
+ return new AppWidgetHost(mContext, Launcher.APPWIDGET_HOST_ID);
+ }
+
@Override
public long insertAndCheck(SQLiteDatabase db, ContentValues values) {
return dbInsertAndCheck(this, db, Favorites.TABLE_NAME, null, values);
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index af2c102..b25b256 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -299,6 +299,8 @@
public static final String EXTRA_EXTRACTED_COLORS = "extra_extractedColors";
public static final String EXTRA_WALLPAPER_ID = "extra_wallpaperId";
+ public static final String METHOD_REMOVE_GHOST_WIDGETS = "remove_ghost_widgets";
+
public static final String EXTRA_VALUE = "value";
public static Bundle call(ContentResolver cr, String method) {