blob: 48372388ae9cbcb9110da5169c4ea6070499e3ec [file] [log] [blame]
Sunny Goyal3a5a9d12014-10-01 15:33:41 -07001package com.android.launcher3;
2
3import android.appwidget.AppWidgetHost;
4import android.content.Context;
5import android.content.Intent;
6import android.content.pm.ActivityInfo;
7import android.content.pm.ApplicationInfo;
8import android.content.pm.PackageManager;
9import android.content.pm.ResolveInfo;
10import android.content.res.Resources;
11import android.content.res.XmlResourceParser;
12import android.text.TextUtils;
13import android.util.Log;
14
15import com.android.launcher3.LauncherSettings.Favorites;
16
17import org.xmlpull.v1.XmlPullParser;
18import org.xmlpull.v1.XmlPullParserException;
19
20import java.io.IOException;
21import java.net.URISyntaxException;
22import java.util.ArrayList;
23import java.util.HashMap;
24import java.util.List;
25
26/**
27 * Implements the layout parser with rules for internal layouts and partner layouts.
28 */
29public class DefaultLayoutParser extends AutoInstallsLayout {
30 private static final String TAG = "DefaultLayoutParser";
31
Sunny Goyalbb3b02f2015-01-15 12:00:14 -080032 protected static final String TAG_RESOLVE = "resolve";
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070033 private static final String TAG_FAVORITES = "favorites";
Sunny Goyalbb3b02f2015-01-15 12:00:14 -080034 protected static final String TAG_FAVORITE = "favorite";
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070035 private static final String TAG_APPWIDGET = "appwidget";
36 private static final String TAG_SHORTCUT = "shortcut";
37 private static final String TAG_FOLDER = "folder";
38 private static final String TAG_PARTNER_FOLDER = "partner-folder";
39 private static final String TAG_INCLUDE = "include";
40
Sunny Goyalbb3b02f2015-01-15 12:00:14 -080041 protected static final String ATTR_URI = "uri";
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070042 private static final String ATTR_WORKSPACE = "workspace";
43 private static final String ATTR_CONTAINER = "container";
44 private static final String ATTR_SCREEN = "screen";
45 private static final String ATTR_FOLDER_ITEMS = "folderItems";
46
47 public DefaultLayoutParser(Context context, AppWidgetHost appWidgetHost,
48 LayoutParserCallback callback, Resources sourceRes, int layoutId) {
49 super(context, appWidgetHost, callback, sourceRes, layoutId, TAG_FAVORITES);
Sunny Goyalbb3b02f2015-01-15 12:00:14 -080050 }
51
52 public DefaultLayoutParser(Context context, AppWidgetHost appWidgetHost,
53 LayoutParserCallback callback, Resources sourceRes, int layoutId, String rootTag,
54 int hotseatAllAppsRank) {
55 super(context, appWidgetHost, callback, sourceRes, layoutId, rootTag, hotseatAllAppsRank);
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070056 }
57
58 @Override
59 protected HashMap<String, TagParser> getFolderElementsMap() {
60 return getFolderElementsMap(mSourceRes);
61 }
62
63 private HashMap<String, TagParser> getFolderElementsMap(Resources res) {
64 HashMap<String, TagParser> parsers = new HashMap<String, TagParser>();
65 parsers.put(TAG_FAVORITE, new AppShortcutWithUriParser());
66 parsers.put(TAG_SHORTCUT, new UriShortcutParser(res));
67 return parsers;
68 }
69
70 @Override
71 protected HashMap<String, TagParser> getLayoutElementsMap() {
72 HashMap<String, TagParser> parsers = new HashMap<String, TagParser>();
73 parsers.put(TAG_FAVORITE, new AppShortcutWithUriParser());
74 parsers.put(TAG_APPWIDGET, new AppWidgetParser());
75 parsers.put(TAG_SHORTCUT, new UriShortcutParser(mSourceRes));
76 parsers.put(TAG_RESOLVE, new ResolveParser());
77 parsers.put(TAG_FOLDER, new MyFolderParser());
78 parsers.put(TAG_PARTNER_FOLDER, new PartnerFolderParser());
79 return parsers;
80 }
81
82 @Override
83 protected void parseContainerAndScreen(XmlResourceParser parser, long[] out) {
84 out[0] = LauncherSettings.Favorites.CONTAINER_DESKTOP;
85 String strContainer = getAttributeValue(parser, ATTR_CONTAINER);
86 if (strContainer != null) {
87 out[0] = Long.valueOf(strContainer);
88 }
89 out[1] = Long.parseLong(getAttributeValue(parser, ATTR_SCREEN));
90 }
91
92 @Override
93 protected int parseAndAddNode(
94 XmlResourceParser parser,
95 HashMap<String, TagParser> tagParserMap,
96 ArrayList<Long> screenIds)
97 throws XmlPullParserException, IOException {
98 if (TAG_INCLUDE.equals(parser.getName())) {
99 final int resId = getAttributeResourceValue(parser, ATTR_WORKSPACE, 0);
100 if (resId != 0) {
101 // recursively load some more favorites, why not?
102 return parseLayout(resId, screenIds);
103 } else {
104 return 0;
105 }
106 } else {
107 return super.parseAndAddNode(parser, tagParserMap, screenIds);
108 }
109 }
110
111 /**
112 * AppShortcutParser which also supports adding URI based intents
113 */
114 private class AppShortcutWithUriParser extends AppShortcutParser {
115
116 @Override
117 protected long invalidPackageOrClass(XmlResourceParser parser) {
118 final String uri = getAttributeValue(parser, ATTR_URI);
119 if (TextUtils.isEmpty(uri)) {
120 Log.e(TAG, "Skipping invalid <favorite> with no component or uri");
121 return -1;
122 }
123
124 final Intent metaIntent;
125 try {
126 metaIntent = Intent.parseUri(uri, 0);
127 } catch (URISyntaxException e) {
128 Log.e(TAG, "Unable to add meta-favorite: " + uri, e);
129 return -1;
130 }
131
132 ResolveInfo resolved = mPackageManager.resolveActivity(metaIntent,
133 PackageManager.MATCH_DEFAULT_ONLY);
134 final List<ResolveInfo> appList = mPackageManager.queryIntentActivities(
135 metaIntent, PackageManager.MATCH_DEFAULT_ONLY);
136
137 // Verify that the result is an app and not just the resolver dialog asking which
138 // app to use.
139 if (wouldLaunchResolverActivity(resolved, appList)) {
140 // If only one of the results is a system app then choose that as the default.
141 final ResolveInfo systemApp = getSingleSystemActivity(appList);
142 if (systemApp == null) {
143 // There is no logical choice for this meta-favorite, so rather than making
144 // a bad choice just add nothing.
145 Log.w(TAG, "No preference or single system activity found for "
146 + metaIntent.toString());
147 return -1;
148 }
149 resolved = systemApp;
150 }
151 final ActivityInfo info = resolved.activityInfo;
152 final Intent intent = mPackageManager.getLaunchIntentForPackage(info.packageName);
153 if (intent == null) {
154 return -1;
155 }
156 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
157 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
158
159 return addShortcut(info.loadLabel(mPackageManager).toString(), intent,
160 Favorites.ITEM_TYPE_APPLICATION);
161 }
162
163 private ResolveInfo getSingleSystemActivity(List<ResolveInfo> appList) {
164 ResolveInfo systemResolve = null;
165 final int N = appList.size();
166 for (int i = 0; i < N; ++i) {
167 try {
168 ApplicationInfo info = mPackageManager.getApplicationInfo(
169 appList.get(i).activityInfo.packageName, 0);
170 if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
171 if (systemResolve != null) {
172 return null;
173 } else {
174 systemResolve = appList.get(i);
175 }
176 }
177 } catch (PackageManager.NameNotFoundException e) {
178 Log.w(TAG, "Unable to get info about resolve results", e);
179 return null;
180 }
181 }
182 return systemResolve;
183 }
184
185 private boolean wouldLaunchResolverActivity(ResolveInfo resolved,
186 List<ResolveInfo> appList) {
187 // If the list contains the above resolved activity, then it can't be
188 // ResolverActivity itself.
189 for (int i = 0; i < appList.size(); ++i) {
190 ResolveInfo tmp = appList.get(i);
191 if (tmp.activityInfo.name.equals(resolved.activityInfo.name)
192 && tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) {
193 return false;
194 }
195 }
196 return true;
197 }
198 }
199
200
201 /**
202 * Shortcut parser which allows any uri and not just web urls.
203 */
204 private class UriShortcutParser extends ShortcutParser {
205
206 public UriShortcutParser(Resources iconRes) {
207 super(iconRes);
208 }
209
210 @Override
211 protected Intent parseIntent(XmlResourceParser parser) {
212 String uri = null;
213 try {
214 uri = getAttributeValue(parser, ATTR_URI);
215 return Intent.parseUri(uri, 0);
216 } catch (URISyntaxException e) {
217 Log.w(TAG, "Shortcut has malformed uri: " + uri);
218 return null; // Oh well
219 }
220 }
221 }
222
223 /**
224 * Contains a list of <favorite> nodes, and accepts the first successfully parsed node.
225 */
Sunny Goyalbb3b02f2015-01-15 12:00:14 -0800226 protected class ResolveParser implements TagParser {
Sunny Goyal3a5a9d12014-10-01 15:33:41 -0700227
228 private final AppShortcutWithUriParser mChildParser = new AppShortcutWithUriParser();
229
230 @Override
231 public long parseAndAdd(XmlResourceParser parser) throws XmlPullParserException,
232 IOException {
233 final int groupDepth = parser.getDepth();
234 int type;
235 long addedId = -1;
236 while ((type = parser.next()) != XmlPullParser.END_TAG ||
237 parser.getDepth() > groupDepth) {
238 if (type != XmlPullParser.START_TAG || addedId > -1) {
239 continue;
240 }
241 final String fallback_item_name = parser.getName();
242 if (TAG_FAVORITE.equals(fallback_item_name)) {
243 addedId = mChildParser.parseAndAdd(parser);
244 } else {
245 Log.e(TAG, "Fallback groups can contain only favorites, found "
246 + fallback_item_name);
247 }
248 }
249 return addedId;
250 }
251 }
252
253 /**
254 * A parser which adds a folder whose contents come from partner apk.
255 */
256 private class PartnerFolderParser implements TagParser {
257
258 @Override
259 public long parseAndAdd(XmlResourceParser parser) throws XmlPullParserException,
260 IOException {
261 // Folder contents come from an external XML resource
262 final Partner partner = Partner.get(mPackageManager);
263 if (partner != null) {
264 final Resources partnerRes = partner.getResources();
265 final int resId = partnerRes.getIdentifier(Partner.RES_FOLDER,
266 "xml", partner.getPackageName());
267 if (resId != 0) {
268 final XmlResourceParser partnerParser = partnerRes.getXml(resId);
269 beginDocument(partnerParser, TAG_FOLDER);
270
271 FolderParser folderParser = new FolderParser(getFolderElementsMap(partnerRes));
272 return folderParser.parseAndAdd(partnerParser);
273 }
274 }
275 return -1;
276 }
277 }
278
279 /**
280 * An extension of FolderParser which allows adding items from a different xml.
281 */
282 private class MyFolderParser extends FolderParser {
283
284 @Override
285 public long parseAndAdd(XmlResourceParser parser) throws XmlPullParserException,
286 IOException {
287 final int resId = getAttributeResourceValue(parser, ATTR_FOLDER_ITEMS, 0);
288 if (resId != 0) {
289 parser = mSourceRes.getXml(resId);
290 beginDocument(parser, TAG_FOLDER);
291 }
292 return super.parseAndAdd(parser);
293 }
294 }
295}