blob: c748693f9bd991b1b5bec8091a8d98a7a7e022d2 [file] [log] [blame]
Sunny Goyal3a5a9d12014-10-01 15:33:41 -07001package com.android.launcher3;
2
Sunny Goyal86df1382016-08-10 15:03:22 -07003import android.appwidget.AppWidgetManager;
4import android.content.ComponentName;
Sunny Goyal3a5a9d12014-10-01 15:33:41 -07005import android.content.Context;
6import android.content.Intent;
7import android.content.pm.ActivityInfo;
8import android.content.pm.ApplicationInfo;
9import android.content.pm.PackageManager;
10import android.content.pm.ResolveInfo;
11import android.content.res.Resources;
Sunny Goyal86df1382016-08-10 15:03:22 -070012import android.os.Bundle;
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070013import android.text.TextUtils;
Rajeev Kumar26453a22017-06-09 16:02:25 -070014import android.util.ArrayMap;
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070015import android.util.Log;
Samuel Fufaca37b8a2019-08-19 17:04:36 -070016
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070017import com.android.launcher3.LauncherSettings.Favorites;
Sunny Goyal3e58eea2022-11-14 14:30:07 -080018import com.android.launcher3.util.Partner;
Adam Cohen091440a2015-03-18 14:16:05 -070019import com.android.launcher3.util.Thunk;
Sihua Ma8bbfcb62022-11-08 16:46:07 -080020import com.android.launcher3.widget.LauncherWidgetHolder;
Samuel Fufaca37b8a2019-08-19 17:04:36 -070021
22import org.xmlpull.v1.XmlPullParser;
23import org.xmlpull.v1.XmlPullParserException;
24
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070025import java.io.IOException;
26import java.net.URISyntaxException;
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070027import java.util.List;
28
29/**
30 * Implements the layout parser with rules for internal layouts and partner layouts.
31 */
32public class DefaultLayoutParser extends AutoInstallsLayout {
33 private static final String TAG = "DefaultLayoutParser";
34
Sunny Goyalbb3b02f2015-01-15 12:00:14 -080035 protected static final String TAG_RESOLVE = "resolve";
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070036 private static final String TAG_FAVORITES = "favorites";
Sunny Goyalbb3b02f2015-01-15 12:00:14 -080037 protected static final String TAG_FAVORITE = "favorite";
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070038 private static final String TAG_APPWIDGET = "appwidget";
Sunny Goyala5c8a9e2016-07-08 08:32:44 -070039 protected static final String TAG_SHORTCUT = "shortcut";
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070040 private static final String TAG_FOLDER = "folder";
41 private static final String TAG_PARTNER_FOLDER = "partner-folder";
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070042
Sunny Goyalbb3b02f2015-01-15 12:00:14 -080043 protected static final String ATTR_URI = "uri";
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070044 private static final String ATTR_CONTAINER = "container";
45 private static final String ATTR_SCREEN = "screen";
46 private static final String ATTR_FOLDER_ITEMS = "folderItems";
47
Sunny Goyal3e58eea2022-11-14 14:30:07 -080048 public static final String RES_PARTNER_FOLDER = "partner_folder";
49 public static final String RES_PARTNER_DEFAULT_LAYOUT = "partner_default_layout";
50
Sunny Goyal86df1382016-08-10 15:03:22 -070051 // TODO: Remove support for this broadcast, instead use widget options to send bind time options
52 private static final String ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE =
53 "com.android.launcher.action.APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE";
54
Sihua Maaa2b8722022-10-25 15:17:58 -070055 public DefaultLayoutParser(Context context, LauncherWidgetHolder appWidgetHolder,
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070056 LayoutParserCallback callback, Resources sourceRes, int layoutId) {
Sihua Maaa2b8722022-10-25 15:17:58 -070057 super(context, appWidgetHolder, callback, sourceRes, layoutId, TAG_FAVORITES);
Sunny Goyalbb3b02f2015-01-15 12:00:14 -080058 }
59
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070060 @Override
Rajeev Kumar26453a22017-06-09 16:02:25 -070061 protected ArrayMap<String, TagParser> getFolderElementsMap() {
Rajeev Kumar26453a22017-06-09 16:02:25 -070062 ArrayMap<String, TagParser> parsers = new ArrayMap<>();
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070063 parsers.put(TAG_FAVORITE, new AppShortcutWithUriParser());
Sunny Goyalafaa8f02023-03-30 12:34:43 -070064 parsers.put(TAG_SHORTCUT, new ShortcutParser());
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070065 return parsers;
66 }
67
68 @Override
Rajeev Kumar26453a22017-06-09 16:02:25 -070069 protected ArrayMap<String, TagParser> getLayoutElementsMap() {
70 ArrayMap<String, TagParser> parsers = new ArrayMap<>();
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070071 parsers.put(TAG_FAVORITE, new AppShortcutWithUriParser());
72 parsers.put(TAG_APPWIDGET, new AppWidgetParser());
Samuel Fufaca37b8a2019-08-19 17:04:36 -070073 parsers.put(TAG_SEARCH_WIDGET, new SearchWidgetParser());
Sunny Goyalafaa8f02023-03-30 12:34:43 -070074 parsers.put(TAG_SHORTCUT, new ShortcutParser());
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070075 parsers.put(TAG_RESOLVE, new ResolveParser());
76 parsers.put(TAG_FOLDER, new MyFolderParser());
77 parsers.put(TAG_PARTNER_FOLDER, new PartnerFolderParser());
78 return parsers;
79 }
80
81 @Override
Sunny Goyal0d742312019-03-04 20:22:26 -080082 protected void parseContainerAndScreen(XmlPullParser parser, int[] out) {
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070083 out[0] = LauncherSettings.Favorites.CONTAINER_DESKTOP;
84 String strContainer = getAttributeValue(parser, ATTR_CONTAINER);
85 if (strContainer != null) {
Sunny Goyalefb7e842018-10-04 15:11:00 -070086 out[0] = Integer.parseInt(strContainer);
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070087 }
Sunny Goyalefb7e842018-10-04 15:11:00 -070088 out[1] = Integer.parseInt(getAttributeValue(parser, ATTR_SCREEN));
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070089 }
90
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070091 /**
92 * AppShortcutParser which also supports adding URI based intents
93 */
Sunny Goyala5c8a9e2016-07-08 08:32:44 -070094 public class AppShortcutWithUriParser extends AppShortcutParser {
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070095
96 @Override
Sunny Goyal0d742312019-03-04 20:22:26 -080097 protected int invalidPackageOrClass(XmlPullParser parser) {
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070098 final String uri = getAttributeValue(parser, ATTR_URI);
99 if (TextUtils.isEmpty(uri)) {
100 Log.e(TAG, "Skipping invalid <favorite> with no component or uri");
101 return -1;
102 }
103
104 final Intent metaIntent;
105 try {
106 metaIntent = Intent.parseUri(uri, 0);
107 } catch (URISyntaxException e) {
108 Log.e(TAG, "Unable to add meta-favorite: " + uri, e);
109 return -1;
110 }
111
112 ResolveInfo resolved = mPackageManager.resolveActivity(metaIntent,
113 PackageManager.MATCH_DEFAULT_ONLY);
114 final List<ResolveInfo> appList = mPackageManager.queryIntentActivities(
115 metaIntent, PackageManager.MATCH_DEFAULT_ONLY);
116
117 // Verify that the result is an app and not just the resolver dialog asking which
118 // app to use.
119 if (wouldLaunchResolverActivity(resolved, appList)) {
120 // If only one of the results is a system app then choose that as the default.
121 final ResolveInfo systemApp = getSingleSystemActivity(appList);
122 if (systemApp == null) {
123 // There is no logical choice for this meta-favorite, so rather than making
124 // a bad choice just add nothing.
125 Log.w(TAG, "No preference or single system activity found for "
126 + metaIntent.toString());
127 return -1;
128 }
129 resolved = systemApp;
130 }
131 final ActivityInfo info = resolved.activityInfo;
132 final Intent intent = mPackageManager.getLaunchIntentForPackage(info.packageName);
133 if (intent == null) {
134 return -1;
135 }
136 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
137 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
138
139 return addShortcut(info.loadLabel(mPackageManager).toString(), intent,
140 Favorites.ITEM_TYPE_APPLICATION);
141 }
142
143 private ResolveInfo getSingleSystemActivity(List<ResolveInfo> appList) {
144 ResolveInfo systemResolve = null;
145 final int N = appList.size();
146 for (int i = 0; i < N; ++i) {
147 try {
148 ApplicationInfo info = mPackageManager.getApplicationInfo(
149 appList.get(i).activityInfo.packageName, 0);
150 if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
151 if (systemResolve != null) {
152 return null;
153 } else {
154 systemResolve = appList.get(i);
155 }
156 }
157 } catch (PackageManager.NameNotFoundException e) {
158 Log.w(TAG, "Unable to get info about resolve results", e);
159 return null;
160 }
161 }
162 return systemResolve;
163 }
164
165 private boolean wouldLaunchResolverActivity(ResolveInfo resolved,
166 List<ResolveInfo> appList) {
167 // If the list contains the above resolved activity, then it can't be
168 // ResolverActivity itself.
169 for (int i = 0; i < appList.size(); ++i) {
170 ResolveInfo tmp = appList.get(i);
171 if (tmp.activityInfo.name.equals(resolved.activityInfo.name)
172 && tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) {
173 return false;
174 }
175 }
176 return true;
177 }
178 }
179
Sunny Goyal3a5a9d12014-10-01 15:33:41 -0700180 /**
Sunny Goyal3a5a9d12014-10-01 15:33:41 -0700181 * Contains a list of <favorite> nodes, and accepts the first successfully parsed node.
182 */
Sunny Goyala5c8a9e2016-07-08 08:32:44 -0700183 public class ResolveParser implements TagParser {
Sunny Goyal3a5a9d12014-10-01 15:33:41 -0700184
185 private final AppShortcutWithUriParser mChildParser = new AppShortcutWithUriParser();
186
187 @Override
Sunny Goyal0d742312019-03-04 20:22:26 -0800188 public int parseAndAdd(XmlPullParser parser) throws XmlPullParserException,
Sunny Goyal3a5a9d12014-10-01 15:33:41 -0700189 IOException {
190 final int groupDepth = parser.getDepth();
191 int type;
Sunny Goyalefb7e842018-10-04 15:11:00 -0700192 int addedId = -1;
Sunny Goyal3a5a9d12014-10-01 15:33:41 -0700193 while ((type = parser.next()) != XmlPullParser.END_TAG ||
194 parser.getDepth() > groupDepth) {
195 if (type != XmlPullParser.START_TAG || addedId > -1) {
196 continue;
197 }
198 final String fallback_item_name = parser.getName();
199 if (TAG_FAVORITE.equals(fallback_item_name)) {
200 addedId = mChildParser.parseAndAdd(parser);
201 } else {
202 Log.e(TAG, "Fallback groups can contain only favorites, found "
203 + fallback_item_name);
204 }
205 }
206 return addedId;
207 }
208 }
209
210 /**
211 * A parser which adds a folder whose contents come from partner apk.
212 */
Samuel Fufaca37b8a2019-08-19 17:04:36 -0700213 @Thunk
214 class PartnerFolderParser implements TagParser {
Sunny Goyal3a5a9d12014-10-01 15:33:41 -0700215
216 @Override
Sunny Goyal0d742312019-03-04 20:22:26 -0800217 public int parseAndAdd(XmlPullParser parser) throws XmlPullParserException,
Sunny Goyal3a5a9d12014-10-01 15:33:41 -0700218 IOException {
219 // Folder contents come from an external XML resource
220 final Partner partner = Partner.get(mPackageManager);
221 if (partner != null) {
Sunny Goyal3e58eea2022-11-14 14:30:07 -0800222 final int resId = partner.getXmlResId(RES_PARTNER_FOLDER);
Sunny Goyal3a5a9d12014-10-01 15:33:41 -0700223 if (resId != 0) {
Sunny Goyalafaa8f02023-03-30 12:34:43 -0700224 final XmlPullParser partnerParser = partner.getResources().getXml(resId);
Sunny Goyal3a5a9d12014-10-01 15:33:41 -0700225 beginDocument(partnerParser, TAG_FOLDER);
Sunny Goyalafaa8f02023-03-30 12:34:43 -0700226 FolderParser folderParser = new FolderParser(getFolderElementsMap());
Sunny Goyal3a5a9d12014-10-01 15:33:41 -0700227 return folderParser.parseAndAdd(partnerParser);
228 }
229 }
230 return -1;
231 }
232 }
233
234 /**
235 * An extension of FolderParser which allows adding items from a different xml.
236 */
Samuel Fufaca37b8a2019-08-19 17:04:36 -0700237 @Thunk
238 class MyFolderParser extends FolderParser {
Sunny Goyal3a5a9d12014-10-01 15:33:41 -0700239
240 @Override
Sunny Goyal0d742312019-03-04 20:22:26 -0800241 public int parseAndAdd(XmlPullParser parser) throws XmlPullParserException,
Sunny Goyal3a5a9d12014-10-01 15:33:41 -0700242 IOException {
243 final int resId = getAttributeResourceValue(parser, ATTR_FOLDER_ITEMS, 0);
244 if (resId != 0) {
245 parser = mSourceRes.getXml(resId);
246 beginDocument(parser, TAG_FOLDER);
247 }
248 return super.parseAndAdd(parser);
249 }
250 }
Sunny Goyal86df1382016-08-10 15:03:22 -0700251
252
253 /**
254 * AppWidget parser which enforces that the app is already installed when the layout is parsed.
255 */
256 protected class AppWidgetParser extends PendingWidgetParser {
257
258 @Override
Sunny Goyalefb7e842018-10-04 15:11:00 -0700259 protected int verifyAndInsert(ComponentName cn, Bundle extras) {
Sunny Goyal86df1382016-08-10 15:03:22 -0700260 try {
261 mPackageManager.getReceiverInfo(cn, 0);
262 } catch (Exception e) {
263 String[] packages = mPackageManager.currentToCanonicalPackageNames(
Samuel Fufaca37b8a2019-08-19 17:04:36 -0700264 new String[]{cn.getPackageName()});
Sunny Goyal86df1382016-08-10 15:03:22 -0700265 cn = new ComponentName(packages[0], cn.getClassName());
266 try {
267 mPackageManager.getReceiverInfo(cn, 0);
268 } catch (Exception e1) {
269 Log.d(TAG, "Can't find widget provider: " + cn.getClassName());
270 return -1;
271 }
272 }
273
274 final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
Sunny Goyalefb7e842018-10-04 15:11:00 -0700275 int insertedId = -1;
Sunny Goyal86df1382016-08-10 15:03:22 -0700276 try {
Sihua Maaa2b8722022-10-25 15:17:58 -0700277 int appWidgetId = mAppWidgetHolder.allocateAppWidgetId();
Sunny Goyal86df1382016-08-10 15:03:22 -0700278
279 if (!appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, cn)) {
280 Log.e(TAG, "Unable to bind app widget id " + cn);
Sihua Maaa2b8722022-10-25 15:17:58 -0700281 mAppWidgetHolder.deleteAppWidgetId(appWidgetId);
Sunny Goyal86df1382016-08-10 15:03:22 -0700282 return -1;
283 }
284
285 mValues.put(Favorites.APPWIDGET_ID, appWidgetId);
286 mValues.put(Favorites.APPWIDGET_PROVIDER, cn.flattenToString());
287 mValues.put(Favorites._ID, mCallback.generateNewItemId());
288 insertedId = mCallback.insertAndCheck(mDb, mValues);
289 if (insertedId < 0) {
Sihua Maaa2b8722022-10-25 15:17:58 -0700290 mAppWidgetHolder.deleteAppWidgetId(appWidgetId);
Sunny Goyal86df1382016-08-10 15:03:22 -0700291 return insertedId;
292 }
293
294 // Send a broadcast to configure the widget
295 if (!extras.isEmpty()) {
296 Intent intent = new Intent(ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE);
297 intent.setComponent(cn);
298 intent.putExtras(extras);
299 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
300 mContext.sendBroadcast(intent);
301 }
302 } catch (RuntimeException ex) {
303 Log.e(TAG, "Problem allocating appWidgetId", ex);
304 }
305 return insertedId;
306 }
307 }
Sunny Goyal3a5a9d12014-10-01 15:33:41 -0700308}