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