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 java.lang.annotation.RetentionPolicy.SOURCE; 20 21 import android.annotation.FloatRange; 22 import android.annotation.IntDef; 23 import android.annotation.NonNull; 24 import android.annotation.SystemApi; 25 import android.content.res.XmlResourceParser; 26 import android.graphics.drawable.Drawable; 27 import android.os.Bundle; 28 import android.os.Parcel; 29 import android.os.UserHandle; 30 import android.text.Html; 31 import android.text.TextPaint; 32 import android.text.TextUtils; 33 import android.util.Printer; 34 import android.util.proto.ProtoOutputStream; 35 36 import com.android.internal.util.Preconditions; 37 38 import java.lang.annotation.Retention; 39 import java.text.Collator; 40 import java.util.BitSet; 41 import java.util.Comparator; 42 43 /** 44 * Base class containing information common to all package items held by 45 * the package manager. This provides a very common basic set of attributes: 46 * a label, icon, and meta-data. This class is not intended 47 * to be used by itself; it is simply here to share common definitions 48 * between all items returned by the package manager. As such, it does not 49 * itself implement Parcelable, but does provide convenience methods to assist 50 * in the implementation of Parcelable in subclasses. 51 */ 52 public class PackageItemInfo { 53 private static final int LINE_FEED_CODE_POINT = 10; 54 private static final int NBSP_CODE_POINT = 160; 55 56 /** 57 * Flags for {@link #loadSafeLabel(PackageManager, float, int)} 58 * 59 * @hide 60 */ 61 @Retention(SOURCE) 62 @IntDef(flag = true, prefix = "SAFE_LABEL_FLAG_", 63 value = {SAFE_LABEL_FLAG_TRIM, SAFE_LABEL_FLAG_SINGLE_LINE, 64 SAFE_LABEL_FLAG_FIRST_LINE}) 65 public @interface SafeLabelFlags {} 66 67 /** 68 * Remove {@link Character#isWhitespace(int) whitespace} and non-breaking spaces from the edges 69 * of the label. 70 * 71 * @see #loadSafeLabel(PackageManager, float, int) 72 * @hide 73 */ 74 public static final int SAFE_LABEL_FLAG_TRIM = 0x1; 75 76 /** 77 * Force entire string into single line of text (no newlines). Cannot be set at the same time as 78 * {@link #SAFE_LABEL_FLAG_FIRST_LINE}. 79 * 80 * @see #loadSafeLabel(PackageManager, float, int) 81 * @hide 82 */ 83 public static final int SAFE_LABEL_FLAG_SINGLE_LINE = 0x2; 84 85 /** 86 * Return only first line of text (truncate at first newline). Cannot be set at the same time as 87 * {@link #SAFE_LABEL_FLAG_SINGLE_LINE}. 88 * 89 * @see #loadSafeLabel(PackageManager, float, int) 90 * @hide 91 */ 92 public static final int SAFE_LABEL_FLAG_FIRST_LINE = 0x4; 93 94 private static final float MAX_LABEL_SIZE_PX = 500f; 95 /** The maximum length of a safe label, in characters */ 96 private static final int MAX_SAFE_LABEL_LENGTH = 50000; 97 98 private static volatile boolean sForceSafeLabels = false; 99 100 /** {@hide} */ setForceSafeLabels(boolean forceSafeLabels)101 public static void setForceSafeLabels(boolean forceSafeLabels) { 102 sForceSafeLabels = forceSafeLabels; 103 } 104 105 /** 106 * Public name of this item. From the "android:name" attribute. 107 */ 108 public String name; 109 110 /** 111 * Name of the package that this item is in. 112 */ 113 public String packageName; 114 115 /** 116 * A string resource identifier (in the package's resources) of this 117 * component's label. From the "label" attribute or, if not set, 0. 118 */ 119 public int labelRes; 120 121 /** 122 * The string provided in the AndroidManifest file, if any. You 123 * probably don't want to use this. You probably want 124 * {@link PackageManager#getApplicationLabel} 125 */ 126 public CharSequence nonLocalizedLabel; 127 128 /** 129 * A drawable resource identifier (in the package's resources) of this 130 * component's icon. From the "icon" attribute or, if not set, 0. 131 */ 132 public int icon; 133 134 /** 135 * A drawable resource identifier (in the package's resources) of this 136 * component's banner. From the "banner" attribute or, if not set, 0. 137 */ 138 public int banner; 139 140 /** 141 * A drawable resource identifier (in the package's resources) of this 142 * component's logo. Logos may be larger/wider than icons and are 143 * displayed by certain UI elements in place of a name or name/icon 144 * combination. From the "logo" attribute or, if not set, 0. 145 */ 146 public int logo; 147 148 /** 149 * Additional meta-data associated with this component. This field 150 * will only be filled in if you set the 151 * {@link PackageManager#GET_META_DATA} flag when requesting the info. 152 */ 153 public Bundle metaData; 154 155 /** 156 * If different of UserHandle.USER_NULL, The icon of this item will be the one of that user. 157 * @hide 158 */ 159 public int showUserIcon; 160 PackageItemInfo()161 public PackageItemInfo() { 162 showUserIcon = UserHandle.USER_NULL; 163 } 164 PackageItemInfo(PackageItemInfo orig)165 public PackageItemInfo(PackageItemInfo orig) { 166 name = orig.name; 167 if (name != null) name = name.trim(); 168 packageName = orig.packageName; 169 labelRes = orig.labelRes; 170 nonLocalizedLabel = orig.nonLocalizedLabel; 171 if (nonLocalizedLabel != null) nonLocalizedLabel = nonLocalizedLabel.toString().trim(); 172 icon = orig.icon; 173 banner = orig.banner; 174 logo = orig.logo; 175 metaData = orig.metaData; 176 showUserIcon = orig.showUserIcon; 177 } 178 179 /** 180 * Retrieve the current textual label associated with this item. This 181 * will call back on the given PackageManager to load the label from 182 * the application. 183 * 184 * @param pm A PackageManager from which the label can be loaded; usually 185 * the PackageManager from which you originally retrieved this item. 186 * 187 * @return Returns a CharSequence containing the item's label. If the 188 * item does not have a label, its name is returned. 189 */ loadLabel(@onNull PackageManager pm)190 public @NonNull CharSequence loadLabel(@NonNull PackageManager pm) { 191 if (sForceSafeLabels) { 192 return loadSafeLabel(pm); 193 } else { 194 return loadUnsafeLabel(pm); 195 } 196 } 197 198 /** {@hide} */ loadUnsafeLabel(PackageManager pm)199 public CharSequence loadUnsafeLabel(PackageManager pm) { 200 if (nonLocalizedLabel != null) { 201 return nonLocalizedLabel; 202 } 203 if (labelRes != 0) { 204 CharSequence label = pm.getText(packageName, labelRes, getApplicationInfo()); 205 if (label != null) { 206 return label.toString().trim(); 207 } 208 } 209 if (name != null) { 210 return name; 211 } 212 return packageName; 213 } 214 215 /** 216 * Deprecated use loadSafeLabel(PackageManager, float, int) instead 217 * 218 * @hide 219 */ 220 @SystemApi loadSafeLabel(@onNull PackageManager pm)221 public @NonNull CharSequence loadSafeLabel(@NonNull PackageManager pm) { 222 // loadLabel() always returns non-null 223 String label = loadUnsafeLabel(pm).toString(); 224 // strip HTML tags to avoid <br> and other tags overwriting original message 225 String labelStr = Html.fromHtml(label).toString(); 226 227 // If the label contains new line characters it may push the UI 228 // down to hide a part of it. Labels shouldn't have new line 229 // characters, so just truncate at the first time one is seen. 230 final int labelLength = Math.min(labelStr.length(), MAX_SAFE_LABEL_LENGTH); 231 final StringBuffer sb = new StringBuffer(labelLength); 232 int offset = 0; 233 while (offset < labelLength) { 234 final int codePoint = labelStr.codePointAt(offset); 235 final int type = Character.getType(codePoint); 236 if (type == Character.LINE_SEPARATOR 237 || type == Character.CONTROL 238 || type == Character.PARAGRAPH_SEPARATOR) { 239 labelStr = labelStr.substring(0, offset); 240 break; 241 } 242 // replace all non-break space to " " in order to be trimmed 243 final int charCount = Character.charCount(codePoint); 244 if (type == Character.SPACE_SEPARATOR) { 245 sb.append(' '); 246 } else { 247 sb.append(labelStr.charAt(offset)); 248 if (charCount == 2) { 249 sb.append(labelStr.charAt(offset + 1)); 250 } 251 } 252 offset += charCount; 253 } 254 255 labelStr = sb.toString().trim(); 256 if (labelStr.isEmpty()) { 257 return packageName; 258 } 259 TextPaint paint = new TextPaint(); 260 paint.setTextSize(42); 261 262 return TextUtils.ellipsize(labelStr, paint, MAX_LABEL_SIZE_PX, 263 TextUtils.TruncateAt.END); 264 } 265 isNewline(int codePoint)266 private static boolean isNewline(int codePoint) { 267 int type = Character.getType(codePoint); 268 return type == Character.PARAGRAPH_SEPARATOR || type == Character.LINE_SEPARATOR 269 || codePoint == LINE_FEED_CODE_POINT; 270 } 271 isWhiteSpace(int codePoint)272 private static boolean isWhiteSpace(int codePoint) { 273 return Character.isWhitespace(codePoint) || codePoint == NBSP_CODE_POINT; 274 } 275 276 /** 277 * A special string manipulation class. Just records removals and executes the when onString() 278 * is called. 279 */ 280 private static class StringWithRemovedChars { 281 /** The original string */ 282 private final String mOriginal; 283 284 /** 285 * One bit per char in string. If bit is set, character needs to be removed. If whole 286 * bit field is not initialized nothing needs to be removed. 287 */ 288 private BitSet mRemovedChars; 289 StringWithRemovedChars(@onNull String original)290 StringWithRemovedChars(@NonNull String original) { 291 mOriginal = original; 292 } 293 294 /** 295 * Mark all chars in a range {@code [firstRemoved - firstNonRemoved[} (not including 296 * firstNonRemoved) as removed. 297 */ removeRange(int firstRemoved, int firstNonRemoved)298 void removeRange(int firstRemoved, int firstNonRemoved) { 299 if (mRemovedChars == null) { 300 mRemovedChars = new BitSet(mOriginal.length()); 301 } 302 303 mRemovedChars.set(firstRemoved, firstNonRemoved); 304 } 305 306 /** 307 * Remove all characters before {@code firstNonRemoved}. 308 */ removeAllCharBefore(int firstNonRemoved)309 void removeAllCharBefore(int firstNonRemoved) { 310 if (mRemovedChars == null) { 311 mRemovedChars = new BitSet(mOriginal.length()); 312 } 313 314 mRemovedChars.set(0, firstNonRemoved); 315 } 316 317 /** 318 * Remove all characters after and including {@code firstRemoved}. 319 */ removeAllCharAfter(int firstRemoved)320 void removeAllCharAfter(int firstRemoved) { 321 if (mRemovedChars == null) { 322 mRemovedChars = new BitSet(mOriginal.length()); 323 } 324 325 mRemovedChars.set(firstRemoved, mOriginal.length()); 326 } 327 328 @Override toString()329 public String toString() { 330 // Common case, no chars removed 331 if (mRemovedChars == null) { 332 return mOriginal; 333 } 334 335 StringBuilder sb = new StringBuilder(mOriginal.length()); 336 for (int i = 0; i < mOriginal.length(); i++) { 337 if (!mRemovedChars.get(i)) { 338 sb.append(mOriginal.charAt(i)); 339 } 340 } 341 342 return sb.toString(); 343 } 344 345 /** 346 * Return length or the original string 347 */ length()348 int length() { 349 return mOriginal.length(); 350 } 351 352 /** 353 * Return if a certain {@code offset} of the original string is removed 354 */ isRemoved(int offset)355 boolean isRemoved(int offset) { 356 return mRemovedChars != null && mRemovedChars.get(offset); 357 } 358 359 /** 360 * Return codePoint of original string at a certain {@code offset} 361 */ codePointAt(int offset)362 int codePointAt(int offset) { 363 return mOriginal.codePointAt(offset); 364 } 365 } 366 367 /** 368 * Load, clean up and truncate label before use. 369 * 370 * <p>This method is meant to remove common mistakes and nefarious formatting from strings that 371 * are used in sensitive parts of the UI. 372 * 373 * <p>This method first treats the string like HTML and then ... 374 * <ul> 375 * <li>Removes new lines or truncates at first new line 376 * <li>Trims the white-space off the end 377 * <li>Truncates the string to a given length 378 * </ul> 379 * ... if specified. 380 * 381 * @param ellipsizeDip Assuming maximum length of the string (in dip), assuming font size 42. 382 * This is roughly 50 characters for {@code ellipsizeDip == 1000}.<br /> 383 * Usually ellipsizing should be left to the view showing the string. If a 384 * string is used as an input to another string, it might be useful to 385 * control the length of the input string though. {@code 0} disables this 386 * feature. 387 * @return The safe label 388 * @hide 389 */ loadSafeLabel(@onNull PackageManager pm, @FloatRange(from = 0) float ellipsizeDip, @SafeLabelFlags int flags)390 public @NonNull CharSequence loadSafeLabel(@NonNull PackageManager pm, 391 @FloatRange(from = 0) float ellipsizeDip, @SafeLabelFlags int flags) { 392 boolean onlyKeepFirstLine = ((flags & SAFE_LABEL_FLAG_FIRST_LINE) != 0); 393 boolean forceSingleLine = ((flags & SAFE_LABEL_FLAG_SINGLE_LINE) != 0); 394 boolean trim = ((flags & SAFE_LABEL_FLAG_TRIM) != 0); 395 396 Preconditions.checkNotNull(pm); 397 Preconditions.checkArgument(ellipsizeDip >= 0); 398 Preconditions.checkFlagsArgument(flags, SAFE_LABEL_FLAG_TRIM | SAFE_LABEL_FLAG_SINGLE_LINE 399 | SAFE_LABEL_FLAG_FIRST_LINE); 400 Preconditions.checkArgument(!(onlyKeepFirstLine && forceSingleLine), 401 "Cannot set SAFE_LABEL_FLAG_SINGLE_LINE and SAFE_LABEL_FLAG_FIRST_LINE at the same " 402 + "time"); 403 404 // loadLabel() always returns non-null 405 String label = loadUnsafeLabel(pm).toString(); 406 407 // Treat string as HTML. This 408 // - converts HTML symbols: e.g. ß -> ß 409 // - applies some HTML tags: e.g. <br> -> \n 410 // - removes invalid characters such as \b 411 // - removes html styling, such as <b> 412 // - applies html formatting: e.g. a<p>b</p>c -> a\n\nb\n\nc 413 // - replaces some html tags by "object replacement" markers: <img> -> \ufffc 414 // - Removes leading white space 415 // - Removes all trailing white space beside a single space 416 // - Collapses double white space 417 StringWithRemovedChars labelStr = new StringWithRemovedChars( 418 Html.fromHtml(label).toString()); 419 420 int firstNonWhiteSpace = -1; 421 int firstTrailingWhiteSpace = -1; 422 423 // Remove new lines (if requested) and control characters. 424 int labelLength = labelStr.length(); 425 for (int offset = 0; offset < labelLength; ) { 426 int codePoint = labelStr.codePointAt(offset); 427 int type = Character.getType(codePoint); 428 int codePointLen = Character.charCount(codePoint); 429 boolean isNewline = isNewline(codePoint); 430 431 if (offset > MAX_SAFE_LABEL_LENGTH || onlyKeepFirstLine && isNewline) { 432 labelStr.removeAllCharAfter(offset); 433 break; 434 } else if (forceSingleLine && isNewline) { 435 labelStr.removeRange(offset, offset + codePointLen); 436 } else if (type == Character.CONTROL && !isNewline) { 437 labelStr.removeRange(offset, offset + codePointLen); 438 } else if (trim && !isWhiteSpace(codePoint)) { 439 // This is only executed if the code point is not removed 440 if (firstNonWhiteSpace == -1) { 441 firstNonWhiteSpace = offset; 442 } 443 firstTrailingWhiteSpace = offset + codePointLen; 444 } 445 446 offset += codePointLen; 447 } 448 449 if (trim) { 450 // Remove leading and trailing white space 451 if (firstNonWhiteSpace == -1) { 452 // No non whitespace found, remove all 453 labelStr.removeAllCharAfter(0); 454 } else { 455 if (firstNonWhiteSpace > 0) { 456 labelStr.removeAllCharBefore(firstNonWhiteSpace); 457 } 458 if (firstTrailingWhiteSpace < labelLength) { 459 labelStr.removeAllCharAfter(firstTrailingWhiteSpace); 460 } 461 } 462 } 463 464 if (ellipsizeDip == 0) { 465 return labelStr.toString(); 466 } else { 467 // Truncate 468 final TextPaint paint = new TextPaint(); 469 paint.setTextSize(42); 470 471 return TextUtils.ellipsize(labelStr.toString(), paint, ellipsizeDip, 472 TextUtils.TruncateAt.END); 473 } 474 } 475 476 /** 477 * Retrieve the current graphical icon associated with this item. This 478 * will call back on the given PackageManager to load the icon from 479 * the application. 480 * 481 * @param pm A PackageManager from which the icon can be loaded; usually 482 * the PackageManager from which you originally retrieved this item. 483 * 484 * @return Returns a Drawable containing the item's icon. If the 485 * item does not have an icon, the item's default icon is returned 486 * such as the default activity icon. 487 */ loadIcon(PackageManager pm)488 public Drawable loadIcon(PackageManager pm) { 489 return pm.loadItemIcon(this, getApplicationInfo()); 490 } 491 492 /** 493 * Retrieve the current graphical icon associated with this item without 494 * the addition of a work badge if applicable. 495 * This will call back on the given PackageManager to load the icon from 496 * the application. 497 * 498 * @param pm A PackageManager from which the icon can be loaded; usually 499 * the PackageManager from which you originally retrieved this item. 500 * 501 * @return Returns a Drawable containing the item's icon. If the 502 * item does not have an icon, the item's default icon is returned 503 * such as the default activity icon. 504 */ loadUnbadgedIcon(PackageManager pm)505 public Drawable loadUnbadgedIcon(PackageManager pm) { 506 return pm.loadUnbadgedItemIcon(this, getApplicationInfo()); 507 } 508 509 /** 510 * Retrieve the current graphical banner associated with this item. This 511 * will call back on the given PackageManager to load the banner from 512 * the application. 513 * 514 * @param pm A PackageManager from which the banner can be loaded; usually 515 * the PackageManager from which you originally retrieved this item. 516 * 517 * @return Returns a Drawable containing the item's banner. If the item 518 * does not have a banner, this method will return null. 519 */ loadBanner(PackageManager pm)520 public Drawable loadBanner(PackageManager pm) { 521 if (banner != 0) { 522 Drawable dr = pm.getDrawable(packageName, banner, getApplicationInfo()); 523 if (dr != null) { 524 return dr; 525 } 526 } 527 return loadDefaultBanner(pm); 528 } 529 530 /** 531 * Retrieve the default graphical icon associated with this item. 532 * 533 * @param pm A PackageManager from which the icon can be loaded; usually 534 * the PackageManager from which you originally retrieved this item. 535 * 536 * @return Returns a Drawable containing the item's default icon 537 * such as the default activity icon. 538 * 539 * @hide 540 */ loadDefaultIcon(PackageManager pm)541 public Drawable loadDefaultIcon(PackageManager pm) { 542 return pm.getDefaultActivityIcon(); 543 } 544 545 /** 546 * Retrieve the default graphical banner associated with this item. 547 * 548 * @param pm A PackageManager from which the banner can be loaded; usually 549 * the PackageManager from which you originally retrieved this item. 550 * 551 * @return Returns a Drawable containing the item's default banner 552 * or null if no default logo is available. 553 * 554 * @hide 555 */ loadDefaultBanner(PackageManager pm)556 protected Drawable loadDefaultBanner(PackageManager pm) { 557 return null; 558 } 559 560 /** 561 * Retrieve the current graphical logo associated with this item. This 562 * will call back on the given PackageManager to load the logo from 563 * the application. 564 * 565 * @param pm A PackageManager from which the logo can be loaded; usually 566 * the PackageManager from which you originally retrieved this item. 567 * 568 * @return Returns a Drawable containing the item's logo. If the item 569 * does not have a logo, this method will return null. 570 */ loadLogo(PackageManager pm)571 public Drawable loadLogo(PackageManager pm) { 572 if (logo != 0) { 573 Drawable d = pm.getDrawable(packageName, logo, getApplicationInfo()); 574 if (d != null) { 575 return d; 576 } 577 } 578 return loadDefaultLogo(pm); 579 } 580 581 /** 582 * Retrieve the default graphical logo associated with this item. 583 * 584 * @param pm A PackageManager from which the logo can be loaded; usually 585 * the PackageManager from which you originally retrieved this item. 586 * 587 * @return Returns a Drawable containing the item's default logo 588 * or null if no default logo is available. 589 * 590 * @hide 591 */ loadDefaultLogo(PackageManager pm)592 protected Drawable loadDefaultLogo(PackageManager pm) { 593 return null; 594 } 595 596 /** 597 * Load an XML resource attached to the meta-data of this item. This will 598 * retrieved the name meta-data entry, and if defined call back on the 599 * given PackageManager to load its XML file from the application. 600 * 601 * @param pm A PackageManager from which the XML can be loaded; usually 602 * the PackageManager from which you originally retrieved this item. 603 * @param name Name of the meta-date you would like to load. 604 * 605 * @return Returns an XmlPullParser you can use to parse the XML file 606 * assigned as the given meta-data. If the meta-data name is not defined 607 * or the XML resource could not be found, null is returned. 608 */ loadXmlMetaData(PackageManager pm, String name)609 public XmlResourceParser loadXmlMetaData(PackageManager pm, String name) { 610 if (metaData != null) { 611 int resid = metaData.getInt(name); 612 if (resid != 0) { 613 return pm.getXml(packageName, resid, getApplicationInfo()); 614 } 615 } 616 return null; 617 } 618 619 /** 620 * @hide Flag for dumping: include all details. 621 */ 622 public static final int DUMP_FLAG_DETAILS = 1<<0; 623 624 /** 625 * @hide Flag for dumping: include nested ApplicationInfo. 626 */ 627 public static final int DUMP_FLAG_APPLICATION = 1<<1; 628 629 /** 630 * @hide Flag for dumping: all flags to dump everything. 631 */ 632 public static final int DUMP_FLAG_ALL = DUMP_FLAG_DETAILS | DUMP_FLAG_APPLICATION; 633 dumpFront(Printer pw, String prefix)634 protected void dumpFront(Printer pw, String prefix) { 635 if (name != null) { 636 pw.println(prefix + "name=" + name); 637 } 638 pw.println(prefix + "packageName=" + packageName); 639 if (labelRes != 0 || nonLocalizedLabel != null || icon != 0 || banner != 0) { 640 pw.println(prefix + "labelRes=0x" + Integer.toHexString(labelRes) 641 + " nonLocalizedLabel=" + nonLocalizedLabel 642 + " icon=0x" + Integer.toHexString(icon) 643 + " banner=0x" + Integer.toHexString(banner)); 644 } 645 } 646 dumpBack(Printer pw, String prefix)647 protected void dumpBack(Printer pw, String prefix) { 648 // no back here 649 } 650 writeToParcel(Parcel dest, int parcelableFlags)651 public void writeToParcel(Parcel dest, int parcelableFlags) { 652 dest.writeString(name); 653 dest.writeString(packageName); 654 dest.writeInt(labelRes); 655 TextUtils.writeToParcel(nonLocalizedLabel, dest, parcelableFlags); 656 dest.writeInt(icon); 657 dest.writeInt(logo); 658 dest.writeBundle(metaData); 659 dest.writeInt(banner); 660 dest.writeInt(showUserIcon); 661 } 662 663 /** 664 * @hide 665 */ writeToProto(ProtoOutputStream proto, long fieldId)666 public void writeToProto(ProtoOutputStream proto, long fieldId) { 667 long token = proto.start(fieldId); 668 if (name != null) { 669 proto.write(PackageItemInfoProto.NAME, name); 670 } 671 proto.write(PackageItemInfoProto.PACKAGE_NAME, packageName); 672 if (labelRes != 0 || nonLocalizedLabel != null || icon != 0 || banner != 0) { 673 proto.write(PackageItemInfoProto.LABEL_RES, labelRes); 674 proto.write(PackageItemInfoProto.NON_LOCALIZED_LABEL, nonLocalizedLabel.toString()); 675 proto.write(PackageItemInfoProto.ICON, icon); 676 proto.write(PackageItemInfoProto.BANNER, banner); 677 } 678 proto.end(token); 679 } 680 PackageItemInfo(Parcel source)681 protected PackageItemInfo(Parcel source) { 682 name = source.readString(); 683 packageName = source.readString(); 684 labelRes = source.readInt(); 685 nonLocalizedLabel 686 = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); 687 icon = source.readInt(); 688 logo = source.readInt(); 689 metaData = source.readBundle(); 690 banner = source.readInt(); 691 showUserIcon = source.readInt(); 692 } 693 694 /** 695 * Get the ApplicationInfo for the application to which this item belongs, 696 * if available, otherwise returns null. 697 * 698 * @return Returns the ApplicationInfo of this item, or null if not known. 699 * 700 * @hide 701 */ getApplicationInfo()702 protected ApplicationInfo getApplicationInfo() { 703 return null; 704 } 705 706 public static class DisplayNameComparator 707 implements Comparator<PackageItemInfo> { DisplayNameComparator(PackageManager pm)708 public DisplayNameComparator(PackageManager pm) { 709 mPM = pm; 710 } 711 compare(PackageItemInfo aa, PackageItemInfo ab)712 public final int compare(PackageItemInfo aa, PackageItemInfo ab) { 713 CharSequence sa = aa.loadLabel(mPM); 714 if (sa == null) sa = aa.name; 715 CharSequence sb = ab.loadLabel(mPM); 716 if (sb == null) sb = ab.name; 717 return sCollator.compare(sa.toString(), sb.toString()); 718 } 719 720 private final Collator sCollator = Collator.getInstance(); 721 private PackageManager mPM; 722 } 723 } 724