1 package com.android.launcher3; 2 3 import android.appwidget.AppWidgetHost; 4 import android.content.Context; 5 import android.content.Intent; 6 import android.content.pm.ActivityInfo; 7 import android.content.pm.ApplicationInfo; 8 import android.content.pm.PackageManager; 9 import android.content.pm.ResolveInfo; 10 import android.content.res.Resources; 11 import android.content.res.XmlResourceParser; 12 import android.text.TextUtils; 13 import android.util.Log; 14 15 import com.android.launcher3.LauncherSettings.Favorites; 16 import com.android.launcher3.util.Thunk; 17 18 import org.xmlpull.v1.XmlPullParser; 19 import org.xmlpull.v1.XmlPullParserException; 20 21 import java.io.IOException; 22 import java.net.URISyntaxException; 23 import java.util.HashMap; 24 import java.util.List; 25 26 /** 27 * Implements the layout parser with rules for internal layouts and partner layouts. 28 */ 29 public class DefaultLayoutParser extends AutoInstallsLayout { 30 private static final String TAG = "DefaultLayoutParser"; 31 32 protected static final String TAG_RESOLVE = "resolve"; 33 private static final String TAG_FAVORITES = "favorites"; 34 protected static final String TAG_FAVORITE = "favorite"; 35 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 40 protected static final String ATTR_URI = "uri"; 41 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 DefaultLayoutParser(Context context, AppWidgetHost appWidgetHost, LayoutParserCallback callback, Resources sourceRes, int layoutId)45 public DefaultLayoutParser(Context context, AppWidgetHost appWidgetHost, 46 LayoutParserCallback callback, Resources sourceRes, int layoutId) { 47 super(context, appWidgetHost, callback, sourceRes, layoutId, TAG_FAVORITES); 48 } 49 DefaultLayoutParser(Context context, AppWidgetHost appWidgetHost, LayoutParserCallback callback, Resources sourceRes, int layoutId, String rootTag)50 public DefaultLayoutParser(Context context, AppWidgetHost appWidgetHost, 51 LayoutParserCallback callback, Resources sourceRes, int layoutId, String rootTag) { 52 super(context, appWidgetHost, callback, sourceRes, layoutId, rootTag); 53 } 54 55 @Override getFolderElementsMap()56 protected HashMap<String, TagParser> getFolderElementsMap() { 57 return getFolderElementsMap(mSourceRes); 58 } 59 getFolderElementsMap(Resources res)60 @Thunk HashMap<String, TagParser> getFolderElementsMap(Resources res) { 61 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 getLayoutElementsMap()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 parseContainerAndScreen(XmlResourceParser parser, long[] out)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 89 /** 90 * AppShortcutParser which also supports adding URI based intents 91 */ 92 @Thunk class AppShortcutWithUriParser extends AppShortcutParser { 93 94 @Override invalidPackageOrClass(XmlResourceParser parser)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 getSingleSystemActivity(List<ResolveInfo> appList)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 wouldLaunchResolverActivity(ResolveInfo resolved, List<ResolveInfo> appList)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 UriShortcutParser(Resources iconRes)184 public UriShortcutParser(Resources iconRes) { 185 super(iconRes); 186 } 187 188 @Override parseIntent(XmlResourceParser parser)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 */ 204 protected class ResolveParser implements TagParser { 205 206 private final AppShortcutWithUriParser mChildParser = new AppShortcutWithUriParser(); 207 208 @Override parseAndAdd(XmlResourceParser parser)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 */ 234 @Thunk class PartnerFolderParser implements TagParser { 235 236 @Override parseAndAdd(XmlResourceParser parser)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 */ 260 @Thunk class MyFolderParser extends FolderParser { 261 262 @Override parseAndAdd(XmlResourceParser parser)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 } 274