From 55fddc825b24b78ebdeba8ed11ed8de9e77ad62b Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 6 Apr 2017 15:02:52 -0700 Subject: [PATCH] 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 --- src/com/android/launcher3/LauncherModel.java | 4 + .../android/launcher3/LauncherProvider.java | 84 +++++++++++++------ .../android/launcher3/LauncherSettings.java | 2 + 3 files changed, 66 insertions(+), 24 deletions(-) diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index f8c591c13d..579352d136 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -1249,6 +1249,10 @@ public class LauncherModel extends BroadcastReceiver 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 6302744699..7d85ac1f9f 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -47,7 +47,6 @@ import android.os.UserHandle; 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 com.android.launcher3.util.Thunk; 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 @@ public class LauncherProvider extends ContentProvider { * 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 @@ public class LauncherProvider extends ContentProvider { 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 @@ public class LauncherProvider extends ContentProvider { 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 @@ public class LauncherProvider extends ContentProvider { loadDefaultFavoritesIfNecessary(); return null; } + case LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS: { + mOpenHelper.removeGhostWidgets(mOpenHelper.getWritableDatabase()); + return null; + } } return null; } @@ -509,7 +498,7 @@ public class LauncherProvider extends ContentProvider { 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 @@ public class LauncherProvider extends ContentProvider { 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 @@ public class LauncherProvider extends ContentProvider { } } case 2: + removeGhostWidgets(db); + case 3: // data updated return; } @@ -905,6 +896,47 @@ public class LauncherProvider extends ContentProvider { } } + /** + * 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 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 @@ public class LauncherProvider extends ContentProvider { 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 af2c10275f..b25b256af9 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -299,6 +299,8 @@ public class LauncherSettings { 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) {