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