1 /**
2  * Copyright (C) 2007 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations
14  * under the License.
15  */
16 
17 package com.android.settings;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.app.ActivityManager;
22 import android.app.ActivityManagerNative;
23 import android.app.AlertDialog;
24 import android.app.AppGlobals;
25 import android.app.Dialog;
26 import android.app.Fragment;
27 import android.app.IActivityManager;
28 import android.app.admin.DevicePolicyManager;
29 import android.content.ComponentName;
30 import android.content.ContentResolver;
31 import android.content.Context;
32 import android.content.DialogInterface;
33 import android.content.Intent;
34 import android.content.IntentFilter;
35 import android.content.pm.ApplicationInfo;
36 import android.content.pm.IPackageManager;
37 import android.content.pm.IntentFilterVerificationInfo;
38 import android.content.pm.PackageManager;
39 import android.content.pm.PackageManager.NameNotFoundException;
40 import android.content.pm.ResolveInfo;
41 import android.content.pm.UserInfo;
42 import android.content.res.Resources;
43 import android.content.res.TypedArray;
44 import android.database.Cursor;
45 import android.graphics.Bitmap;
46 import android.graphics.BitmapFactory;
47 import android.net.ConnectivityManager;
48 import android.net.LinkProperties;
49 import android.net.Uri;
50 import android.os.BatteryManager;
51 import android.os.Bundle;
52 import android.os.Handler;
53 import android.os.HandlerThread;
54 import android.os.IBinder;
55 import android.os.INetworkManagementService;
56 import android.os.Looper;
57 import android.os.RemoteException;
58 import android.os.ServiceManager;
59 import android.os.UserHandle;
60 import android.os.UserManager;
61 import android.os.storage.StorageManager;
62 import android.preference.PreferenceFrameLayout;
63 import android.provider.ContactsContract.CommonDataKinds;
64 import android.provider.ContactsContract.Contacts;
65 import android.provider.ContactsContract.Data;
66 import android.provider.ContactsContract.Profile;
67 import android.provider.ContactsContract.RawContacts;
68 import android.provider.Settings;
69 import android.service.persistentdata.PersistentDataBlockManager;
70 import android.support.v7.preference.Preference;
71 import android.support.v7.preference.PreferenceGroup;
72 import android.support.v7.preference.PreferenceManager;
73 import android.support.v7.preference.PreferenceScreen;
74 import android.telephony.TelephonyManager;
75 import android.text.Spannable;
76 import android.text.SpannableString;
77 import android.text.TextUtils;
78 import android.text.format.DateUtils;
79 import android.text.style.TtsSpan;
80 import android.util.ArraySet;
81 import android.util.Log;
82 import android.util.SparseArray;
83 import android.util.TypedValue;
84 import android.view.LayoutInflater;
85 import android.view.View;
86 import android.view.ViewGroup;
87 import android.view.animation.Animation;
88 import android.view.animation.Animation.AnimationListener;
89 import android.view.animation.AnimationUtils;
90 import android.widget.ListView;
91 import android.widget.TabWidget;
92 import com.android.internal.app.UnlaunchableAppActivity;
93 import com.android.internal.util.ArrayUtils;
94 import com.android.internal.util.UserIcons;
95 
96 import java.io.IOException;
97 import java.io.InputStream;
98 import java.net.InetAddress;
99 import java.util.ArrayList;
100 import java.util.Iterator;
101 import java.util.List;
102 import java.util.Locale;
103 
104 import static android.content.Intent.EXTRA_USER;
105 import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
106 import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
107 
108 public final class Utils extends com.android.settingslib.Utils {
109 
110     private static final String TAG = "Settings";
111 
112     /**
113      * Set the preference's title to the matching activity's label.
114      */
115     public static final int UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY = 1;
116 
117     /**
118      * The opacity level of a disabled icon.
119      */
120     public static final float DISABLED_ALPHA = 0.4f;
121 
122     /**
123      * Color spectrum to use to indicate badness.  0 is completely transparent (no data),
124      * 1 is most bad (red), the last value is least bad (green).
125      */
126     public static final int[] BADNESS_COLORS = new int[] {
127             0x00000000, 0xffc43828, 0xffe54918, 0xfff47b00,
128             0xfffabf2c, 0xff679e37, 0xff0a7f42
129     };
130 
131     private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
132 
133     private static final int SECONDS_PER_MINUTE = 60;
134     private static final int SECONDS_PER_HOUR = 60 * 60;
135     private static final int SECONDS_PER_DAY = 24 * 60 * 60;
136 
137     public static final String OS_PKG = "os";
138 
139     private static SparseArray<Bitmap> sDarkDefaultUserBitmapCache = new SparseArray<Bitmap>();
140 
141     /**
142      * Finds a matching activity for a preference's intent. If a matching
143      * activity is not found, it will remove the preference.
144      *
145      * @param context The context.
146      * @param parentPreferenceGroup The preference group that contains the
147      *            preference whose intent is being resolved.
148      * @param preferenceKey The key of the preference whose intent is being
149      *            resolved.
150      * @param flags 0 or one or more of
151      *            {@link #UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY}
152      *            .
153      * @return Whether an activity was found. If false, the preference was
154      *         removed.
155      */
updatePreferenceToSpecificActivityOrRemove(Context context, PreferenceGroup parentPreferenceGroup, String preferenceKey, int flags)156     public static boolean updatePreferenceToSpecificActivityOrRemove(Context context,
157             PreferenceGroup parentPreferenceGroup, String preferenceKey, int flags) {
158 
159         Preference preference = parentPreferenceGroup.findPreference(preferenceKey);
160         if (preference == null) {
161             return false;
162         }
163 
164         Intent intent = preference.getIntent();
165         if (intent != null) {
166             // Find the activity that is in the system image
167             PackageManager pm = context.getPackageManager();
168             List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
169             int listSize = list.size();
170             for (int i = 0; i < listSize; i++) {
171                 ResolveInfo resolveInfo = list.get(i);
172                 if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
173                         != 0) {
174 
175                     // Replace the intent with this specific activity
176                     preference.setIntent(new Intent().setClassName(
177                             resolveInfo.activityInfo.packageName,
178                             resolveInfo.activityInfo.name));
179 
180                     if ((flags & UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY) != 0) {
181                         // Set the preference title to the activity's label
182                         preference.setTitle(resolveInfo.loadLabel(pm));
183                     }
184 
185                     return true;
186                 }
187             }
188         }
189 
190         // Did not find a matching activity, so remove the preference
191         parentPreferenceGroup.removePreference(preference);
192 
193         return false;
194     }
195 
196     /**
197      * Returns the UserManager for a given context
198      *
199      * @throws IllegalStateException if no UserManager could be retrieved.
200      */
getUserManager(Context context)201     public static UserManager getUserManager(Context context) {
202         UserManager um = UserManager.get(context);
203         if (um == null) {
204             throw new IllegalStateException("Unable to load UserManager");
205         }
206         return um;
207     }
208 
209     /**
210      * Returns true if Monkey is running.
211      */
isMonkeyRunning()212     public static boolean isMonkeyRunning() {
213         return ActivityManager.isUserAMonkey();
214     }
215 
216     /**
217      * Returns whether the device is voice-capable (meaning, it is also a phone).
218      */
isVoiceCapable(Context context)219     public static boolean isVoiceCapable(Context context) {
220         TelephonyManager telephony =
221                 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
222         return telephony != null && telephony.isVoiceCapable();
223     }
224 
isWifiOnly(Context context)225     public static boolean isWifiOnly(Context context) {
226         ConnectivityManager cm = (ConnectivityManager)context.getSystemService(
227                 Context.CONNECTIVITY_SERVICE);
228         return (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false);
229     }
230 
231     /**
232      * Returns the WIFI IP Addresses, if any, taking into account IPv4 and IPv6 style addresses.
233      * @param context the application context
234      * @return the formatted and newline-separated IP addresses, or null if none.
235      */
getWifiIpAddresses(Context context)236     public static String getWifiIpAddresses(Context context) {
237         ConnectivityManager cm = (ConnectivityManager)
238                 context.getSystemService(Context.CONNECTIVITY_SERVICE);
239         LinkProperties prop = cm.getLinkProperties(ConnectivityManager.TYPE_WIFI);
240         return formatIpAddresses(prop);
241     }
242 
243     /**
244      * Returns the default link's IP addresses, if any, taking into account IPv4 and IPv6 style
245      * addresses.
246      * @param context the application context
247      * @return the formatted and newline-separated IP addresses, or null if none.
248      */
getDefaultIpAddresses(ConnectivityManager cm)249     public static String getDefaultIpAddresses(ConnectivityManager cm) {
250         LinkProperties prop = cm.getActiveLinkProperties();
251         return formatIpAddresses(prop);
252     }
253 
formatIpAddresses(LinkProperties prop)254     private static String formatIpAddresses(LinkProperties prop) {
255         if (prop == null) return null;
256         Iterator<InetAddress> iter = prop.getAllAddresses().iterator();
257         // If there are no entries, return null
258         if (!iter.hasNext()) return null;
259         // Concatenate all available addresses, comma separated
260         String addresses = "";
261         while (iter.hasNext()) {
262             addresses += iter.next().getHostAddress();
263             if (iter.hasNext()) addresses += "\n";
264         }
265         return addresses;
266     }
267 
createLocaleFromString(String localeStr)268     public static Locale createLocaleFromString(String localeStr) {
269         // TODO: is there a better way to actually construct a locale that will match?
270         // The main problem is, on top of Java specs, locale.toString() and
271         // new Locale(locale.toString()).toString() do not return equal() strings in
272         // many cases, because the constructor takes the only string as the language
273         // code. So : new Locale("en", "US").toString() => "en_US"
274         // And : new Locale("en_US").toString() => "en_us"
275         if (null == localeStr)
276             return Locale.getDefault();
277         String[] brokenDownLocale = localeStr.split("_", 3);
278         // split may not return a 0-length array.
279         if (1 == brokenDownLocale.length) {
280             return new Locale(brokenDownLocale[0]);
281         } else if (2 == brokenDownLocale.length) {
282             return new Locale(brokenDownLocale[0], brokenDownLocale[1]);
283         } else {
284             return new Locale(brokenDownLocale[0], brokenDownLocale[1], brokenDownLocale[2]);
285         }
286     }
287 
isBatteryPresent(Intent batteryChangedIntent)288     public static boolean isBatteryPresent(Intent batteryChangedIntent) {
289         return batteryChangedIntent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true);
290     }
291 
getBatteryPercentage(Intent batteryChangedIntent)292     public static String getBatteryPercentage(Intent batteryChangedIntent) {
293         return formatPercentage(getBatteryLevel(batteryChangedIntent));
294     }
295 
forcePrepareCustomPreferencesList( ViewGroup parent, View child, ListView list, boolean ignoreSidePadding)296     public static void forcePrepareCustomPreferencesList(
297             ViewGroup parent, View child, ListView list, boolean ignoreSidePadding) {
298         list.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);
299         list.setClipToPadding(false);
300         prepareCustomPreferencesList(parent, child, list, ignoreSidePadding);
301     }
302 
303     /**
304      * Prepare a custom preferences layout, moving padding to {@link ListView}
305      * when outside scrollbars are requested. Usually used to display
306      * {@link ListView} and {@link TabWidget} with correct padding.
307      */
prepareCustomPreferencesList( ViewGroup parent, View child, View list, boolean ignoreSidePadding)308     public static void prepareCustomPreferencesList(
309             ViewGroup parent, View child, View list, boolean ignoreSidePadding) {
310         final boolean movePadding = list.getScrollBarStyle() == View.SCROLLBARS_OUTSIDE_OVERLAY;
311         if (movePadding) {
312             final Resources res = list.getResources();
313             final int paddingSide = res.getDimensionPixelSize(R.dimen.settings_side_margin);
314             final int paddingBottom = res.getDimensionPixelSize(
315                     com.android.internal.R.dimen.preference_fragment_padding_bottom);
316 
317             if (parent instanceof PreferenceFrameLayout) {
318                 ((PreferenceFrameLayout.LayoutParams) child.getLayoutParams()).removeBorders = true;
319 
320                 final int effectivePaddingSide = ignoreSidePadding ? 0 : paddingSide;
321                 list.setPaddingRelative(effectivePaddingSide, 0, effectivePaddingSide, paddingBottom);
322             } else {
323                 list.setPaddingRelative(paddingSide, 0, paddingSide, paddingBottom);
324             }
325         }
326     }
327 
forceCustomPadding(View view, boolean additive)328     public static void forceCustomPadding(View view, boolean additive) {
329         final Resources res = view.getResources();
330         final int paddingSide = res.getDimensionPixelSize(R.dimen.settings_side_margin);
331 
332         final int paddingStart = paddingSide + (additive ? view.getPaddingStart() : 0);
333         final int paddingEnd = paddingSide + (additive ? view.getPaddingEnd() : 0);
334         final int paddingBottom = res.getDimensionPixelSize(
335                 com.android.internal.R.dimen.preference_fragment_padding_bottom);
336 
337         view.setPaddingRelative(paddingStart, 0, paddingEnd, paddingBottom);
338     }
339 
340     /* Used by UserSettings as well. Call this on a non-ui thread. */
copyMeProfilePhoto(Context context, UserInfo user)341     public static void copyMeProfilePhoto(Context context, UserInfo user) {
342         Uri contactUri = Profile.CONTENT_URI;
343 
344         int userId = user != null ? user.id : UserHandle.myUserId();
345 
346         InputStream avatarDataStream = Contacts.openContactPhotoInputStream(
347                     context.getContentResolver(),
348                     contactUri, true);
349         // If there's no profile photo, assign a default avatar
350         if (avatarDataStream == null) {
351             assignDefaultPhoto(context, userId);
352             return;
353         }
354 
355         UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
356         Bitmap icon = BitmapFactory.decodeStream(avatarDataStream);
357         um.setUserIcon(userId, icon);
358         try {
359             avatarDataStream.close();
360         } catch (IOException ioe) { }
361     }
362 
assignDefaultPhoto(Context context, int userId)363     public static void assignDefaultPhoto(Context context, int userId) {
364         UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
365         Bitmap bitmap = getDefaultUserIconAsBitmap(userId);
366         um.setUserIcon(userId, bitmap);
367     }
368 
getMeProfileName(Context context, boolean full)369     public static String getMeProfileName(Context context, boolean full) {
370         if (full) {
371             return getProfileDisplayName(context);
372         } else {
373             return getShorterNameIfPossible(context);
374         }
375     }
376 
getShorterNameIfPossible(Context context)377     private static String getShorterNameIfPossible(Context context) {
378         final String given = getLocalProfileGivenName(context);
379         return !TextUtils.isEmpty(given) ? given : getProfileDisplayName(context);
380     }
381 
getLocalProfileGivenName(Context context)382     private static String getLocalProfileGivenName(Context context) {
383         final ContentResolver cr = context.getContentResolver();
384 
385         // Find the raw contact ID for the local ME profile raw contact.
386         final long localRowProfileId;
387         final Cursor localRawProfile = cr.query(
388                 Profile.CONTENT_RAW_CONTACTS_URI,
389                 new String[] {RawContacts._ID},
390                 RawContacts.ACCOUNT_TYPE + " IS NULL AND " +
391                         RawContacts.ACCOUNT_NAME + " IS NULL",
392                 null, null);
393         if (localRawProfile == null) return null;
394 
395         try {
396             if (!localRawProfile.moveToFirst()) {
397                 return null;
398             }
399             localRowProfileId = localRawProfile.getLong(0);
400         } finally {
401             localRawProfile.close();
402         }
403 
404         // Find the structured name for the raw contact.
405         final Cursor structuredName = cr.query(
406                 Profile.CONTENT_URI.buildUpon().appendPath(Contacts.Data.CONTENT_DIRECTORY).build(),
407                 new String[] {CommonDataKinds.StructuredName.GIVEN_NAME,
408                     CommonDataKinds.StructuredName.FAMILY_NAME},
409                 Data.RAW_CONTACT_ID + "=" + localRowProfileId,
410                 null, null);
411         if (structuredName == null) return null;
412 
413         try {
414             if (!structuredName.moveToFirst()) {
415                 return null;
416             }
417             String partialName = structuredName.getString(0);
418             if (TextUtils.isEmpty(partialName)) {
419                 partialName = structuredName.getString(1);
420             }
421             return partialName;
422         } finally {
423             structuredName.close();
424         }
425     }
426 
getProfileDisplayName(Context context)427     private static final String getProfileDisplayName(Context context) {
428         final ContentResolver cr = context.getContentResolver();
429         final Cursor profile = cr.query(Profile.CONTENT_URI,
430                 new String[] {Profile.DISPLAY_NAME}, null, null, null);
431         if (profile == null) return null;
432 
433         try {
434             if (!profile.moveToFirst()) {
435                 return null;
436             }
437             return profile.getString(0);
438         } finally {
439             profile.close();
440         }
441     }
442 
443     /** Not global warming, it's global change warning. */
buildGlobalChangeWarningDialog(final Context context, int titleResId, final Runnable positiveAction)444     public static Dialog buildGlobalChangeWarningDialog(final Context context, int titleResId,
445             final Runnable positiveAction) {
446         final AlertDialog.Builder builder = new AlertDialog.Builder(context);
447         builder.setTitle(titleResId);
448         builder.setMessage(R.string.global_change_warning);
449         builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
450             @Override
451             public void onClick(DialogInterface dialog, int which) {
452                 positiveAction.run();
453             }
454         });
455         builder.setNegativeButton(android.R.string.cancel, null);
456 
457         return builder.create();
458     }
459 
hasMultipleUsers(Context context)460     public static boolean hasMultipleUsers(Context context) {
461         return ((UserManager) context.getSystemService(Context.USER_SERVICE))
462                 .getUsers().size() > 1;
463     }
464 
465     /**
466      * Start a new instance of the activity, showing only the given fragment.
467      * When launched in this mode, the given preference fragment will be instantiated and fill the
468      * entire activity.
469      *
470      * @param context The context.
471      * @param fragmentName The name of the fragment to display.
472      * @param args Optional arguments to supply to the fragment.
473      * @param resultTo Option fragment that should receive the result of the activity launch.
474      * @param resultRequestCode If resultTo is non-null, this is the request code in which
475      *                          to report the result.
476      * @param titleResId resource id for the String to display for the title of this set
477      *                   of preferences.
478      * @param title String to display for the title of this set of preferences.
479      */
startWithFragment(Context context, String fragmentName, Bundle args, Fragment resultTo, int resultRequestCode, int titleResId, CharSequence title)480     public static void startWithFragment(Context context, String fragmentName, Bundle args,
481             Fragment resultTo, int resultRequestCode, int titleResId,
482             CharSequence title) {
483         startWithFragment(context, fragmentName, args, resultTo, resultRequestCode,
484                 null /* titleResPackageName */, titleResId, title, false /* not a shortcut */);
485     }
486 
487     /**
488      * Start a new instance of the activity, showing only the given fragment.
489      * When launched in this mode, the given preference fragment will be instantiated and fill the
490      * entire activity.
491      *
492      * @param context The context.
493      * @param fragmentName The name of the fragment to display.
494      * @param args Optional arguments to supply to the fragment.
495      * @param resultTo Option fragment that should receive the result of the activity launch.
496      * @param resultRequestCode If resultTo is non-null, this is the request code in which
497      *                          to report the result.
498      * @param titleResPackageName Optional package name for the resource id of the title.
499      * @param titleResId resource id for the String to display for the title of this set
500      *                   of preferences.
501      * @param title String to display for the title of this set of preferences.
502      */
startWithFragment(Context context, String fragmentName, Bundle args, Fragment resultTo, int resultRequestCode, String titleResPackageName, int titleResId, CharSequence title)503     public static void startWithFragment(Context context, String fragmentName, Bundle args,
504             Fragment resultTo, int resultRequestCode, String titleResPackageName, int titleResId,
505             CharSequence title) {
506         startWithFragment(context, fragmentName, args, resultTo, resultRequestCode,
507                 titleResPackageName, titleResId, title, false /* not a shortcut */);
508     }
509 
startWithFragment(Context context, String fragmentName, Bundle args, Fragment resultTo, int resultRequestCode, int titleResId, CharSequence title, boolean isShortcut)510     public static void startWithFragment(Context context, String fragmentName, Bundle args,
511             Fragment resultTo, int resultRequestCode, int titleResId,
512             CharSequence title, boolean isShortcut) {
513         Intent intent = onBuildStartFragmentIntent(context, fragmentName, args,
514                 null /* titleResPackageName */, titleResId, title, isShortcut);
515         if (resultTo == null) {
516             context.startActivity(intent);
517         } else {
518             resultTo.startActivityForResult(intent, resultRequestCode);
519         }
520     }
521 
startWithFragment(Context context, String fragmentName, Bundle args, Fragment resultTo, int resultRequestCode, String titleResPackageName, int titleResId, CharSequence title, boolean isShortcut)522     public static void startWithFragment(Context context, String fragmentName, Bundle args,
523             Fragment resultTo, int resultRequestCode, String titleResPackageName, int titleResId,
524             CharSequence title, boolean isShortcut) {
525         Intent intent = onBuildStartFragmentIntent(context, fragmentName, args, titleResPackageName,
526                 titleResId, title, isShortcut);
527         if (resultTo == null) {
528             context.startActivity(intent);
529         } else {
530             resultTo.startActivityForResult(intent, resultRequestCode);
531         }
532     }
533 
startWithFragmentAsUser(Context context, String fragmentName, Bundle args, int titleResId, CharSequence title, boolean isShortcut, UserHandle userHandle)534     public static void startWithFragmentAsUser(Context context, String fragmentName, Bundle args,
535             int titleResId, CharSequence title, boolean isShortcut,
536             UserHandle userHandle) {
537         // workaround to avoid crash in b/17523189
538         if (userHandle.getIdentifier() == UserHandle.myUserId()) {
539             startWithFragment(context, fragmentName, args, null, 0, titleResId, title, isShortcut);
540         } else {
541             Intent intent = onBuildStartFragmentIntent(context, fragmentName, args,
542                     null /* titleResPackageName */, titleResId, title, isShortcut);
543             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
544             intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
545             context.startActivityAsUser(intent, userHandle);
546         }
547     }
548 
startWithFragmentAsUser(Context context, String fragmentName, Bundle args, String titleResPackageName, int titleResId, CharSequence title, boolean isShortcut, UserHandle userHandle)549     public static void startWithFragmentAsUser(Context context, String fragmentName, Bundle args,
550             String titleResPackageName, int titleResId, CharSequence title, boolean isShortcut,
551             UserHandle userHandle) {
552         // workaround to avoid crash in b/17523189
553         if (userHandle.getIdentifier() == UserHandle.myUserId()) {
554             startWithFragment(context, fragmentName, args, null, 0, titleResPackageName, titleResId,
555                     title, isShortcut);
556         } else {
557             Intent intent = onBuildStartFragmentIntent(context, fragmentName, args,
558                     titleResPackageName, titleResId, title, isShortcut);
559             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
560             intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
561             context.startActivityAsUser(intent, userHandle);
562         }
563     }
564 
565     /**
566      * Build an Intent to launch a new activity showing the selected fragment.
567      * The implementation constructs an Intent that re-launches the current activity with the
568      * appropriate arguments to display the fragment.
569      *
570      *
571      * @param context The Context.
572      * @param fragmentName The name of the fragment to display.
573      * @param args Optional arguments to supply to the fragment.
574      * @param titleResPackageName Optional package name for the resource id of the title.
575      * @param titleResId Optional title resource id to show for this item.
576      * @param title Optional title to show for this item.
577      * @param isShortcut  tell if this is a Launcher Shortcut or not
578      * @return Returns an Intent that can be launched to display the given
579      * fragment.
580      */
onBuildStartFragmentIntent(Context context, String fragmentName, Bundle args, String titleResPackageName, int titleResId, CharSequence title, boolean isShortcut)581     public static Intent onBuildStartFragmentIntent(Context context, String fragmentName,
582             Bundle args, String titleResPackageName, int titleResId, CharSequence title,
583             boolean isShortcut) {
584         Intent intent = new Intent(Intent.ACTION_MAIN);
585         intent.setClass(context, SubSettings.class);
586         intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, fragmentName);
587         intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
588         intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME,
589                 titleResPackageName);
590         intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, titleResId);
591         intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE, title);
592         intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, isShortcut);
593         return intent;
594     }
595 
596     /**
597      * Returns the managed profile of the current user or null if none found.
598      */
getManagedProfile(UserManager userManager)599     public static UserHandle getManagedProfile(UserManager userManager) {
600         List<UserHandle> userProfiles = userManager.getUserProfiles();
601         final int count = userProfiles.size();
602         for (int i = 0; i < count; i++) {
603             final UserHandle profile = userProfiles.get(i);
604             if (profile.getIdentifier() == userManager.getUserHandle()) {
605                 continue;
606             }
607             final UserInfo userInfo = userManager.getUserInfo(profile.getIdentifier());
608             if (userInfo.isManagedProfile()) {
609                 return profile;
610             }
611         }
612         return null;
613     }
614 
615     /**
616      * Returns true if the current profile is a managed one.
617      *
618      * @throws IllegalArgumentException if userManager is null.
619      */
isManagedProfile(@onNull UserManager userManager)620     public static boolean isManagedProfile(@NonNull UserManager userManager) {
621         return isManagedProfile(userManager, UserHandle.myUserId());
622     }
623 
624     /**
625      * Retrieves the id for the given user's managed profile.
626      *
627      * @return the managed profile id or UserHandle.USER_NULL if there is none.
628      */
getManagedProfileId(UserManager um, int parentUserId)629     public static int getManagedProfileId(UserManager um, int parentUserId) {
630         int[] profileIds = um.getProfileIdsWithDisabled(parentUserId);
631         for (int profileId : profileIds) {
632             if (profileId != parentUserId) {
633                 return profileId;
634             }
635         }
636         return UserHandle.USER_NULL;
637     }
638 
639     /**
640      * Returns true if the userId passed in is a managed profile.
641      *
642      * @throws IllegalArgumentException if userManager is null.
643      */
isManagedProfile(@onNull UserManager userManager, int userId)644     public static boolean isManagedProfile(@NonNull UserManager userManager, int userId) {
645         if (userManager == null) {
646             throw new IllegalArgumentException("userManager must not be null");
647         }
648         UserInfo userInfo = userManager.getUserInfo(userId);
649         return (userInfo != null) ? userInfo.isManagedProfile() : false;
650     }
651 
652     /**
653      * Returns the target user for a Settings activity.
654      *
655      * The target user can be either the current user, the user that launched this activity or
656      * the user contained as an extra in the arguments or intent extras.
657      *
658      * Note: This is secure in the sense that it only returns a target user different to the current
659      * one if the app launching this activity is the Settings app itself, running in the same user
660      * or in one that is in the same profile group, or if the user id is provided by the system.
661      */
getSecureTargetUser(IBinder activityToken, UserManager um, @Nullable Bundle arguments, @Nullable Bundle intentExtras)662     public static UserHandle getSecureTargetUser(IBinder activityToken,
663            UserManager um, @Nullable Bundle arguments, @Nullable Bundle intentExtras) {
664         UserHandle currentUser = new UserHandle(UserHandle.myUserId());
665         IActivityManager am = ActivityManagerNative.getDefault();
666         try {
667             String launchedFromPackage = am.getLaunchedFromPackage(activityToken);
668             boolean launchedFromSettingsApp = SETTINGS_PACKAGE_NAME.equals(launchedFromPackage);
669 
670             UserHandle launchedFromUser = new UserHandle(UserHandle.getUserId(
671                     am.getLaunchedFromUid(activityToken)));
672             if (launchedFromUser != null && !launchedFromUser.equals(currentUser)) {
673                 // Check it's secure
674                 if (isProfileOf(um, launchedFromUser)) {
675                     return launchedFromUser;
676                 }
677             }
678             UserHandle extrasUser = intentExtras != null
679                     ? (UserHandle) intentExtras.getParcelable(EXTRA_USER) : null;
680             if (extrasUser != null && !extrasUser.equals(currentUser)) {
681                 // Check it's secure
682                 if (launchedFromSettingsApp && isProfileOf(um, extrasUser)) {
683                     return extrasUser;
684                 }
685             }
686             UserHandle argumentsUser = arguments != null
687                     ? (UserHandle) arguments.getParcelable(EXTRA_USER) : null;
688             if (argumentsUser != null && !argumentsUser.equals(currentUser)) {
689                 // Check it's secure
690                 if (launchedFromSettingsApp && isProfileOf(um, argumentsUser)) {
691                     return argumentsUser;
692                 }
693             }
694         } catch (RemoteException e) {
695             // Should not happen
696             Log.v(TAG, "Could not talk to activity manager.", e);
697         }
698         return currentUser;
699    }
700 
701     /**
702      * Returns the target user for a Settings activity.
703      *
704      * The target user can be either the current user, the user that launched this activity or
705      * the user contained as an extra in the arguments or intent extras.
706      *
707      * You should use {@link #getSecureTargetUser(IBinder, UserManager, Bundle, Bundle)} if
708      * possible.
709      *
710      * @see #getInsecureTargetUser(IBinder, Bundle, Bundle)
711      */
getInsecureTargetUser(IBinder activityToken, @Nullable Bundle arguments, @Nullable Bundle intentExtras)712    public static UserHandle getInsecureTargetUser(IBinder activityToken, @Nullable Bundle arguments,
713            @Nullable Bundle intentExtras) {
714        UserHandle currentUser = new UserHandle(UserHandle.myUserId());
715        IActivityManager am = ActivityManagerNative.getDefault();
716        try {
717            UserHandle launchedFromUser = new UserHandle(UserHandle.getUserId(
718                    am.getLaunchedFromUid(activityToken)));
719            if (launchedFromUser != null && !launchedFromUser.equals(currentUser)) {
720                return launchedFromUser;
721            }
722            UserHandle extrasUser = intentExtras != null
723                    ? (UserHandle) intentExtras.getParcelable(EXTRA_USER) : null;
724            if (extrasUser != null && !extrasUser.equals(currentUser)) {
725                return extrasUser;
726            }
727            UserHandle argumentsUser = arguments != null
728                    ? (UserHandle) arguments.getParcelable(EXTRA_USER) : null;
729            if (argumentsUser != null && !argumentsUser.equals(currentUser)) {
730                return argumentsUser;
731            }
732        } catch (RemoteException e) {
733            // Should not happen
734            Log.v(TAG, "Could not talk to activity manager.", e);
735            return null;
736        }
737        return currentUser;
738    }
739 
740    /**
741     * Returns true if the user provided is in the same profiles group as the current user.
742     */
isProfileOf(UserManager um, UserHandle otherUser)743    private static boolean isProfileOf(UserManager um, UserHandle otherUser) {
744        if (um == null || otherUser == null) return false;
745        return (UserHandle.myUserId() == otherUser.getIdentifier())
746                || um.getUserProfiles().contains(otherUser);
747    }
748 
749 
750     /**
751      * Returns whether or not this device is able to be OEM unlocked.
752      */
isOemUnlockEnabled(Context context)753     static boolean isOemUnlockEnabled(Context context) {
754         PersistentDataBlockManager manager =(PersistentDataBlockManager)
755                 context.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
756         return manager.getOemUnlockEnabled();
757     }
758 
759     /**
760      * Allows enabling or disabling OEM unlock on this device. OEM unlocked
761      * devices allow users to flash other OSes to them.
762      */
setOemUnlockEnabled(Context context, boolean enabled)763     static void setOemUnlockEnabled(Context context, boolean enabled) {
764         PersistentDataBlockManager manager =(PersistentDataBlockManager)
765                 context.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
766         manager.setOemUnlockEnabled(enabled);
767     }
768 
769     /**
770      * Return whether or not the user should have a SIM Cards option in Settings.
771      * TODO: Change back to returning true if count is greater than one after testing.
772      * TODO: See bug 16533525.
773      */
showSimCardTile(Context context)774     public static boolean showSimCardTile(Context context) {
775         final TelephonyManager tm =
776                 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
777 
778         return tm.getSimCount() > 1;
779     }
780 
781     /**
782      * Returns elapsed time for the given millis, in the following format:
783      * 2d 5h 40m 29s
784      * @param context the application context
785      * @param millis the elapsed time in milli seconds
786      * @param withSeconds include seconds?
787      * @return the formatted elapsed time
788      */
formatElapsedTime(Context context, double millis, boolean withSeconds)789     public static String formatElapsedTime(Context context, double millis, boolean withSeconds) {
790         StringBuilder sb = new StringBuilder();
791         int seconds = (int) Math.floor(millis / 1000);
792         if (!withSeconds) {
793             // Round up.
794             seconds += 30;
795         }
796 
797         int days = 0, hours = 0, minutes = 0;
798         if (seconds >= SECONDS_PER_DAY) {
799             days = seconds / SECONDS_PER_DAY;
800             seconds -= days * SECONDS_PER_DAY;
801         }
802         if (seconds >= SECONDS_PER_HOUR) {
803             hours = seconds / SECONDS_PER_HOUR;
804             seconds -= hours * SECONDS_PER_HOUR;
805         }
806         if (seconds >= SECONDS_PER_MINUTE) {
807             minutes = seconds / SECONDS_PER_MINUTE;
808             seconds -= minutes * SECONDS_PER_MINUTE;
809         }
810         if (withSeconds) {
811             if (days > 0) {
812                 sb.append(context.getString(R.string.battery_history_days,
813                         days, hours, minutes, seconds));
814             } else if (hours > 0) {
815                 sb.append(context.getString(R.string.battery_history_hours,
816                         hours, minutes, seconds));
817             } else if (minutes > 0) {
818                 sb.append(context.getString(R.string.battery_history_minutes, minutes, seconds));
819             } else {
820                 sb.append(context.getString(R.string.battery_history_seconds, seconds));
821             }
822         } else {
823             if (days > 0) {
824                 sb.append(context.getString(R.string.battery_history_days_no_seconds,
825                         days, hours, minutes));
826             } else if (hours > 0) {
827                 sb.append(context.getString(R.string.battery_history_hours_no_seconds,
828                         hours, minutes));
829             } else {
830                 sb.append(context.getString(R.string.battery_history_minutes_no_seconds, minutes));
831             }
832         }
833         return sb.toString();
834     }
835 
836     /**
837      * Queries for the UserInfo of a user. Returns null if the user doesn't exist (was removed).
838      * @param userManager Instance of UserManager
839      * @param checkUser The user to check the existence of.
840      * @return UserInfo of the user or null for non-existent user.
841      */
getExistingUser(UserManager userManager, UserHandle checkUser)842     public static UserInfo getExistingUser(UserManager userManager, UserHandle checkUser) {
843         final List<UserInfo> users = userManager.getUsers(true /* excludeDying */);
844         final int checkUserId = checkUser.getIdentifier();
845         for (UserInfo user : users) {
846             if (user.id == checkUserId) {
847                 return user;
848             }
849         }
850         return null;
851     }
852 
inflateCategoryHeader(LayoutInflater inflater, ViewGroup parent)853     public static View inflateCategoryHeader(LayoutInflater inflater, ViewGroup parent) {
854         final TypedArray a = inflater.getContext().obtainStyledAttributes(null,
855                 com.android.internal.R.styleable.Preference,
856                 com.android.internal.R.attr.preferenceCategoryStyle, 0);
857         final int resId = a.getResourceId(com.android.internal.R.styleable.Preference_layout,
858                 0);
859         a.recycle();
860         return inflater.inflate(resId, parent, false);
861     }
862 
863     /**
864      * Return if we are running low on storage space or not.
865      *
866      * @param context The context
867      * @return true if we are running low on storage space
868      */
isLowStorage(Context context)869     public static boolean isLowStorage(Context context) {
870         final StorageManager sm = StorageManager.from(context);
871         return (sm.getStorageBytesUntilLow(context.getFilesDir()) < 0);
872     }
873 
874     /**
875      * Returns a default user icon (as a {@link Bitmap}) for the given user.
876      *
877      * Note that for guest users, you should pass in {@code UserHandle.USER_NULL}.
878      * @param userId the user id or {@code UserHandle.USER_NULL} for a non-user specific icon
879      */
getDefaultUserIconAsBitmap(int userId)880     public static Bitmap getDefaultUserIconAsBitmap(int userId) {
881         Bitmap bitmap = null;
882         // Try finding the corresponding bitmap in the dark bitmap cache
883         bitmap = sDarkDefaultUserBitmapCache.get(userId);
884         if (bitmap == null) {
885             bitmap = UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(userId, false));
886             // Save it to cache
887             sDarkDefaultUserBitmapCache.put(userId, bitmap);
888         }
889         return bitmap;
890     }
891 
hasPreferredActivities(PackageManager pm, String packageName)892     public static boolean hasPreferredActivities(PackageManager pm, String packageName) {
893         // Get list of preferred activities
894         List<ComponentName> prefActList = new ArrayList<>();
895         // Intent list cannot be null. so pass empty list
896         List<IntentFilter> intentList = new ArrayList<>();
897         pm.getPreferredActivities(intentList, prefActList, packageName);
898         Log.d(TAG, "Have " + prefActList.size() + " number of activities in preferred list");
899         return prefActList.size() > 0;
900     }
901 
getHandledDomains(PackageManager pm, String packageName)902     public static ArraySet<String> getHandledDomains(PackageManager pm, String packageName) {
903         List<IntentFilterVerificationInfo> iviList = pm.getIntentFilterVerifications(packageName);
904         List<IntentFilter> filters = pm.getAllIntentFilters(packageName);
905 
906         ArraySet<String> result = new ArraySet<>();
907         if (iviList.size() > 0) {
908             for (IntentFilterVerificationInfo ivi : iviList) {
909                 for (String host : ivi.getDomains()) {
910                     result.add(host);
911                 }
912             }
913         }
914         if (filters != null && filters.size() > 0) {
915             for (IntentFilter filter : filters) {
916                 if (filter.hasCategory(Intent.CATEGORY_BROWSABLE)
917                         && (filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
918                                 filter.hasDataScheme(IntentFilter.SCHEME_HTTPS))) {
919                     result.addAll(filter.getHostsList());
920                 }
921             }
922         }
923         return result;
924     }
925 
handleLoadingContainer(View loading, View doneLoading, boolean done, boolean animate)926     public static void handleLoadingContainer(View loading, View doneLoading, boolean done,
927             boolean animate) {
928         setViewShown(loading, !done, animate);
929         setViewShown(doneLoading, done, animate);
930     }
931 
setViewShown(final View view, boolean shown, boolean animate)932     private static void setViewShown(final View view, boolean shown, boolean animate) {
933         if (animate) {
934             Animation animation = AnimationUtils.loadAnimation(view.getContext(),
935                     shown ? android.R.anim.fade_in : android.R.anim.fade_out);
936             if (shown) {
937                 view.setVisibility(View.VISIBLE);
938             } else {
939                 animation.setAnimationListener(new AnimationListener() {
940                     @Override
941                     public void onAnimationStart(Animation animation) {
942                     }
943 
944                     @Override
945                     public void onAnimationRepeat(Animation animation) {
946                     }
947 
948                     @Override
949                     public void onAnimationEnd(Animation animation) {
950                         view.setVisibility(View.INVISIBLE);
951                     }
952                 });
953             }
954             view.startAnimation(animation);
955         } else {
956             view.clearAnimation();
957             view.setVisibility(shown ? View.VISIBLE : View.INVISIBLE);
958         }
959     }
960 
961     /**
962      * Returns the application info of the currently installed MDM package.
963      */
getAdminApplicationInfo(Context context, int profileId)964     public static ApplicationInfo getAdminApplicationInfo(Context context, int profileId) {
965         DevicePolicyManager dpm =
966                 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
967         ComponentName mdmPackage = dpm.getProfileOwnerAsUser(profileId);
968         if (mdmPackage == null) {
969             return null;
970         }
971         String mdmPackageName = mdmPackage.getPackageName();
972         try {
973             IPackageManager ipm = AppGlobals.getPackageManager();
974             ApplicationInfo mdmApplicationInfo =
975                     ipm.getApplicationInfo(mdmPackageName, 0, profileId);
976             return mdmApplicationInfo;
977         } catch (RemoteException e) {
978             Log.e(TAG, "Error while retrieving application info for package " + mdmPackageName
979                     + ", userId " + profileId, e);
980             return null;
981         }
982     }
983 
isBandwidthControlEnabled()984     public static boolean isBandwidthControlEnabled() {
985         final INetworkManagementService netManager = INetworkManagementService.Stub
986                 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
987         try {
988             return netManager.isBandwidthControlEnabled();
989         } catch (RemoteException e) {
990             return false;
991         }
992     }
993 
994     /**
995      * Returns an accessible SpannableString.
996      * @param displayText the text to display
997      * @param accessibileText the text text-to-speech engines should read
998      */
createAccessibleSequence(CharSequence displayText, String accessibileText)999     public static SpannableString createAccessibleSequence(CharSequence displayText,
1000             String accessibileText) {
1001         SpannableString str = new SpannableString(displayText);
1002         str.setSpan(new TtsSpan.TextBuilder(accessibileText).build(), 0,
1003                 displayText.length(),
1004                 Spannable.SPAN_INCLUSIVE_INCLUSIVE);
1005         return str;
1006     }
1007 
1008     /**
1009      * Returns the user id present in the bundle with {@link Intent#EXTRA_USER_ID} if it
1010      * belongs to the current user.
1011      *
1012      * @throws SecurityException if the given userId does not belong to the current user group.
1013      */
getUserIdFromBundle(Context context, Bundle bundle)1014     public static int getUserIdFromBundle(Context context, Bundle bundle) {
1015         if (bundle == null) {
1016             return getCredentialOwnerUserId(context);
1017         }
1018         int userId = bundle.getInt(Intent.EXTRA_USER_ID, UserHandle.myUserId());
1019         return enforceSameOwner(context, userId);
1020     }
1021 
1022     /**
1023      * Returns the given user id if it belongs to the current user.
1024      *
1025      * @throws SecurityException if the given userId does not belong to the current user group.
1026      */
enforceSameOwner(Context context, int userId)1027     public static int enforceSameOwner(Context context, int userId) {
1028         final UserManager um = getUserManager(context);
1029         final int[] profileIds = um.getProfileIdsWithDisabled(UserHandle.myUserId());
1030         if (ArrayUtils.contains(profileIds, userId)) {
1031             return userId;
1032         }
1033         throw new SecurityException("Given user id " + userId + " does not belong to user "
1034                 + UserHandle.myUserId());
1035     }
1036 
1037     /**
1038      * Returns the effective credential owner of the calling user.
1039      */
getCredentialOwnerUserId(Context context)1040     public static int getCredentialOwnerUserId(Context context) {
1041         return getCredentialOwnerUserId(context, UserHandle.myUserId());
1042     }
1043 
1044     /**
1045      * Returns the user id of the credential owner of the given user id.
1046      */
getCredentialOwnerUserId(Context context, int userId)1047     public static int getCredentialOwnerUserId(Context context, int userId) {
1048         UserManager um = getUserManager(context);
1049         return um.getCredentialOwnerProfile(userId);
1050     }
1051 
resolveResource(Context context, int attr)1052     public static int resolveResource(Context context, int attr) {
1053         TypedValue value = new TypedValue();
1054         context.getTheme().resolveAttribute(attr, value, true);
1055         return value.resourceId;
1056     }
1057 
1058     private static final StringBuilder sBuilder = new StringBuilder(50);
1059     private static final java.util.Formatter sFormatter = new java.util.Formatter(
1060             sBuilder, Locale.getDefault());
1061 
formatDateRange(Context context, long start, long end)1062     public static String formatDateRange(Context context, long start, long end) {
1063         final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
1064 
1065         synchronized (sBuilder) {
1066             sBuilder.setLength(0);
1067             return DateUtils.formatDateRange(context, sFormatter, start, end, flags, null)
1068                     .toString();
1069         }
1070     }
1071 
getNonIndexable(int xml, Context context)1072     public static List<String> getNonIndexable(int xml, Context context) {
1073         if (Looper.myLooper() == null) {
1074             // Hack to make sure Preferences can initialize.  Prefs expect a looper, but
1075             // don't actually use it for the basic stuff here.
1076             Looper.prepare();
1077         }
1078         final List<String> ret = new ArrayList<>();
1079         PreferenceManager manager = new PreferenceManager(context);
1080         PreferenceScreen screen = manager.inflateFromResource(context, xml, null);
1081         checkPrefs(screen, ret);
1082 
1083         return ret;
1084     }
1085 
checkPrefs(PreferenceGroup group, List<String> ret)1086     private static void checkPrefs(PreferenceGroup group, List<String> ret) {
1087         if (group == null) return;
1088         for (int i = 0; i < group.getPreferenceCount(); i++) {
1089             Preference pref = group.getPreference(i);
1090             if (pref instanceof SelfAvailablePreference
1091                     && !((SelfAvailablePreference) pref).isAvailable(group.getContext())) {
1092                 ret.add(pref.getKey());
1093                 if (pref instanceof PreferenceGroup) {
1094                     addAll((PreferenceGroup) pref, ret);
1095                 }
1096             } else if (pref instanceof PreferenceGroup) {
1097                 checkPrefs((PreferenceGroup) pref, ret);
1098             }
1099         }
1100     }
1101 
addAll(PreferenceGroup group, List<String> ret)1102     private static void addAll(PreferenceGroup group, List<String> ret) {
1103         if (group == null) return;
1104         for (int i = 0; i < group.getPreferenceCount(); i++) {
1105             Preference pref = group.getPreference(i);
1106             ret.add(pref.getKey());
1107             if (pref instanceof PreferenceGroup) {
1108                 addAll((PreferenceGroup) pref, ret);
1109             }
1110         }
1111     }
1112 
isDeviceProvisioned(Context context)1113     public static boolean isDeviceProvisioned(Context context) {
1114         return Settings.Global.getInt(context.getContentResolver(),
1115                 Settings.Global.DEVICE_PROVISIONED, 0) != 0;
1116     }
1117 
startQuietModeDialogIfNecessary(Context context, UserManager um, int userId)1118     public static boolean startQuietModeDialogIfNecessary(Context context, UserManager um,
1119             int userId) {
1120         if (um.isQuietModeEnabled(UserHandle.of(userId))) {
1121             final Intent intent = UnlaunchableAppActivity.createInQuietModeDialogIntent(userId);
1122             context.startActivity(intent);
1123             return true;
1124         }
1125         return false;
1126     }
1127 
getApplicationLabel(Context context, String packageName)1128     public static CharSequence getApplicationLabel(Context context, String packageName) {
1129         try {
1130             final ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
1131                     packageName,
1132                     PackageManager.MATCH_DISABLED_COMPONENTS
1133                     | PackageManager.MATCH_UNINSTALLED_PACKAGES);
1134             return appInfo.loadLabel(context.getPackageManager());
1135         } catch (PackageManager.NameNotFoundException e) {
1136             Log.w(TAG, "Unable to find info for package: " + packageName);
1137         }
1138         return null;
1139     }
1140 
isPackageEnabled(Context context, String packageName)1141     public static boolean isPackageEnabled(Context context, String packageName) {
1142         try {
1143             return context.getPackageManager().getApplicationInfo(packageName, 0).enabled;
1144         } catch (NameNotFoundException e) {
1145             // Thrown by PackageManager.getApplicationInfo if the package does not exist
1146         }
1147         return false;
1148     }
1149 }
1150 
1151