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 getComponentInfo()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 */ loadLabel(PackageManager pm)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 */ loadIcon(PackageManager pm)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 */ getIconResource()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 dump(Printer pw, String prefix)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 ResolveInfo()267 public ResolveInfo() { 268 targetUserId = UserHandle.USER_CURRENT; 269 } 270 ResolveInfo(ResolveInfo orig)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 toString()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 describeContents()313 public int describeContents() { 314 return 0; 315 } 316 writeToParcel(Parcel dest, int parcelableFlags)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 ResolveInfo(Parcel source)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> { DisplayNameComparator(PackageManager pm)396 public DisplayNameComparator(PackageManager pm) { 397 mPM = pm; 398 mCollator.setStrength(Collator.PRIMARY); 399 } 400 compare(ResolveInfo a, ResolveInfo b)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