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.annotation.NonNull;
20 import android.annotation.SystemApi;
21 import android.content.res.XmlResourceParser;
22 
23 import android.graphics.drawable.Drawable;
24 import android.os.Bundle;
25 import android.os.Parcel;
26 import android.os.UserHandle;
27 import android.text.Html;
28 import android.text.TextPaint;
29 import android.text.TextUtils;
30 import android.util.Printer;
31 import java.text.Collator;
32 import java.util.Comparator;
33 
34 /**
35  * Base class containing information common to all package items held by
36  * the package manager.  This provides a very common basic set of attributes:
37  * a label, icon, and meta-data.  This class is not intended
38  * to be used by itself; it is simply here to share common definitions
39  * between all items returned by the package manager.  As such, it does not
40  * itself implement Parcelable, but does provide convenience methods to assist
41  * in the implementation of Parcelable in subclasses.
42  */
43 public class PackageItemInfo {
44     private static final float MAX_LABEL_SIZE_PX = 500f;
45     /**
46      * Public name of this item. From the "android:name" attribute.
47      */
48     public String name;
49 
50     /**
51      * Name of the package that this item is in.
52      */
53     public String packageName;
54 
55     /**
56      * A string resource identifier (in the package's resources) of this
57      * component's label.  From the "label" attribute or, if not set, 0.
58      */
59     public int labelRes;
60 
61     /**
62      * The string provided in the AndroidManifest file, if any.  You
63      * probably don't want to use this.  You probably want
64      * {@link PackageManager#getApplicationLabel}
65      */
66     public CharSequence nonLocalizedLabel;
67 
68     /**
69      * A drawable resource identifier (in the package's resources) of this
70      * component's icon.  From the "icon" attribute or, if not set, 0.
71      */
72     public int icon;
73 
74     /**
75      * A drawable resource identifier (in the package's resources) of this
76      * component's banner.  From the "banner" attribute or, if not set, 0.
77      */
78     public int banner;
79 
80     /**
81      * A drawable resource identifier (in the package's resources) of this
82      * component's logo. Logos may be larger/wider than icons and are
83      * displayed by certain UI elements in place of a name or name/icon
84      * combination. From the "logo" attribute or, if not set, 0.
85      */
86     public int logo;
87 
88     /**
89      * Additional meta-data associated with this component.  This field
90      * will only be filled in if you set the
91      * {@link PackageManager#GET_META_DATA} flag when requesting the info.
92      */
93     public Bundle metaData;
94 
95     /**
96      * If different of UserHandle.USER_NULL, The icon of this item will be the one of that user.
97      * @hide
98      */
99     public int showUserIcon;
100 
PackageItemInfo()101     public PackageItemInfo() {
102         showUserIcon = UserHandle.USER_NULL;
103     }
104 
PackageItemInfo(PackageItemInfo orig)105     public PackageItemInfo(PackageItemInfo orig) {
106         name = orig.name;
107         if (name != null) name = name.trim();
108         packageName = orig.packageName;
109         labelRes = orig.labelRes;
110         nonLocalizedLabel = orig.nonLocalizedLabel;
111         if (nonLocalizedLabel != null) nonLocalizedLabel = nonLocalizedLabel.toString().trim();
112         icon = orig.icon;
113         banner = orig.banner;
114         logo = orig.logo;
115         metaData = orig.metaData;
116         showUserIcon = orig.showUserIcon;
117     }
118 
119     /**
120      * Retrieve the current textual label associated with this item.  This
121      * will call back on the given PackageManager to load the label from
122      * the application.
123      *
124      * @param pm A PackageManager from which the label can be loaded; usually
125      * the PackageManager from which you originally retrieved this item.
126      *
127      * @return Returns a CharSequence containing the item's label.  If the
128      * item does not have a label, its name is returned.
129      */
loadLabel(PackageManager pm)130     public CharSequence loadLabel(PackageManager pm) {
131         if (nonLocalizedLabel != null) {
132             return nonLocalizedLabel;
133         }
134         if (labelRes != 0) {
135             CharSequence label = pm.getText(packageName, labelRes, getApplicationInfo());
136             if (label != null) {
137                 return label.toString().trim();
138             }
139         }
140         if (name != null) {
141             return name;
142         }
143         return packageName;
144     }
145 
146     /**
147      * Same as {@link #loadLabel(PackageManager)} with the addition that
148      * the returned label is safe for being presented in the UI since it
149      * will not contain new lines and the length will be limited to a
150      * reasonable amount. This prevents a malicious party to influence UI
151      * layout via the app label misleading the user into performing a
152      * detrimental for them action. If the label is too long it will be
153      * truncated and ellipsized at the end.
154      *
155      * @param pm A PackageManager from which the label can be loaded; usually
156      * the PackageManager from which you originally retrieved this item
157      * @return Returns a CharSequence containing the item's label. If the
158      * item does not have a label, its name is returned.
159      *
160      * @hide
161      */
162     @SystemApi
loadSafeLabel(@onNull PackageManager pm)163     public @NonNull CharSequence loadSafeLabel(@NonNull PackageManager pm) {
164         // loadLabel() always returns non-null
165         String label = loadLabel(pm).toString();
166         // strip HTML tags to avoid <br> and other tags overwriting original message
167         String labelStr = Html.fromHtml(label).toString();
168 
169         // If the label contains new line characters it may push the UI
170         // down to hide a part of it. Labels shouldn't have new line
171         // characters, so just truncate at the first time one is seen.
172         final int labelLength = labelStr.length();
173         int offset = 0;
174         while (offset < labelLength) {
175             final int codePoint = labelStr.codePointAt(offset);
176             final int type = Character.getType(codePoint);
177             if (type == Character.LINE_SEPARATOR
178                     || type == Character.CONTROL
179                     || type == Character.PARAGRAPH_SEPARATOR) {
180                 labelStr = labelStr.substring(0, offset);
181                 break;
182             }
183             // replace all non-break space to " " in order to be trimmed
184             if (type == Character.SPACE_SEPARATOR) {
185                 labelStr = labelStr.substring(0, offset) + " " + labelStr.substring(offset +
186                         Character.charCount(codePoint));
187             }
188             offset += Character.charCount(codePoint);
189         }
190 
191         labelStr = labelStr.trim();
192         if (labelStr.isEmpty()) {
193             return packageName;
194         }
195         TextPaint paint = new TextPaint();
196         paint.setTextSize(42);
197 
198         return TextUtils.ellipsize(labelStr, paint, MAX_LABEL_SIZE_PX,
199                 TextUtils.TruncateAt.END);
200     }
201 
202     /**
203      * Retrieve the current graphical icon associated with this item.  This
204      * will call back on the given PackageManager to load the icon from
205      * the application.
206      *
207      * @param pm A PackageManager from which the icon can be loaded; usually
208      * the PackageManager from which you originally retrieved this item.
209      *
210      * @return Returns a Drawable containing the item's icon.  If the
211      * item does not have an icon, the item's default icon is returned
212      * such as the default activity icon.
213      */
loadIcon(PackageManager pm)214     public Drawable loadIcon(PackageManager pm) {
215         return pm.loadItemIcon(this, getApplicationInfo());
216     }
217 
218     /**
219      * Retrieve the current graphical icon associated with this item without
220      * the addition of a work badge if applicable.
221      * This will call back on the given PackageManager to load the icon from
222      * the application.
223      *
224      * @param pm A PackageManager from which the icon can be loaded; usually
225      * the PackageManager from which you originally retrieved this item.
226      *
227      * @return Returns a Drawable containing the item's icon.  If the
228      * item does not have an icon, the item's default icon is returned
229      * such as the default activity icon.
230      */
loadUnbadgedIcon(PackageManager pm)231     public Drawable loadUnbadgedIcon(PackageManager pm) {
232         return pm.loadUnbadgedItemIcon(this, getApplicationInfo());
233     }
234 
235     /**
236      * Retrieve the current graphical banner associated with this item.  This
237      * will call back on the given PackageManager to load the banner from
238      * the application.
239      *
240      * @param pm A PackageManager from which the banner can be loaded; usually
241      * the PackageManager from which you originally retrieved this item.
242      *
243      * @return Returns a Drawable containing the item's banner.  If the item
244      * does not have a banner, this method will return null.
245      */
loadBanner(PackageManager pm)246     public Drawable loadBanner(PackageManager pm) {
247         if (banner != 0) {
248             Drawable dr = pm.getDrawable(packageName, banner, getApplicationInfo());
249             if (dr != null) {
250                 return dr;
251             }
252         }
253         return loadDefaultBanner(pm);
254     }
255 
256     /**
257      * Retrieve the default graphical icon associated with this item.
258      *
259      * @param pm A PackageManager from which the icon can be loaded; usually
260      * the PackageManager from which you originally retrieved this item.
261      *
262      * @return Returns a Drawable containing the item's default icon
263      * such as the default activity icon.
264      *
265      * @hide
266      */
loadDefaultIcon(PackageManager pm)267     public Drawable loadDefaultIcon(PackageManager pm) {
268         return pm.getDefaultActivityIcon();
269     }
270 
271     /**
272      * Retrieve the default graphical banner associated with this item.
273      *
274      * @param pm A PackageManager from which the banner can be loaded; usually
275      * the PackageManager from which you originally retrieved this item.
276      *
277      * @return Returns a Drawable containing the item's default banner
278      * or null if no default logo is available.
279      *
280      * @hide
281      */
loadDefaultBanner(PackageManager pm)282     protected Drawable loadDefaultBanner(PackageManager pm) {
283         return null;
284     }
285 
286     /**
287      * Retrieve the current graphical logo associated with this item. This
288      * will call back on the given PackageManager to load the logo from
289      * the application.
290      *
291      * @param pm A PackageManager from which the logo can be loaded; usually
292      * the PackageManager from which you originally retrieved this item.
293      *
294      * @return Returns a Drawable containing the item's logo. If the item
295      * does not have a logo, this method will return null.
296      */
loadLogo(PackageManager pm)297     public Drawable loadLogo(PackageManager pm) {
298         if (logo != 0) {
299             Drawable d = pm.getDrawable(packageName, logo, getApplicationInfo());
300             if (d != null) {
301                 return d;
302             }
303         }
304         return loadDefaultLogo(pm);
305     }
306 
307     /**
308      * Retrieve the default graphical logo associated with this item.
309      *
310      * @param pm A PackageManager from which the logo can be loaded; usually
311      * the PackageManager from which you originally retrieved this item.
312      *
313      * @return Returns a Drawable containing the item's default logo
314      * or null if no default logo is available.
315      *
316      * @hide
317      */
loadDefaultLogo(PackageManager pm)318     protected Drawable loadDefaultLogo(PackageManager pm) {
319         return null;
320     }
321 
322     /**
323      * Load an XML resource attached to the meta-data of this item.  This will
324      * retrieved the name meta-data entry, and if defined call back on the
325      * given PackageManager to load its XML file from the application.
326      *
327      * @param pm A PackageManager from which the XML can be loaded; usually
328      * the PackageManager from which you originally retrieved this item.
329      * @param name Name of the meta-date you would like to load.
330      *
331      * @return Returns an XmlPullParser you can use to parse the XML file
332      * assigned as the given meta-data.  If the meta-data name is not defined
333      * or the XML resource could not be found, null is returned.
334      */
loadXmlMetaData(PackageManager pm, String name)335     public XmlResourceParser loadXmlMetaData(PackageManager pm, String name) {
336         if (metaData != null) {
337             int resid = metaData.getInt(name);
338             if (resid != 0) {
339                 return pm.getXml(packageName, resid, getApplicationInfo());
340             }
341         }
342         return null;
343     }
344 
345     /**
346      * @hide Flag for dumping: include all details.
347      */
348     public static final int DUMP_FLAG_DETAILS = 1<<0;
349 
350     /**
351      * @hide Flag for dumping: include nested ApplicationInfo.
352      */
353     public static final int DUMP_FLAG_APPLICATION = 1<<1;
354 
355     /**
356      * @hide Flag for dumping: all flags to dump everything.
357      */
358     public static final int DUMP_FLAG_ALL = DUMP_FLAG_DETAILS | DUMP_FLAG_APPLICATION;
359 
dumpFront(Printer pw, String prefix)360     protected void dumpFront(Printer pw, String prefix) {
361         if (name != null) {
362             pw.println(prefix + "name=" + name);
363         }
364         pw.println(prefix + "packageName=" + packageName);
365         if (labelRes != 0 || nonLocalizedLabel != null || icon != 0 || banner != 0) {
366             pw.println(prefix + "labelRes=0x" + Integer.toHexString(labelRes)
367                     + " nonLocalizedLabel=" + nonLocalizedLabel
368                     + " icon=0x" + Integer.toHexString(icon)
369                     + " banner=0x" + Integer.toHexString(banner));
370         }
371     }
372 
dumpBack(Printer pw, String prefix)373     protected void dumpBack(Printer pw, String prefix) {
374         // no back here
375     }
376 
writeToParcel(Parcel dest, int parcelableFlags)377     public void writeToParcel(Parcel dest, int parcelableFlags) {
378         dest.writeString(name);
379         dest.writeString(packageName);
380         dest.writeInt(labelRes);
381         TextUtils.writeToParcel(nonLocalizedLabel, dest, parcelableFlags);
382         dest.writeInt(icon);
383         dest.writeInt(logo);
384         dest.writeBundle(metaData);
385         dest.writeInt(banner);
386         dest.writeInt(showUserIcon);
387     }
388 
PackageItemInfo(Parcel source)389     protected PackageItemInfo(Parcel source) {
390         name = source.readString();
391         packageName = source.readString();
392         labelRes = source.readInt();
393         nonLocalizedLabel
394                 = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
395         icon = source.readInt();
396         logo = source.readInt();
397         metaData = source.readBundle();
398         banner = source.readInt();
399         showUserIcon = source.readInt();
400     }
401 
402     /**
403      * Get the ApplicationInfo for the application to which this item belongs,
404      * if available, otherwise returns null.
405      *
406      * @return Returns the ApplicationInfo of this item, or null if not known.
407      *
408      * @hide
409      */
getApplicationInfo()410     protected ApplicationInfo getApplicationInfo() {
411         return null;
412     }
413 
414     public static class DisplayNameComparator
415             implements Comparator<PackageItemInfo> {
DisplayNameComparator(PackageManager pm)416         public DisplayNameComparator(PackageManager pm) {
417             mPM = pm;
418         }
419 
compare(PackageItemInfo aa, PackageItemInfo ab)420         public final int compare(PackageItemInfo aa, PackageItemInfo ab) {
421             CharSequence  sa = aa.loadLabel(mPM);
422             if (sa == null) sa = aa.name;
423             CharSequence  sb = ab.loadLabel(mPM);
424             if (sb == null) sb = ab.name;
425             return sCollator.compare(sa.toString(), sb.toString());
426         }
427 
428         private final Collator   sCollator = Collator.getInstance();
429         private PackageManager   mPM;
430     }
431 }
432