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 static android.text.TextUtils.SAFE_STRING_FLAG_FIRST_LINE; 20 import static android.text.TextUtils.SAFE_STRING_FLAG_SINGLE_LINE; 21 import static android.text.TextUtils.SAFE_STRING_FLAG_TRIM; 22 import static android.text.TextUtils.makeSafeForPresentation; 23 24 import android.annotation.FloatRange; 25 import android.annotation.NonNull; 26 import android.annotation.SystemApi; 27 import android.app.ActivityThread; 28 import android.content.res.XmlResourceParser; 29 import android.graphics.drawable.Drawable; 30 import android.os.Bundle; 31 import android.os.Parcel; 32 import android.os.UserHandle; 33 import android.text.TextUtils; 34 import android.util.Printer; 35 import android.util.proto.ProtoOutputStream; 36 37 38 import java.text.Collator; 39 import java.util.Comparator; 40 import java.util.Objects; 41 42 /** 43 * Base class containing information common to all package items held by 44 * the package manager. This provides a very common basic set of attributes: 45 * a label, icon, and meta-data. This class is not intended 46 * to be used by itself; it is simply here to share common definitions 47 * between all items returned by the package manager. As such, it does not 48 * itself implement Parcelable, but does provide convenience methods to assist 49 * in the implementation of Parcelable in subclasses. 50 */ 51 public class PackageItemInfo { 52 53 /** 54 * The maximum length of a safe label, in characters 55 * 56 * TODO(b/157997155): It may make sense to expose this publicly so that apps can check for the 57 * value and truncate the strings/use a different label, without having to hardcode and make 58 * assumptions about the value. 59 * @hide 60 */ 61 public static final int MAX_SAFE_LABEL_LENGTH = 1000; 62 63 /** @hide */ 64 public static final float DEFAULT_MAX_LABEL_SIZE_PX = 1000f; 65 66 /** 67 * Remove {@link Character#isWhitespace(int) whitespace} and non-breaking spaces from the edges 68 * of the label. 69 * 70 * @see #loadSafeLabel(PackageManager, float, int) 71 * 72 * @deprecated Use {@link TextUtils#SAFE_STRING_FLAG_TRIM} instead 73 * @hide 74 * @removed 75 */ 76 @Deprecated 77 @SystemApi 78 public static final int SAFE_LABEL_FLAG_TRIM = SAFE_STRING_FLAG_TRIM; 79 80 /** 81 * Force entire string into single line of text (no newlines). Cannot be set at the same time as 82 * {@link #SAFE_LABEL_FLAG_FIRST_LINE}. 83 * 84 * @see #loadSafeLabel(PackageManager, float, int) 85 * 86 * @deprecated Use {@link TextUtils#SAFE_STRING_FLAG_SINGLE_LINE} instead 87 * @hide 88 * @removed 89 */ 90 @Deprecated 91 @SystemApi 92 public static final int SAFE_LABEL_FLAG_SINGLE_LINE = SAFE_STRING_FLAG_SINGLE_LINE; 93 94 /** 95 * Return only first line of text (truncate at first newline). Cannot be set at the same time as 96 * {@link #SAFE_LABEL_FLAG_SINGLE_LINE}. 97 * 98 * @see #loadSafeLabel(PackageManager, float, int) 99 * 100 * @deprecated Use {@link TextUtils#SAFE_STRING_FLAG_FIRST_LINE} instead 101 * @hide 102 * @removed 103 */ 104 @Deprecated 105 @SystemApi 106 public static final int SAFE_LABEL_FLAG_FIRST_LINE = SAFE_STRING_FLAG_FIRST_LINE; 107 108 private static volatile boolean sForceSafeLabels = false; 109 110 /** 111 * Always use {@link #loadSafeLabel safe labels} when calling {@link #loadLabel}. 112 * 113 * @hide 114 */ 115 @SystemApi forceSafeLabels()116 public static void forceSafeLabels() { 117 sForceSafeLabels = true; 118 } 119 120 /** 121 * Public name of this item. From the "android:name" attribute. 122 */ 123 public String name; 124 125 /** 126 * Name of the package that this item is in. 127 */ 128 public String packageName; 129 130 /** 131 * A string resource identifier (in the package's resources) of this 132 * component's label. From the "label" attribute or, if not set, 0. 133 */ 134 public int labelRes; 135 136 /** 137 * The string provided in the AndroidManifest file, if any. You 138 * probably don't want to use this. You probably want 139 * {@link PackageManager#getApplicationLabel} 140 */ 141 public CharSequence nonLocalizedLabel; 142 143 /** 144 * A drawable resource identifier (in the package's resources) of this 145 * component's icon. From the "icon" attribute or, if not set, 0. 146 */ 147 public int icon; 148 149 /** 150 * A drawable resource identifier (in the package's resources) of this 151 * component's banner. From the "banner" attribute or, if not set, 0. 152 */ 153 public int banner; 154 155 /** 156 * A drawable resource identifier (in the package's resources) of this 157 * component's logo. Logos may be larger/wider than icons and are 158 * displayed by certain UI elements in place of a name or name/icon 159 * combination. From the "logo" attribute or, if not set, 0. 160 */ 161 public int logo; 162 163 /** 164 * Additional meta-data associated with this component. This field 165 * will only be filled in if you set the 166 * {@link PackageManager#GET_META_DATA} flag when requesting the info. 167 */ 168 public Bundle metaData; 169 170 /** 171 * If different of UserHandle.USER_NULL, The icon of this item will represent that user. 172 * @hide 173 */ 174 public int showUserIcon; 175 PackageItemInfo()176 public PackageItemInfo() { 177 showUserIcon = UserHandle.USER_NULL; 178 } 179 PackageItemInfo(PackageItemInfo orig)180 public PackageItemInfo(PackageItemInfo orig) { 181 name = orig.name; 182 if (name != null) name = name.trim(); 183 packageName = orig.packageName; 184 labelRes = orig.labelRes; 185 nonLocalizedLabel = orig.nonLocalizedLabel; 186 if (nonLocalizedLabel != null) nonLocalizedLabel = nonLocalizedLabel.toString().trim(); 187 icon = orig.icon; 188 banner = orig.banner; 189 logo = orig.logo; 190 metaData = orig.metaData; 191 showUserIcon = orig.showUserIcon; 192 } 193 194 /** 195 * Retrieve the current textual label associated with this item. This 196 * will call back on the given PackageManager to load the label from 197 * the application. 198 * 199 * @param pm A PackageManager from which the label can be loaded; usually 200 * the PackageManager from which you originally retrieved this item. 201 * 202 * @return Returns a CharSequence containing the item's label. If the 203 * item does not have a label, its name is returned. 204 */ loadLabel(@onNull PackageManager pm)205 public @NonNull CharSequence loadLabel(@NonNull PackageManager pm) { 206 if (sForceSafeLabels && !Objects.equals(packageName, ActivityThread.currentPackageName())) { 207 return loadSafeLabel(pm, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_STRING_FLAG_TRIM 208 | SAFE_STRING_FLAG_FIRST_LINE); 209 } else { 210 // Trims the label string to the MAX_SAFE_LABEL_LENGTH. This is to prevent that the 211 // system is overwhelmed by an enormous string returned by the application. 212 return TextUtils.trimToSize(loadUnsafeLabel(pm), MAX_SAFE_LABEL_LENGTH); 213 } 214 } 215 216 /** {@hide} */ loadUnsafeLabel(PackageManager pm)217 public CharSequence loadUnsafeLabel(PackageManager pm) { 218 if (nonLocalizedLabel != null) { 219 return nonLocalizedLabel; 220 } 221 if (labelRes != 0) { 222 CharSequence label = pm.getText(packageName, labelRes, getApplicationInfo()); 223 if (label != null) { 224 return label.toString().trim(); 225 } 226 } 227 if (name != null) { 228 return name; 229 } 230 return packageName; 231 } 232 233 /** 234 * @hide 235 * @deprecated use loadSafeLabel(PackageManager, float, int) instead 236 */ 237 @SystemApi 238 @Deprecated loadSafeLabel(@onNull PackageManager pm)239 public @NonNull CharSequence loadSafeLabel(@NonNull PackageManager pm) { 240 return loadSafeLabel(pm, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_STRING_FLAG_TRIM 241 | SAFE_STRING_FLAG_FIRST_LINE); 242 } 243 244 /** 245 * Calls {@link TextUtils#makeSafeForPresentation} for the label of this item. 246 * 247 * <p>For parameters see {@link TextUtils#makeSafeForPresentation}. 248 * 249 * @hide 250 */ 251 @SystemApi loadSafeLabel(@onNull PackageManager pm, @FloatRange(from = 0) float ellipsizeDip, @TextUtils.SafeStringFlags int flags)252 public @NonNull CharSequence loadSafeLabel(@NonNull PackageManager pm, 253 @FloatRange(from = 0) float ellipsizeDip, @TextUtils.SafeStringFlags int flags) { 254 Objects.requireNonNull(pm); 255 256 return makeSafeForPresentation(loadUnsafeLabel(pm).toString(), MAX_SAFE_LABEL_LENGTH, 257 ellipsizeDip, flags); 258 } 259 260 /** 261 * Retrieve the current graphical icon associated with this item. This 262 * will call back on the given PackageManager to load the icon from 263 * the application. 264 * 265 * @param pm A PackageManager from which the icon can be loaded; usually 266 * the PackageManager from which you originally retrieved this item. 267 * 268 * @return Returns a Drawable containing the item's icon. If the 269 * item does not have an icon, the item's default icon is returned 270 * such as the default activity icon. 271 */ loadIcon(PackageManager pm)272 public Drawable loadIcon(PackageManager pm) { 273 return pm.loadItemIcon(this, getApplicationInfo()); 274 } 275 276 /** 277 * Retrieve the current graphical icon associated with this item without 278 * the addition of a work badge if applicable. 279 * This will call back on the given PackageManager to load the icon from 280 * the application. 281 * 282 * @param pm A PackageManager from which the icon can be loaded; usually 283 * the PackageManager from which you originally retrieved this item. 284 * 285 * @return Returns a Drawable containing the item's icon. If the 286 * item does not have an icon, the item's default icon is returned 287 * such as the default activity icon. 288 */ loadUnbadgedIcon(PackageManager pm)289 public Drawable loadUnbadgedIcon(PackageManager pm) { 290 return pm.loadUnbadgedItemIcon(this, getApplicationInfo()); 291 } 292 293 /** 294 * Retrieve the current graphical banner associated with this item. This 295 * will call back on the given PackageManager to load the banner from 296 * the application. 297 * 298 * @param pm A PackageManager from which the banner can be loaded; usually 299 * the PackageManager from which you originally retrieved this item. 300 * 301 * @return Returns a Drawable containing the item's banner. If the item 302 * does not have a banner, this method will return null. 303 */ loadBanner(PackageManager pm)304 public Drawable loadBanner(PackageManager pm) { 305 if (banner != 0) { 306 Drawable dr = pm.getDrawable(packageName, banner, getApplicationInfo()); 307 if (dr != null) { 308 return dr; 309 } 310 } 311 return loadDefaultBanner(pm); 312 } 313 314 /** 315 * Retrieve the default graphical icon associated with this item. 316 * 317 * @param pm A PackageManager from which the icon can be loaded; usually 318 * the PackageManager from which you originally retrieved this item. 319 * 320 * @return Returns a Drawable containing the item's default icon 321 * such as the default activity icon. 322 * 323 * @hide 324 */ loadDefaultIcon(PackageManager pm)325 public Drawable loadDefaultIcon(PackageManager pm) { 326 return pm.getDefaultActivityIcon(); 327 } 328 329 /** 330 * Retrieve the default graphical banner associated with this item. 331 * 332 * @param pm A PackageManager from which the banner can be loaded; usually 333 * the PackageManager from which you originally retrieved this item. 334 * 335 * @return Returns a Drawable containing the item's default banner 336 * or null if no default logo is available. 337 * 338 * @hide 339 */ loadDefaultBanner(PackageManager pm)340 protected Drawable loadDefaultBanner(PackageManager pm) { 341 return null; 342 } 343 344 /** 345 * Retrieve the current graphical logo associated with this item. This 346 * will call back on the given PackageManager to load the logo from 347 * the application. 348 * 349 * @param pm A PackageManager from which the logo can be loaded; usually 350 * the PackageManager from which you originally retrieved this item. 351 * 352 * @return Returns a Drawable containing the item's logo. If the item 353 * does not have a logo, this method will return null. 354 */ loadLogo(PackageManager pm)355 public Drawable loadLogo(PackageManager pm) { 356 if (logo != 0) { 357 Drawable d = pm.getDrawable(packageName, logo, getApplicationInfo()); 358 if (d != null) { 359 return d; 360 } 361 } 362 return loadDefaultLogo(pm); 363 } 364 365 /** 366 * Retrieve the default graphical logo associated with this item. 367 * 368 * @param pm A PackageManager from which the logo can be loaded; usually 369 * the PackageManager from which you originally retrieved this item. 370 * 371 * @return Returns a Drawable containing the item's default logo 372 * or null if no default logo is available. 373 * 374 * @hide 375 */ loadDefaultLogo(PackageManager pm)376 protected Drawable loadDefaultLogo(PackageManager pm) { 377 return null; 378 } 379 380 /** 381 * Load an XML resource attached to the meta-data of this item. This will 382 * retrieved the name meta-data entry, and if defined call back on the 383 * given PackageManager to load its XML file from the application. 384 * 385 * @param pm A PackageManager from which the XML can be loaded; usually 386 * the PackageManager from which you originally retrieved this item. 387 * @param name Name of the meta-date you would like to load. 388 * 389 * @return Returns an XmlPullParser you can use to parse the XML file 390 * assigned as the given meta-data. If the meta-data name is not defined 391 * or the XML resource could not be found, null is returned. 392 */ loadXmlMetaData(PackageManager pm, String name)393 public XmlResourceParser loadXmlMetaData(PackageManager pm, String name) { 394 if (metaData != null) { 395 int resid = metaData.getInt(name); 396 if (resid != 0) { 397 return pm.getXml(packageName, resid, getApplicationInfo()); 398 } 399 } 400 return null; 401 } 402 403 /** 404 * @hide Flag for dumping: include all details. 405 */ 406 public static final int DUMP_FLAG_DETAILS = 1<<0; 407 408 /** 409 * @hide Flag for dumping: include nested ApplicationInfo. 410 */ 411 public static final int DUMP_FLAG_APPLICATION = 1<<1; 412 413 /** 414 * @hide Flag for dumping: all flags to dump everything. 415 */ 416 public static final int DUMP_FLAG_ALL = DUMP_FLAG_DETAILS | DUMP_FLAG_APPLICATION; 417 dumpFront(Printer pw, String prefix)418 protected void dumpFront(Printer pw, String prefix) { 419 if (name != null) { 420 pw.println(prefix + "name=" + name); 421 } 422 pw.println(prefix + "packageName=" + packageName); 423 if (labelRes != 0 || nonLocalizedLabel != null || icon != 0 || banner != 0) { 424 pw.println(prefix + "labelRes=0x" + Integer.toHexString(labelRes) 425 + " nonLocalizedLabel=" + nonLocalizedLabel 426 + " icon=0x" + Integer.toHexString(icon) 427 + " banner=0x" + Integer.toHexString(banner)); 428 } 429 } 430 dumpBack(Printer pw, String prefix)431 protected void dumpBack(Printer pw, String prefix) { 432 // no back here 433 } 434 writeToParcel(Parcel dest, int parcelableFlags)435 public void writeToParcel(Parcel dest, int parcelableFlags) { 436 dest.writeString8(name); 437 dest.writeString8(packageName); 438 dest.writeInt(labelRes); 439 TextUtils.writeToParcel(nonLocalizedLabel, dest, parcelableFlags); 440 dest.writeInt(icon); 441 dest.writeInt(logo); 442 dest.writeBundle(metaData); 443 dest.writeInt(banner); 444 dest.writeInt(showUserIcon); 445 } 446 447 /** 448 * @hide 449 */ dumpDebug(ProtoOutputStream proto, long fieldId, int dumpFlags)450 public void dumpDebug(ProtoOutputStream proto, long fieldId, int dumpFlags) { 451 long token = proto.start(fieldId); 452 if (name != null) { 453 proto.write(PackageItemInfoProto.NAME, name); 454 } 455 proto.write(PackageItemInfoProto.PACKAGE_NAME, packageName); 456 proto.write(PackageItemInfoProto.LABEL_RES, labelRes); 457 if (nonLocalizedLabel != null) { 458 proto.write(PackageItemInfoProto.NON_LOCALIZED_LABEL, nonLocalizedLabel.toString()); 459 } 460 proto.write(PackageItemInfoProto.ICON, icon); 461 proto.write(PackageItemInfoProto.BANNER, banner); 462 proto.end(token); 463 } 464 PackageItemInfo(Parcel source)465 protected PackageItemInfo(Parcel source) { 466 name = source.readString8(); 467 packageName = source.readString8(); 468 labelRes = source.readInt(); 469 nonLocalizedLabel 470 = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); 471 icon = source.readInt(); 472 logo = source.readInt(); 473 metaData = source.readBundle(); 474 banner = source.readInt(); 475 showUserIcon = source.readInt(); 476 } 477 478 /** 479 * Get the ApplicationInfo for the application to which this item belongs, 480 * if available, otherwise returns null. 481 * 482 * @return Returns the ApplicationInfo of this item, or null if not known. 483 * 484 * @hide 485 */ getApplicationInfo()486 protected ApplicationInfo getApplicationInfo() { 487 return null; 488 } 489 490 public static class DisplayNameComparator 491 implements Comparator<PackageItemInfo> { DisplayNameComparator(PackageManager pm)492 public DisplayNameComparator(PackageManager pm) { 493 mPM = pm; 494 } 495 compare(PackageItemInfo aa, PackageItemInfo ab)496 public final int compare(PackageItemInfo aa, PackageItemInfo ab) { 497 CharSequence sa = aa.loadLabel(mPM); 498 if (sa == null) sa = aa.name; 499 CharSequence sb = ab.loadLabel(mPM); 500 if (sb == null) sb = ab.name; 501 return sCollator.compare(sa.toString(), sb.toString()); 502 } 503 504 private final Collator sCollator = Collator.getInstance(); 505 private PackageManager mPM; 506 } 507 } 508