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