• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.launcher3;
2 
3 import static android.os.Process.myUserHandle;
4 
5 import android.appwidget.AppWidgetHost;
6 import android.appwidget.AppWidgetManager;
7 import android.appwidget.AppWidgetProviderInfo;
8 import android.content.BroadcastReceiver;
9 import android.content.ContentResolver;
10 import android.content.Context;
11 import android.content.Intent;
12 import android.database.Cursor;
13 import android.util.Log;
14 
15 import androidx.annotation.NonNull;
16 import androidx.annotation.WorkerThread;
17 
18 import com.android.launcher3.LauncherSettings.Favorites;
19 import com.android.launcher3.model.LoaderTask;
20 import com.android.launcher3.model.WidgetsModel;
21 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
22 import com.android.launcher3.pm.UserCache;
23 import com.android.launcher3.provider.RestoreDbTask;
24 import com.android.launcher3.util.ContentWriter;
25 import com.android.launcher3.widget.LauncherWidgetHolder;
26 
27 public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
28 
29     private static final String TAG = "AWRestoredReceiver";
30 
31     @Override
onReceive(final Context context, Intent intent)32     public void onReceive(final Context context, Intent intent) {
33         if (AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED.equals(intent.getAction())) {
34             int hostId = intent.getIntExtra(AppWidgetManager.EXTRA_HOST_ID, 0);
35             Log.d(TAG, "Widget ID map received for host:" + hostId);
36             if (hostId != LauncherWidgetHolder.APPWIDGET_HOST_ID) {
37                 return;
38             }
39 
40             final int[] oldIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS);
41             final int[] newIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
42             if (oldIds != null && newIds != null && oldIds.length == newIds.length) {
43                 RestoreDbTask.setRestoredAppWidgetIds(context, oldIds, newIds);
44             } else {
45                 Log.e(TAG, "Invalid host restored received");
46             }
47         }
48     }
49 
50     /**
51      * Updates the app widgets whose id has changed during the restore process.
52      */
53     @WorkerThread
restoreAppWidgetIds(Context context, int[] oldWidgetIds, int[] newWidgetIds, @NonNull AppWidgetHost host)54     public static void restoreAppWidgetIds(Context context, int[] oldWidgetIds, int[] newWidgetIds,
55             @NonNull AppWidgetHost host) {
56         if (WidgetsModel.GO_DISABLE_WIDGETS) {
57             Log.e(TAG, "Skipping widget ID remap as widgets not supported");
58             host.deleteHost();
59             return;
60         }
61         if (!RestoreDbTask.isPending(context)) {
62             // Someone has already gone through our DB once, probably LoaderTask. Skip any further
63             // modifications of the DB.
64             Log.e(TAG, "Skipping widget ID remap as DB already in use");
65             for (int widgetId : newWidgetIds) {
66                 Log.d(TAG, "Deleting widgetId: " + widgetId);
67                 host.deleteAppWidgetId(widgetId);
68             }
69             return;
70         }
71         final ContentResolver cr = context.getContentResolver();
72         final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
73 
74         for (int i = 0; i < oldWidgetIds.length; i++) {
75             Log.i(TAG, "Widget state restore id " + oldWidgetIds[i] + " => " + newWidgetIds[i]);
76 
77             final AppWidgetProviderInfo provider = widgets.getAppWidgetInfo(newWidgetIds[i]);
78             final int state;
79             if (LoaderTask.isValidProvider(provider)) {
80                 // This will ensure that we show 'Click to setup' UI if required.
81                 state = LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
82             } else {
83                 state = LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
84             }
85 
86             // b/135926478: Work profile widget restore is broken in platform. This forces us to
87             // recreate the widget during loading with the correct host provider.
88             long mainProfileId = UserCache.INSTANCE.get(context)
89                     .getSerialNumberForUser(myUserHandle());
90             String oldWidgetId = Integer.toString(oldWidgetIds[i]);
91             final String where = "appWidgetId=? and (restored & 1) = 1 and profileId=?";
92             final String[] args = new String[] { oldWidgetId, Long.toString(mainProfileId) };
93             int result = new ContentWriter(context, new ContentWriter.CommitParams(where, args))
94                     .put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i])
95                     .put(LauncherSettings.Favorites.RESTORED, state)
96                     .commit();
97             if (result == 0) {
98                 Cursor cursor = cr.query(Favorites.CONTENT_URI,
99                         new String[] {Favorites.APPWIDGET_ID},
100                         "appWidgetId=?", new String[] { oldWidgetId }, null);
101                 try {
102                     if (!cursor.moveToFirst()) {
103                         // The widget no long exists.
104                         host.deleteAppWidgetId(newWidgetIds[i]);
105                     }
106                 } finally {
107                     cursor.close();
108                 }
109             }
110             // attempt to update widget id in backup table as well
111             new ContentWriter(context, ContentWriter.CommitParams.backupCommitParams(
112                     "appWidgetId=? and profileId=?", args))
113                     .put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i])
114                     .put(LauncherSettings.Favorites.RESTORED, state)
115                     .commit();
116         }
117 
118         LauncherAppState app = LauncherAppState.getInstanceNoCreate();
119         if (app != null) {
120             app.getModel().forceReload();
121         }
122     }
123 }
124