Home | History | Annotate | Download | only in pm
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.content.pm;
     18 
     19 import android.content.ComponentName;
     20 import android.content.IntentFilter;
     21 import android.graphics.drawable.Drawable;
     22 import android.os.Parcel;
     23 import android.os.Parcelable;
     24 import android.os.UserHandle;
     25 import android.text.TextUtils;
     26 import android.util.Printer;
     27 import android.util.Slog;
     28 
     29 import java.text.Collator;
     30 import java.util.Comparator;
     31 
     32 /**
     33  * Information that is returned from resolving an intent
     34  * against an IntentFilter. This partially corresponds to
     35  * information collected from the AndroidManifest.xml's
     36  * <intent> tags.
     37  */
     38 public class ResolveInfo implements Parcelable {
     39     private static final String TAG = "ResolveInfo";
     40 
     41     /**
     42      * The activity or broadcast receiver that corresponds to this resolution
     43      * match, if this resolution is for an activity or broadcast receiver.
     44      * Exactly one of {@link #activityInfo}, {@link #serviceInfo}, or
     45      * {@link #providerInfo} will be non-null.
     46      */
     47     public ActivityInfo activityInfo;
     48 
     49     /**
     50      * The service that corresponds to this resolution match, if this resolution
     51      * is for a service. Exactly one of {@link #activityInfo},
     52      * {@link #serviceInfo}, or {@link #providerInfo} will be non-null.
     53      */
     54     public ServiceInfo serviceInfo;
     55 
     56     /**
     57      * The provider that corresponds to this resolution match, if this
     58      * resolution is for a provider. Exactly one of {@link #activityInfo},
     59      * {@link #serviceInfo}, or {@link #providerInfo} will be non-null.
     60      */
     61     public ProviderInfo providerInfo;
     62 
     63     /**
     64      * The IntentFilter that was matched for this ResolveInfo.
     65      */
     66     public IntentFilter filter;
     67 
     68     /**
     69      * The declared priority of this match.  Comes from the "priority"
     70      * attribute or, if not set, defaults to 0.  Higher values are a higher
     71      * priority.
     72      */
     73     public int priority;
     74 
     75     /**
     76      * Order of result according to the user's preference.  If the user
     77      * has not set a preference for this result, the value is 0; higher
     78      * values are a higher priority.
     79      */
     80     public int preferredOrder;
     81 
     82     /**
     83      * The system's evaluation of how well the activity matches the
     84      * IntentFilter.  This is a match constant, a combination of
     85      * {@link IntentFilter#MATCH_CATEGORY_MASK IntentFilter.MATCH_CATEGORY_MASK}
     86      * and {@link IntentFilter#MATCH_ADJUSTMENT_MASK IntentFiler.MATCH_ADJUSTMENT_MASK}.
     87      */
     88     public int match;
     89 
     90     /**
     91      * Only set when returned by
     92      * {@link PackageManager#queryIntentActivityOptions}, this tells you
     93      * which of the given specific intents this result came from.  0 is the
     94      * first in the list, < 0 means it came from the generic Intent query.
     95      */
     96     public int specificIndex = -1;
     97 
     98     /**
     99      * This filter has specified the Intent.CATEGORY_DEFAULT, meaning it
    100      * would like to be considered a default action that the user can
    101      * perform on this data.
    102      */
    103     public boolean isDefault;
    104 
    105     /**
    106      * A string resource identifier (in the package's resources) of this
    107      * match's label.  From the "label" attribute or, if not set, 0.
    108      */
    109     public int labelRes;
    110 
    111     /**
    112      * The actual string retrieve from <var>labelRes</var> or null if none
    113      * was provided.
    114      */
    115     public CharSequence nonLocalizedLabel;
    116 
    117     /**
    118      * A drawable resource identifier (in the package's resources) of this
    119      * match's icon.  From the "icon" attribute or, if not set, 0.
    120      */
    121     public int icon;
    122 
    123     /**
    124      * Optional -- if non-null, the {@link #labelRes} and {@link #icon}
    125      * resources will be loaded from this package, rather than the one
    126      * containing the resolved component.
    127      */
    128     public String resolvePackageName;
    129 
    130     /**
    131      * If not equal to UserHandle.USER_CURRENT, then the intent will be forwarded to this user.
    132      * @hide
    133      */
    134     public int targetUserId;
    135 
    136     /**
    137      * @hide
    138      */
    139     public boolean noResourceId;
    140 
    141     /**
    142      * @hide Target comes from system process?
    143      */
    144     public boolean system;
    145 
    146     private ComponentInfo getComponentInfo() {
    147         if (activityInfo != null) return activityInfo;
    148         if (serviceInfo != null) return serviceInfo;
    149         if (providerInfo != null) return providerInfo;
    150         throw new IllegalStateException("Missing ComponentInfo!");
    151     }
    152 
    153     /**
    154      * Retrieve the current textual label associated with this resolution.  This
    155      * will call back on the given PackageManager to load the label from
    156      * the application.
    157      *
    158      * @param pm A PackageManager from which the label can be loaded; usually
    159      * the PackageManager from which you originally retrieved this item.
    160      *
    161      * @return Returns a CharSequence containing the resolutions's label.  If the
    162      * item does not have a label, its name is returned.
    163      */
    164     public CharSequence loadLabel(PackageManager pm) {
    165         if (nonLocalizedLabel != null) {
    166             return nonLocalizedLabel;
    167         }
    168         CharSequence label;
    169         if (resolvePackageName != null && labelRes != 0) {
    170             label = pm.getText(resolvePackageName, labelRes, null);
    171             if (label != null) {
    172                 return label.toString().trim();
    173             }
    174         }
    175         ComponentInfo ci = getComponentInfo();
    176         ApplicationInfo ai = ci.applicationInfo;
    177         if (labelRes != 0) {
    178             label = pm.getText(ci.packageName, labelRes, ai);
    179             if (label != null) {
    180                 return label.toString().trim();
    181             }
    182         }
    183 
    184         CharSequence data = ci.loadLabel(pm);
    185         // Make the data safe
    186         if (data != null) data = data.toString().trim();
    187         return data;
    188     }
    189 
    190     /**
    191      * Retrieve the current graphical icon associated with this resolution.  This
    192      * will call back on the given PackageManager to load the icon from
    193      * the application.
    194      *
    195      * @param pm A PackageManager from which the icon can be loaded; usually
    196      * the PackageManager from which you originally retrieved this item.
    197      *
    198      * @return Returns a Drawable containing the resolution's icon.  If the
    199      * item does not have an icon, the default activity icon is returned.
    200      */
    201     public Drawable loadIcon(PackageManager pm) {
    202         Drawable dr;
    203         if (resolvePackageName != null && icon != 0) {
    204             dr = pm.getDrawable(resolvePackageName, icon, null);
    205             if (dr != null) {
    206                 return dr;
    207             }
    208         }
    209         ComponentInfo ci = getComponentInfo();
    210         ApplicationInfo ai = ci.applicationInfo;
    211         if (icon != 0) {
    212             dr = pm.getDrawable(ci.packageName, icon, ai);
    213             if (dr != null) {
    214                 return dr;
    215             }
    216         }
    217         return ci.loadIcon(pm);
    218     }
    219 
    220     /**
    221      * Return the icon resource identifier to use for this match.  If the
    222      * match defines an icon, that is used; else if the activity defines
    223      * an icon, that is used; else, the application icon is used.
    224      *
    225      * @return The icon associated with this match.
    226      */
    227     public final int getIconResource() {
    228         if (noResourceId) return 0;
    229         if (icon != 0) return icon;
    230         final ComponentInfo ci = getComponentInfo();
    231         if (ci != null) {
    232             return ci.getIconResource();
    233         }
    234         return 0;
    235     }
    236 
    237     public void dump(Printer pw, String prefix) {
    238         if (filter != null) {
    239             pw.println(prefix + "Filter:");
    240             filter.dump(pw, prefix + "  ");
    241         }
    242         pw.println(prefix + "priority=" + priority
    243                 + " preferredOrder=" + preferredOrder
    244                 + " match=0x" + Integer.toHexString(match)
    245                 + " specificIndex=" + specificIndex
    246                 + " isDefault=" + isDefault);
    247         if (resolvePackageName != null) {
    248             pw.println(prefix + "resolvePackageName=" + resolvePackageName);
    249         }
    250         if (labelRes != 0 || nonLocalizedLabel != null || icon != 0) {
    251             pw.println(prefix + "labelRes=0x" + Integer.toHexString(labelRes)
    252                     + " nonLocalizedLabel=" + nonLocalizedLabel
    253                     + " icon=0x" + Integer.toHexString(icon));
    254         }
    255         if (activityInfo != null) {
    256             pw.println(prefix + "ActivityInfo:");
    257             activityInfo.dump(pw, prefix + "  ");
    258         } else if (serviceInfo != null) {
    259             pw.println(prefix + "ServiceInfo:");
    260             serviceInfo.dump(pw, prefix + "  ");
    261         } else if (providerInfo != null) {
    262             pw.println(prefix + "ProviderInfo:");
    263             providerInfo.dump(pw, prefix + "  ");
    264         }
    265     }
    266 
    267     public ResolveInfo() {
    268         targetUserId = UserHandle.USER_CURRENT;
    269     }
    270 
    271     public ResolveInfo(ResolveInfo orig) {
    272         activityInfo = orig.activityInfo;
    273         serviceInfo = orig.serviceInfo;
    274         providerInfo = orig.providerInfo;
    275         filter = orig.filter;
    276         priority = orig.priority;
    277         preferredOrder = orig.preferredOrder;
    278         match = orig.match;
    279         specificIndex = orig.specificIndex;
    280         labelRes = orig.labelRes;
    281         nonLocalizedLabel = orig.nonLocalizedLabel;
    282         icon = orig.icon;
    283         resolvePackageName = orig.resolvePackageName;
    284         system = orig.system;
    285         targetUserId = orig.targetUserId;
    286     }
    287 
    288     public String toString() {
    289         final ComponentInfo ci = getComponentInfo();
    290         StringBuilder sb = new StringBuilder(128);
    291         sb.append("ResolveInfo{");
    292         sb.append(Integer.toHexString(System.identityHashCode(this)));
    293         sb.append(' ');
    294         ComponentName.appendShortString(sb, ci.packageName, ci.name);
    295         if (priority != 0) {
    296             sb.append(" p=");
    297             sb.append(priority);
    298         }
    299         if (preferredOrder != 0) {
    300             sb.append(" o=");
    301             sb.append(preferredOrder);
    302         }
    303         sb.append(" m=0x");
    304         sb.append(Integer.toHexString(match));
    305         if (targetUserId != UserHandle.USER_CURRENT) {
    306             sb.append(" targetUserId=");
    307             sb.append(targetUserId);
    308         }
    309         sb.append('}');
    310         return sb.toString();
    311     }
    312 
    313     public int describeContents() {
    314         return 0;
    315     }
    316 
    317     public void writeToParcel(Parcel dest, int parcelableFlags) {
    318         if (activityInfo != null) {
    319             dest.writeInt(1);
    320             activityInfo.writeToParcel(dest, parcelableFlags);
    321         } else if (serviceInfo != null) {
    322             dest.writeInt(2);
    323             serviceInfo.writeToParcel(dest, parcelableFlags);
    324         } else if (providerInfo != null) {
    325             dest.writeInt(3);
    326             providerInfo.writeToParcel(dest, parcelableFlags);
    327         } else {
    328             dest.writeInt(0);
    329         }
    330         if (filter != null) {
    331             dest.writeInt(1);
    332             filter.writeToParcel(dest, parcelableFlags);
    333         } else {
    334             dest.writeInt(0);
    335         }
    336         dest.writeInt(priority);
    337         dest.writeInt(preferredOrder);
    338         dest.writeInt(match);
    339         dest.writeInt(specificIndex);
    340         dest.writeInt(labelRes);
    341         TextUtils.writeToParcel(nonLocalizedLabel, dest, parcelableFlags);
    342         dest.writeInt(icon);
    343         dest.writeString(resolvePackageName);
    344         dest.writeInt(targetUserId);
    345         dest.writeInt(system ? 1 : 0);
    346         dest.writeInt(noResourceId ? 1 : 0);
    347     }
    348 
    349     public static final Creator<ResolveInfo> CREATOR
    350             = new Creator<ResolveInfo>() {
    351         public ResolveInfo createFromParcel(Parcel source) {
    352             return new ResolveInfo(source);
    353         }
    354         public ResolveInfo[] newArray(int size) {
    355             return new ResolveInfo[size];
    356         }
    357     };
    358 
    359     private ResolveInfo(Parcel source) {
    360         activityInfo = null;
    361         serviceInfo = null;
    362         providerInfo = null;
    363         switch (source.readInt()) {
    364             case 1:
    365                 activityInfo = ActivityInfo.CREATOR.createFromParcel(source);
    366                 break;
    367             case 2:
    368                 serviceInfo = ServiceInfo.CREATOR.createFromParcel(source);
    369                 break;
    370             case 3:
    371                 providerInfo = ProviderInfo.CREATOR.createFromParcel(source);
    372                 break;
    373             default:
    374                 Slog.w(TAG, "Missing ComponentInfo!");
    375                 break;
    376         }
    377         if (source.readInt() != 0) {
    378             filter = IntentFilter.CREATOR.createFromParcel(source);
    379         }
    380         priority = source.readInt();
    381         preferredOrder = source.readInt();
    382         match = source.readInt();
    383         specificIndex = source.readInt();
    384         labelRes = source.readInt();
    385         nonLocalizedLabel
    386                 = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
    387         icon = source.readInt();
    388         resolvePackageName = source.readString();
    389         targetUserId = source.readInt();
    390         system = source.readInt() != 0;
    391         noResourceId = source.readInt() != 0;
    392     }
    393 
    394     public static class DisplayNameComparator
    395             implements Comparator<ResolveInfo> {
    396         public DisplayNameComparator(PackageManager pm) {
    397             mPM = pm;
    398             mCollator.setStrength(Collator.PRIMARY);
    399         }
    400 
    401         public final int compare(ResolveInfo a, ResolveInfo b) {
    402             // We want to put the one targeted to another user at the end of the dialog.
    403             if (a.targetUserId != UserHandle.USER_CURRENT) {
    404                 return 1;
    405             }
    406             if (b.targetUserId != UserHandle.USER_CURRENT) {
    407                 return -1;
    408             }
    409             CharSequence  sa = a.loadLabel(mPM);
    410             if (sa == null) sa = a.activityInfo.name;
    411             CharSequence  sb = b.loadLabel(mPM);
    412             if (sb == null) sb = b.activityInfo.name;
    413 
    414             return mCollator.compare(sa.toString(), sb.toString());
    415         }
    416 
    417         private final Collator   mCollator = Collator.getInstance();
    418         private PackageManager   mPM;
    419     }
    420 }
    421