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