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 17package android.content.pm; 18 19import android.content.ComponentName; 20import android.content.IntentFilter; 21import android.graphics.drawable.Drawable; 22import android.os.Parcel; 23import android.os.Parcelable; 24import android.os.UserHandle; 25import android.text.TextUtils; 26import android.util.Printer; 27import android.util.Slog; 28 29import java.text.Collator; 30import 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 */ 38public 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. It is 120 * set only if the icon can be obtained by resource id alone. 121 */ 122 public int icon; 123 124 /** 125 * Optional -- if non-null, the {@link #labelRes} and {@link #icon} 126 * resources will be loaded from this package, rather than the one 127 * containing the resolved component. 128 */ 129 public String resolvePackageName; 130 131 /** 132 * If not equal to UserHandle.USER_CURRENT, then the intent will be forwarded to this user. 133 * @hide 134 */ 135 public int targetUserId; 136 137 /** 138 * Set to true if the icon cannot be obtained by resource ids alone. 139 * It is set to true for ResolveInfos from the managed profile: They need to 140 * have their icon badged, so it cannot be obtained by resource ids alone. 141 * @hide 142 */ 143 public boolean noResourceId; 144 145 /** 146 * Same as {@link #icon} but it will always correspond to "icon" attribute 147 * regardless of {@link #noResourceId} value. 148 * @hide 149 */ 150 public int iconResourceId; 151 152 /** 153 * @hide Target comes from system process? 154 */ 155 public boolean system; 156 157 /** 158 * @hide Does the associated IntentFilter comes from a Browser ? 159 */ 160 public boolean handleAllWebDataURI; 161 162 private ComponentInfo getComponentInfo() { 163 if (activityInfo != null) return activityInfo; 164 if (serviceInfo != null) return serviceInfo; 165 if (providerInfo != null) return providerInfo; 166 throw new IllegalStateException("Missing ComponentInfo!"); 167 } 168 169 /** 170 * Retrieve the current textual label associated with this resolution. This 171 * will call back on the given PackageManager to load the label from 172 * the application. 173 * 174 * @param pm A PackageManager from which the label can be loaded; usually 175 * the PackageManager from which you originally retrieved this item. 176 * 177 * @return Returns a CharSequence containing the resolutions's label. If the 178 * item does not have a label, its name is returned. 179 */ 180 public CharSequence loadLabel(PackageManager pm) { 181 if (nonLocalizedLabel != null) { 182 return nonLocalizedLabel; 183 } 184 CharSequence label; 185 if (resolvePackageName != null && labelRes != 0) { 186 label = pm.getText(resolvePackageName, labelRes, null); 187 if (label != null) { 188 return label.toString().trim(); 189 } 190 } 191 ComponentInfo ci = getComponentInfo(); 192 ApplicationInfo ai = ci.applicationInfo; 193 if (labelRes != 0) { 194 label = pm.getText(ci.packageName, labelRes, ai); 195 if (label != null) { 196 return label.toString().trim(); 197 } 198 } 199 200 CharSequence data = ci.loadLabel(pm); 201 // Make the data safe 202 if (data != null) data = data.toString().trim(); 203 return data; 204 } 205 206 /** 207 * Retrieve the current graphical icon associated with this resolution. This 208 * will call back on the given PackageManager to load the icon from 209 * the application. 210 * 211 * @param pm A PackageManager from which the icon can be loaded; usually 212 * the PackageManager from which you originally retrieved this item. 213 * 214 * @return Returns a Drawable containing the resolution's icon. If the 215 * item does not have an icon, the default activity icon is returned. 216 */ 217 public Drawable loadIcon(PackageManager pm) { 218 Drawable dr = null; 219 if (resolvePackageName != null && iconResourceId != 0) { 220 dr = pm.getDrawable(resolvePackageName, iconResourceId, null); 221 } 222 ComponentInfo ci = getComponentInfo(); 223 if (dr == null && iconResourceId != 0) { 224 ApplicationInfo ai = ci.applicationInfo; 225 dr = pm.getDrawable(ci.packageName, iconResourceId, ai); 226 } 227 if (dr != null) { 228 return pm.getUserBadgedIcon(dr, new UserHandle(UserHandle.myUserId())); 229 } 230 return ci.loadIcon(pm); 231 } 232 233 /** 234 * Return the icon resource identifier to use for this match. If the 235 * match defines an icon, that is used; else if the activity defines 236 * an icon, that is used; else, the application icon is used. 237 * This function does not check noResourceId flag. 238 * 239 * @return The icon associated with this match. 240 */ 241 final int getIconResourceInternal() { 242 if (iconResourceId != 0) return iconResourceId; 243 final ComponentInfo ci = getComponentInfo(); 244 if (ci != null) { 245 return ci.getIconResource(); 246 } 247 return 0; 248 } 249 250 /** 251 * Return the icon resource identifier to use for this match. If the 252 * match defines an icon, that is used; else if the activity defines 253 * an icon, that is used; else, the application icon is used. 254 * 255 * @return The icon associated with this match. 256 */ 257 public final int getIconResource() { 258 if (noResourceId) return 0; 259 return getIconResourceInternal(); 260 } 261 262 public void dump(Printer pw, String prefix) { 263 if (filter != null) { 264 pw.println(prefix + "Filter:"); 265 filter.dump(pw, prefix + " "); 266 } 267 pw.println(prefix + "priority=" + priority 268 + " preferredOrder=" + preferredOrder 269 + " match=0x" + Integer.toHexString(match) 270 + " specificIndex=" + specificIndex 271 + " isDefault=" + isDefault); 272 if (resolvePackageName != null) { 273 pw.println(prefix + "resolvePackageName=" + resolvePackageName); 274 } 275 if (labelRes != 0 || nonLocalizedLabel != null || icon != 0) { 276 pw.println(prefix + "labelRes=0x" + Integer.toHexString(labelRes) 277 + " nonLocalizedLabel=" + nonLocalizedLabel 278 + " icon=0x" + Integer.toHexString(icon)); 279 } 280 if (activityInfo != null) { 281 pw.println(prefix + "ActivityInfo:"); 282 activityInfo.dump(pw, prefix + " "); 283 } else if (serviceInfo != null) { 284 pw.println(prefix + "ServiceInfo:"); 285 serviceInfo.dump(pw, prefix + " "); 286 } else if (providerInfo != null) { 287 pw.println(prefix + "ProviderInfo:"); 288 providerInfo.dump(pw, prefix + " "); 289 } 290 } 291 292 public ResolveInfo() { 293 targetUserId = UserHandle.USER_CURRENT; 294 } 295 296 public ResolveInfo(ResolveInfo orig) { 297 activityInfo = orig.activityInfo; 298 serviceInfo = orig.serviceInfo; 299 providerInfo = orig.providerInfo; 300 filter = orig.filter; 301 priority = orig.priority; 302 preferredOrder = orig.preferredOrder; 303 match = orig.match; 304 specificIndex = orig.specificIndex; 305 labelRes = orig.labelRes; 306 nonLocalizedLabel = orig.nonLocalizedLabel; 307 icon = orig.icon; 308 resolvePackageName = orig.resolvePackageName; 309 noResourceId = orig.noResourceId; 310 iconResourceId = orig.iconResourceId; 311 system = orig.system; 312 targetUserId = orig.targetUserId; 313 handleAllWebDataURI = orig.handleAllWebDataURI; 314 } 315 316 public String toString() { 317 final ComponentInfo ci = getComponentInfo(); 318 StringBuilder sb = new StringBuilder(128); 319 sb.append("ResolveInfo{"); 320 sb.append(Integer.toHexString(System.identityHashCode(this))); 321 sb.append(' '); 322 ComponentName.appendShortString(sb, ci.packageName, ci.name); 323 if (priority != 0) { 324 sb.append(" p="); 325 sb.append(priority); 326 } 327 if (preferredOrder != 0) { 328 sb.append(" o="); 329 sb.append(preferredOrder); 330 } 331 sb.append(" m=0x"); 332 sb.append(Integer.toHexString(match)); 333 if (targetUserId != UserHandle.USER_CURRENT) { 334 sb.append(" targetUserId="); 335 sb.append(targetUserId); 336 } 337 sb.append('}'); 338 return sb.toString(); 339 } 340 341 public int describeContents() { 342 return 0; 343 } 344 345 public void writeToParcel(Parcel dest, int parcelableFlags) { 346 if (activityInfo != null) { 347 dest.writeInt(1); 348 activityInfo.writeToParcel(dest, parcelableFlags); 349 } else if (serviceInfo != null) { 350 dest.writeInt(2); 351 serviceInfo.writeToParcel(dest, parcelableFlags); 352 } else if (providerInfo != null) { 353 dest.writeInt(3); 354 providerInfo.writeToParcel(dest, parcelableFlags); 355 } else { 356 dest.writeInt(0); 357 } 358 if (filter != null) { 359 dest.writeInt(1); 360 filter.writeToParcel(dest, parcelableFlags); 361 } else { 362 dest.writeInt(0); 363 } 364 dest.writeInt(priority); 365 dest.writeInt(preferredOrder); 366 dest.writeInt(match); 367 dest.writeInt(specificIndex); 368 dest.writeInt(labelRes); 369 TextUtils.writeToParcel(nonLocalizedLabel, dest, parcelableFlags); 370 dest.writeInt(icon); 371 dest.writeString(resolvePackageName); 372 dest.writeInt(targetUserId); 373 dest.writeInt(system ? 1 : 0); 374 dest.writeInt(noResourceId ? 1 : 0); 375 dest.writeInt(iconResourceId); 376 dest.writeInt(handleAllWebDataURI ? 1 : 0); 377 } 378 379 public static final Creator<ResolveInfo> CREATOR 380 = new Creator<ResolveInfo>() { 381 public ResolveInfo createFromParcel(Parcel source) { 382 return new ResolveInfo(source); 383 } 384 public ResolveInfo[] newArray(int size) { 385 return new ResolveInfo[size]; 386 } 387 }; 388 389 private ResolveInfo(Parcel source) { 390 activityInfo = null; 391 serviceInfo = null; 392 providerInfo = null; 393 switch (source.readInt()) { 394 case 1: 395 activityInfo = ActivityInfo.CREATOR.createFromParcel(source); 396 break; 397 case 2: 398 serviceInfo = ServiceInfo.CREATOR.createFromParcel(source); 399 break; 400 case 3: 401 providerInfo = ProviderInfo.CREATOR.createFromParcel(source); 402 break; 403 default: 404 Slog.w(TAG, "Missing ComponentInfo!"); 405 break; 406 } 407 if (source.readInt() != 0) { 408 filter = IntentFilter.CREATOR.createFromParcel(source); 409 } 410 priority = source.readInt(); 411 preferredOrder = source.readInt(); 412 match = source.readInt(); 413 specificIndex = source.readInt(); 414 labelRes = source.readInt(); 415 nonLocalizedLabel 416 = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); 417 icon = source.readInt(); 418 resolvePackageName = source.readString(); 419 targetUserId = source.readInt(); 420 system = source.readInt() != 0; 421 noResourceId = source.readInt() != 0; 422 iconResourceId = source.readInt(); 423 handleAllWebDataURI = source.readInt() != 0; 424 } 425 426 public static class DisplayNameComparator 427 implements Comparator<ResolveInfo> { 428 public DisplayNameComparator(PackageManager pm) { 429 mPM = pm; 430 mCollator.setStrength(Collator.PRIMARY); 431 } 432 433 public final int compare(ResolveInfo a, ResolveInfo b) { 434 // We want to put the one targeted to another user at the end of the dialog. 435 if (a.targetUserId != UserHandle.USER_CURRENT) { 436 return 1; 437 } 438 if (b.targetUserId != UserHandle.USER_CURRENT) { 439 return -1; 440 } 441 CharSequence sa = a.loadLabel(mPM); 442 if (sa == null) sa = a.activityInfo.name; 443 CharSequence sb = b.loadLabel(mPM); 444 if (sb == null) sb = b.activityInfo.name; 445 446 return mCollator.compare(sa.toString(), sb.toString()); 447 } 448 449 private final Collator mCollator = Collator.getInstance(); 450 private PackageManager mPM; 451 } 452} 453