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