blob: 2bba3800621bcf97ab3910060dd43cdb2d7cb218 [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;
Adam Cohen091440a2015-03-18 14:16:05 -070016import com.android.launcher3.util.Thunk;
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070017
18import org.xmlpull.v1.XmlPullParser;
19import org.xmlpull.v1.XmlPullParserException;
20
21import java.io.IOException;
22import java.net.URISyntaxException;
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070023import 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";
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070039
Sunny Goyalbb3b02f2015-01-15 12:00:14 -080040 protected static final String ATTR_URI = "uri";
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070041 private static final String ATTR_CONTAINER = "container";
42 private static final String ATTR_SCREEN = "screen";
43 private static final String ATTR_FOLDER_ITEMS = "folderItems";
44
45 public DefaultLayoutParser(Context context, AppWidgetHost appWidgetHost,
46 LayoutParserCallback callback, Resources sourceRes, int layoutId) {
47 super(context, appWidgetHost, callback, sourceRes, layoutId, TAG_FAVORITES);
Sunny Goyalbb3b02f2015-01-15 12:00:14 -080048 }
49
50 public DefaultLayoutParser(Context context, AppWidgetHost appWidgetHost,
Sunny Goyal96a09632015-12-16 11:32:54 -080051 LayoutParserCallback callback, Resources sourceRes, int layoutId, String rootTag) {
52 super(context, appWidgetHost, callback, sourceRes, layoutId, rootTag);
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070053 }
54
55 @Override
56 protected HashMap<String, TagParser> getFolderElementsMap() {
57 return getFolderElementsMap(mSourceRes);
58 }
59
Adam Cohen091440a2015-03-18 14:16:05 -070060 @Thunk HashMap<String, TagParser> getFolderElementsMap(Resources res) {
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070061 HashMap<String, TagParser> parsers = new HashMap<String, TagParser>();
62 parsers.put(TAG_FAVORITE, new AppShortcutWithUriParser());
63 parsers.put(TAG_SHORTCUT, new UriShortcutParser(res));
64 return parsers;
65 }
66
67 @Override
68 protected HashMap<String, TagParser> getLayoutElementsMap() {
69 HashMap<String, TagParser> parsers = new HashMap<String, TagParser>();
70 parsers.put(TAG_FAVORITE, new AppShortcutWithUriParser());
71 parsers.put(TAG_APPWIDGET, new AppWidgetParser());
72 parsers.put(TAG_SHORTCUT, new UriShortcutParser(mSourceRes));
73 parsers.put(TAG_RESOLVE, new ResolveParser());
74 parsers.put(TAG_FOLDER, new MyFolderParser());
75 parsers.put(TAG_PARTNER_FOLDER, new PartnerFolderParser());
76 return parsers;
77 }
78
79 @Override
80 protected void parseContainerAndScreen(XmlResourceParser parser, long[] out) {
81 out[0] = LauncherSettings.Favorites.CONTAINER_DESKTOP;
82 String strContainer = getAttributeValue(parser, ATTR_CONTAINER);
83 if (strContainer != null) {
84 out[0] = Long.valueOf(strContainer);
85 }
86 out[1] = Long.parseLong(getAttributeValue(parser, ATTR_SCREEN));
87 }
88
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070089 /**
90 * AppShortcutParser which also supports adding URI based intents
91 */
Adam Cohen091440a2015-03-18 14:16:05 -070092 @Thunk class AppShortcutWithUriParser extends AppShortcutParser {
Sunny Goyal3a5a9d12014-10-01 15:33:41 -070093
94 @Override
95 protected long invalidPackageOrClass(XmlResourceParser parser) {
96 final String uri = getAttributeValue(parser, ATTR_URI);
97 if (TextUtils.isEmpty(uri)) {
98 Log.e(TAG, "Skipping invalid <favorite> with no component or uri");
99 return -1;
100 }
101
102 final Intent metaIntent;
103 try {
104 metaIntent = Intent.parseUri(uri, 0);
105 } catch (URISyntaxException e) {
106 Log.e(TAG, "Unable to add meta-favorite: " + uri, e);
107 return -1;
108 }
109
110 ResolveInfo resolved = mPackageManager.resolveActivity(metaIntent,
111 PackageManager.MATCH_DEFAULT_ONLY);
112 final List<ResolveInfo> appList = mPackageManager.queryIntentActivities(
113 metaIntent, PackageManager.MATCH_DEFAULT_ONLY);
114
115 // Verify that the result is an app and not just the resolver dialog asking which
116 // app to use.
117 if (wouldLaunchResolverActivity(resolved, appList)) {
118 // If only one of the results is a system app then choose that as the default.
119 final ResolveInfo systemApp = getSingleSystemActivity(appList);
120 if (systemApp == null) {
121 // There is no logical choice for this meta-favorite, so rather than making
122 // a bad choice just add nothing.
123 Log.w(TAG, "No preference or single system activity found for "
124 + metaIntent.toString());
125 return -1;
126 }
127 resolved = systemApp;
128 }
129 final ActivityInfo info = resolved.activityInfo;
130 final Intent intent = mPackageManager.getLaunchIntentForPackage(info.packageName);
131 if (intent == null) {
132 return -1;
133 }
134 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
135 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
136
137 return addShortcut(info.loadLabel(mPackageManager).toString(), intent,
138 Favorites.ITEM_TYPE_APPLICATION);
139 }
140
141 private ResolveInfo getSingleSystemActivity(List<ResolveInfo> appList) {
142 ResolveInfo systemResolve = null;
143 final int N = appList.size();
144 for (int i = 0; i < N; ++i) {
145 try {
146 ApplicationInfo info = mPackageManager.getApplicationInfo(
147 appList.get(i).activityInfo.packageName, 0);
148 if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
149 if (systemResolve != null) {
150 return null;
151 } else {
152 systemResolve = appList.get(i);
153 }
154 }
155 } catch (PackageManager.NameNotFoundException e) {
156 Log.w(TAG, "Unable to get info about resolve results", e);
157 return null;
158 }
159 }
160 return systemResolve;
161 }
162
163 private boolean wouldLaunchResolverActivity(ResolveInfo resolved,
164 List<ResolveInfo> appList) {
165 // If the list contains the above resolved activity, then it can't be
166 // ResolverActivity itself.
167 for (int i = 0; i < appList.size(); ++i) {
168 ResolveInfo tmp = appList.get(i);
169 if (tmp.activityInfo.name.equals(resolved.activityInfo.name)
170 && tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) {
171 return false;
172 }
173 }
174 return true;
175 }
176 }
177
178
179 /**
180 * Shortcut parser which allows any uri and not just web urls.
181 */
182 private class UriShortcutParser extends ShortcutParser {
183
184 public UriShortcutParser(Resources iconRes) {
185 super(iconRes);
186 }
187
188 @Override
189 protected Intent parseIntent(XmlResourceParser parser) {
190 String uri = null;
191 try {
192 uri = getAttributeValue(parser, ATTR_URI);
193 return Intent.parseUri(uri, 0);
194 } catch (URISyntaxException e) {
195 Log.w(TAG, "Shortcut has malformed uri: " + uri);
196 return null; // Oh well
197 }
198 }
199 }
200
201 /**
202 * Contains a list of <favorite> nodes, and accepts the first successfully parsed node.
203 */
Sunny Goyalbb3b02f2015-01-15 12:00:14 -0800204 protected class ResolveParser implements TagParser {
Sunny Goyal3a5a9d12014-10-01 15:33:41 -0700205
206 private final AppShortcutWithUriParser mChildParser = new AppShortcutWithUriParser();
207
208 @Override
209 public long parseAndAdd(XmlResourceParser parser) throws XmlPullParserException,
210 IOException {
211 final int groupDepth = parser.getDepth();
212 int type;
213 long addedId = -1;
214 while ((type = parser.next()) != XmlPullParser.END_TAG ||
215 parser.getDepth() > groupDepth) {
216 if (type != XmlPullParser.START_TAG || addedId > -1) {
217 continue;
218 }
219 final String fallback_item_name = parser.getName();
220 if (TAG_FAVORITE.equals(fallback_item_name)) {
221 addedId = mChildParser.parseAndAdd(parser);
222 } else {
223 Log.e(TAG, "Fallback groups can contain only favorites, found "
224 + fallback_item_name);
225 }
226 }
227 return addedId;
228 }
229 }
230
231 /**
232 * A parser which adds a folder whose contents come from partner apk.
233 */
Adam Cohen091440a2015-03-18 14:16:05 -0700234 @Thunk class PartnerFolderParser implements TagParser {
Sunny Goyal3a5a9d12014-10-01 15:33:41 -0700235
236 @Override
237 public long parseAndAdd(XmlResourceParser parser) throws XmlPullParserException,
238 IOException {
239 // Folder contents come from an external XML resource
240 final Partner partner = Partner.get(mPackageManager);
241 if (partner != null) {
242 final Resources partnerRes = partner.getResources();
243 final int resId = partnerRes.getIdentifier(Partner.RES_FOLDER,
244 "xml", partner.getPackageName());
245 if (resId != 0) {
246 final XmlResourceParser partnerParser = partnerRes.getXml(resId);
247 beginDocument(partnerParser, TAG_FOLDER);
248
249 FolderParser folderParser = new FolderParser(getFolderElementsMap(partnerRes));
250 return folderParser.parseAndAdd(partnerParser);
251 }
252 }
253 return -1;
254 }
255 }
256
257 /**
258 * An extension of FolderParser which allows adding items from a different xml.
259 */
Adam Cohen091440a2015-03-18 14:16:05 -0700260 @Thunk class MyFolderParser extends FolderParser {
Sunny Goyal3a5a9d12014-10-01 15:33:41 -0700261
262 @Override
263 public long parseAndAdd(XmlResourceParser parser) throws XmlPullParserException,
264 IOException {
265 final int resId = getAttributeResourceValue(parser, ATTR_FOLDER_ITEMS, 0);
266 if (resId != 0) {
267 parser = mSourceRes.getXml(resId);
268 beginDocument(parser, TAG_FOLDER);
269 }
270 return super.parseAndAdd(parser);
271 }
272 }
273}