2 * Copyright (C) 2007 Google Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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
17 package com.android.settings;
19 import static android.content.Intent.EXTRA_USER;
21 import android.annotation.Nullable;
22 import android.app.ActivityManager;
23 import android.app.ActivityManagerNative;
24 import android.app.AlertDialog;
25 import android.app.AppGlobals;
26 import android.app.Dialog;
27 import android.app.Fragment;
28 import android.app.IActivityManager;
29 import android.app.admin.DevicePolicyManager;
30 import android.content.ComponentName;
31 import android.content.ContentResolver;
32 import android.content.Context;
33 import android.content.DialogInterface;
34 import android.content.Intent;
35 import android.content.IntentFilter;
36 import android.content.pm.ApplicationInfo;
37 import android.content.pm.IPackageManager;
38 import android.content.pm.PackageInfo;
39 import android.content.pm.PackageManager;
40 import android.content.pm.PackageManager.NameNotFoundException;
41 import android.content.pm.ResolveInfo;
42 import android.content.pm.Signature;
43 import android.content.pm.UserInfo;
44 import android.content.res.Resources;
45 import android.content.res.Resources.NotFoundException;
46 import android.content.res.TypedArray;
47 import android.database.Cursor;
48 import android.graphics.Bitmap;
49 import android.graphics.BitmapFactory;
50 import android.graphics.drawable.Drawable;
51 import android.hardware.usb.IUsbManager;
52 import android.net.ConnectivityManager;
53 import android.net.LinkProperties;
54 import android.net.Uri;
55 import android.os.BatteryManager;
56 import android.os.Bundle;
57 import android.os.IBinder;
58 import android.os.INetworkManagementService;
59 import android.os.RemoteException;
60 import android.os.ServiceManager;
61 import android.os.UserHandle;
62 import android.os.UserManager;
63 import android.os.storage.StorageManager;
64 import android.preference.Preference;
65 import android.preference.PreferenceFrameLayout;
66 import android.preference.PreferenceGroup;
67 import android.provider.ContactsContract.CommonDataKinds;
68 import android.provider.ContactsContract.Contacts;
69 import android.provider.ContactsContract.Data;
70 import android.provider.ContactsContract.Profile;
71 import android.provider.ContactsContract.RawContacts;
72 import android.service.persistentdata.PersistentDataBlockManager;
73 import android.telephony.SubscriptionInfo;
74 import android.telephony.SubscriptionManager;
75 import android.telephony.TelephonyManager;
76 import android.text.TextUtils;
77 import android.util.Log;
78 import android.util.SparseArray;
79 import android.view.LayoutInflater;
80 import android.view.View;
81 import android.view.ViewGroup;
82 import android.view.animation.Animation;
83 import android.view.animation.Animation.AnimationListener;
84 import android.view.animation.AnimationUtils;
85 import android.widget.ListView;
86 import android.widget.TabWidget;
88 import com.android.internal.util.UserIcons;
89 import com.android.settings.UserAdapter.UserDetails;
90 import com.android.settings.applications.ApplicationsState;
91 import com.android.settings.dashboard.DashboardTile;
92 import com.android.settings.drawable.CircleFramedDrawable;
94 import java.io.IOException;
95 import java.io.InputStream;
96 import java.net.InetAddress;
97 import java.text.NumberFormat;
98 import java.util.ArrayList;
99 import java.util.Collections;
100 import java.util.Iterator;
101 import java.util.List;
102 import java.util.Locale;
104 public final class Utils {
105 private static final String TAG = "Settings";
108 * Set the preference's title to the matching activity's label.
110 public static final int UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY = 1;
113 * The opacity level of a disabled icon.
115 public static final float DISABLED_ALPHA = 0.4f;
118 * Color spectrum to use to indicate badness. 0 is completely transparent (no data),
119 * 1 is most bad (red), the last value is least bad (green).
121 public static final int[] BADNESS_COLORS = new int[] {
122 0x00000000, 0xffc43828, 0xffe54918, 0xfff47b00,
123 0xfffabf2c, 0xff679e37, 0xff0a7f42
127 * Name of the meta-data item that should be set in the AndroidManifest.xml
128 * to specify the icon that should be displayed for the preference.
130 public static final String META_DATA_PREFERENCE_ICON = "com.android.settings.icon";
133 * Name of the meta-data item that should be set in the AndroidManifest.xml
134 * to specify the title that should be displayed for the preference.
136 public static final String META_DATA_PREFERENCE_TITLE = "com.android.settings.title";
139 * Name of the meta-data item that should be set in the AndroidManifest.xml
140 * to specify the summary text that should be displayed for the preference.
142 public static final String META_DATA_PREFERENCE_SUMMARY = "com.android.settings.summary";
144 private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
146 private static final int SECONDS_PER_MINUTE = 60;
147 private static final int SECONDS_PER_HOUR = 60 * 60;
148 private static final int SECONDS_PER_DAY = 24 * 60 * 60;
150 private static SparseArray<Bitmap> sDarkDefaultUserBitmapCache = new SparseArray<Bitmap>();
153 * Finds a matching activity for a preference's intent. If a matching
154 * activity is not found, it will remove the preference.
156 * @param context The context.
157 * @param parentPreferenceGroup The preference group that contains the
158 * preference whose intent is being resolved.
159 * @param preferenceKey The key of the preference whose intent is being
161 * @param flags 0 or one or more of
162 * {@link #UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY}
164 * @return Whether an activity was found. If false, the preference was
167 public static boolean updatePreferenceToSpecificActivityOrRemove(Context context,
168 PreferenceGroup parentPreferenceGroup, String preferenceKey, int flags) {
170 Preference preference = parentPreferenceGroup.findPreference(preferenceKey);
171 if (preference == null) {
175 Intent intent = preference.getIntent();
176 if (intent != null) {
177 // Find the activity that is in the system image
178 PackageManager pm = context.getPackageManager();
179 List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
180 int listSize = list.size();
181 for (int i = 0; i < listSize; i++) {
182 ResolveInfo resolveInfo = list.get(i);
183 if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
186 // Replace the intent with this specific activity
187 preference.setIntent(new Intent().setClassName(
188 resolveInfo.activityInfo.packageName,
189 resolveInfo.activityInfo.name));
191 if ((flags & UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY) != 0) {
192 // Set the preference title to the activity's label
193 preference.setTitle(resolveInfo.loadLabel(pm));
201 // Did not find a matching activity, so remove the preference
202 parentPreferenceGroup.removePreference(preference);
207 public static boolean updateTileToSpecificActivityFromMetaDataOrRemove(Context context,
208 DashboardTile tile) {
210 Intent intent = tile.intent;
211 if (intent != null) {
212 // Find the activity that is in the system image
213 PackageManager pm = context.getPackageManager();
214 List<ResolveInfo> list = tile.userHandle.size() != 0
215 ? pm.queryIntentActivitiesAsUser(intent, PackageManager.GET_META_DATA,
216 tile.userHandle.get(0).getIdentifier())
217 : pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);
218 int listSize = list.size();
219 for (int i = 0; i < listSize; i++) {
220 ResolveInfo resolveInfo = list.get(i);
221 if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
224 CharSequence title = null;
225 String summary = null;
227 // Get the activity's meta-data
229 Resources res = pm.getResourcesForApplication(
230 resolveInfo.activityInfo.packageName);
231 Bundle metaData = resolveInfo.activityInfo.metaData;
233 if (res != null && metaData != null) {
234 if (metaData.containsKey(META_DATA_PREFERENCE_ICON)) {
235 icon = metaData.getInt(META_DATA_PREFERENCE_ICON);
237 if (metaData.containsKey(META_DATA_PREFERENCE_TITLE)) {
238 title = res.getString(metaData.getInt(META_DATA_PREFERENCE_TITLE));
240 if (metaData.containsKey(META_DATA_PREFERENCE_SUMMARY)) {
241 summary = res.getString(
242 metaData.getInt(META_DATA_PREFERENCE_SUMMARY));
245 } catch (NameNotFoundException | NotFoundException e) {
249 // Set the preference title to the activity's label if no
250 // meta-data is found
251 if (TextUtils.isEmpty(title)) {
252 title = resolveInfo.loadLabel(pm).toString();
255 icon = resolveInfo.activityInfo.icon;
258 // Set icon, title and summary for the preference
260 tile.iconPkg = resolveInfo.activityInfo.packageName;
262 tile.summary = summary;
263 // Replace the intent with this specific activity
264 tile.intent = new Intent().setClassName(resolveInfo.activityInfo.packageName,
265 resolveInfo.activityInfo.name);
276 * Returns true if Monkey is running.
278 public static boolean isMonkeyRunning() {
279 return ActivityManager.isUserAMonkey();
283 * Returns whether the device is voice-capable (meaning, it is also a phone).
285 public static boolean isVoiceCapable(Context context) {
286 TelephonyManager telephony =
287 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
288 return telephony != null && telephony.isVoiceCapable();
291 public static boolean isWifiOnly(Context context) {
292 ConnectivityManager cm = (ConnectivityManager)context.getSystemService(
293 Context.CONNECTIVITY_SERVICE);
294 return (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false);
298 * Returns the WIFI IP Addresses, if any, taking into account IPv4 and IPv6 style addresses.
299 * @param context the application context
300 * @return the formatted and newline-separated IP addresses, or null if none.
302 public static String getWifiIpAddresses(Context context) {
303 ConnectivityManager cm = (ConnectivityManager)
304 context.getSystemService(Context.CONNECTIVITY_SERVICE);
305 LinkProperties prop = cm.getLinkProperties(ConnectivityManager.TYPE_WIFI);
306 return formatIpAddresses(prop);
310 * Returns the default link's IP addresses, if any, taking into account IPv4 and IPv6 style
312 * @param context the application context
313 * @return the formatted and newline-separated IP addresses, or null if none.
315 public static String getDefaultIpAddresses(ConnectivityManager cm) {
316 LinkProperties prop = cm.getActiveLinkProperties();
317 return formatIpAddresses(prop);
320 private static String formatIpAddresses(LinkProperties prop) {
321 if (prop == null) return null;
322 Iterator<InetAddress> iter = prop.getAllAddresses().iterator();
323 // If there are no entries, return null
324 if (!iter.hasNext()) return null;
325 // Concatenate all available addresses, comma separated
326 String addresses = "";
327 while (iter.hasNext()) {
328 addresses += iter.next().getHostAddress();
329 if (iter.hasNext()) addresses += "\n";
334 public static Locale createLocaleFromString(String localeStr) {
335 // TODO: is there a better way to actually construct a locale that will match?
336 // The main problem is, on top of Java specs, locale.toString() and
337 // new Locale(locale.toString()).toString() do not return equal() strings in
338 // many cases, because the constructor takes the only string as the language
339 // code. So : new Locale("en", "US").toString() => "en_US"
340 // And : new Locale("en_US").toString() => "en_us"
341 if (null == localeStr)
342 return Locale.getDefault();
343 String[] brokenDownLocale = localeStr.split("_", 3);
344 // split may not return a 0-length array.
345 if (1 == brokenDownLocale.length) {
346 return new Locale(brokenDownLocale[0]);
347 } else if (2 == brokenDownLocale.length) {
348 return new Locale(brokenDownLocale[0], brokenDownLocale[1]);
350 return new Locale(brokenDownLocale[0], brokenDownLocale[1], brokenDownLocale[2]);
354 /** Formats the ratio of amount/total as a percentage. */
355 public static String formatPercentage(long amount, long total) {
356 return formatPercentage(((double) amount) / total);
359 /** Formats an integer from 0..100 as a percentage. */
360 public static String formatPercentage(int percentage) {
361 return formatPercentage(((double) percentage) / 100.0);
364 /** Formats a double from 0.0..1.0 as a percentage. */
365 private static String formatPercentage(double percentage) {
366 return NumberFormat.getPercentInstance().format(percentage);
369 public static boolean isBatteryPresent(Intent batteryChangedIntent) {
370 return batteryChangedIntent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true);
373 public static String getBatteryPercentage(Intent batteryChangedIntent) {
374 return formatPercentage(getBatteryLevel(batteryChangedIntent));
377 public static int getBatteryLevel(Intent batteryChangedIntent) {
378 int level = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
379 int scale = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 100);
380 return (level * 100) / scale;
383 public static String getBatteryStatus(Resources res, Intent batteryChangedIntent) {
384 final Intent intent = batteryChangedIntent;
386 int plugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
387 int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
388 BatteryManager.BATTERY_STATUS_UNKNOWN);
390 if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
392 if (plugType == BatteryManager.BATTERY_PLUGGED_AC) {
393 resId = R.string.battery_info_status_charging_ac;
394 } else if (plugType == BatteryManager.BATTERY_PLUGGED_USB) {
395 resId = R.string.battery_info_status_charging_usb;
396 } else if (plugType == BatteryManager.BATTERY_PLUGGED_WIRELESS) {
397 resId = R.string.battery_info_status_charging_wireless;
399 resId = R.string.battery_info_status_charging;
401 statusString = res.getString(resId);
402 } else if (status == BatteryManager.BATTERY_STATUS_DISCHARGING) {
403 statusString = res.getString(R.string.battery_info_status_discharging);
404 } else if (status == BatteryManager.BATTERY_STATUS_NOT_CHARGING) {
405 statusString = res.getString(R.string.battery_info_status_not_charging);
406 } else if (status == BatteryManager.BATTERY_STATUS_FULL) {
407 statusString = res.getString(R.string.battery_info_status_full);
409 statusString = res.getString(R.string.battery_info_status_unknown);
415 public static void forcePrepareCustomPreferencesList(
416 ViewGroup parent, View child, ListView list, boolean ignoreSidePadding) {
417 list.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);
418 list.setClipToPadding(false);
419 prepareCustomPreferencesList(parent, child, list, ignoreSidePadding);
423 * Prepare a custom preferences layout, moving padding to {@link ListView}
424 * when outside scrollbars are requested. Usually used to display
425 * {@link ListView} and {@link TabWidget} with correct padding.
427 public static void prepareCustomPreferencesList(
428 ViewGroup parent, View child, View list, boolean ignoreSidePadding) {
429 final boolean movePadding = list.getScrollBarStyle() == View.SCROLLBARS_OUTSIDE_OVERLAY;
431 final Resources res = list.getResources();
432 final int paddingSide = res.getDimensionPixelSize(R.dimen.settings_side_margin);
433 final int paddingBottom = res.getDimensionPixelSize(
434 com.android.internal.R.dimen.preference_fragment_padding_bottom);
436 if (parent instanceof PreferenceFrameLayout) {
437 ((PreferenceFrameLayout.LayoutParams) child.getLayoutParams()).removeBorders = true;
439 final int effectivePaddingSide = ignoreSidePadding ? 0 : paddingSide;
440 list.setPaddingRelative(effectivePaddingSide, 0, effectivePaddingSide, paddingBottom);
442 list.setPaddingRelative(paddingSide, 0, paddingSide, paddingBottom);
447 public static void forceCustomPadding(View view, boolean additive) {
448 final Resources res = view.getResources();
449 final int paddingSide = res.getDimensionPixelSize(R.dimen.settings_side_margin);
451 final int paddingStart = paddingSide + (additive ? view.getPaddingStart() : 0);
452 final int paddingEnd = paddingSide + (additive ? view.getPaddingEnd() : 0);
453 final int paddingBottom = res.getDimensionPixelSize(
454 com.android.internal.R.dimen.preference_fragment_padding_bottom);
456 view.setPaddingRelative(paddingStart, 0, paddingEnd, paddingBottom);
460 * Return string resource that best describes combination of tethering
461 * options available on this device.
463 public static int getTetheringLabel(ConnectivityManager cm) {
464 String[] usbRegexs = cm.getTetherableUsbRegexs();
465 String[] wifiRegexs = cm.getTetherableWifiRegexs();
466 String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs();
468 boolean usbAvailable = usbRegexs.length != 0;
469 boolean wifiAvailable = wifiRegexs.length != 0;
470 boolean bluetoothAvailable = bluetoothRegexs.length != 0;
472 if (wifiAvailable && usbAvailable && bluetoothAvailable) {
473 return R.string.tether_settings_title_all;
474 } else if (wifiAvailable && usbAvailable) {
475 return R.string.tether_settings_title_all;
476 } else if (wifiAvailable && bluetoothAvailable) {
477 return R.string.tether_settings_title_all;
478 } else if (wifiAvailable) {
479 return R.string.tether_settings_title_wifi;
480 } else if (usbAvailable && bluetoothAvailable) {
481 return R.string.tether_settings_title_usb_bluetooth;
482 } else if (usbAvailable) {
483 return R.string.tether_settings_title_usb;
485 return R.string.tether_settings_title_bluetooth;
489 /* Used by UserSettings as well. Call this on a non-ui thread. */
490 public static boolean copyMeProfilePhoto(Context context, UserInfo user) {
491 Uri contactUri = Profile.CONTENT_URI;
493 InputStream avatarDataStream = Contacts.openContactPhotoInputStream(
494 context.getContentResolver(),
496 // If there's no profile photo, assign a default avatar
497 if (avatarDataStream == null) {
500 int userId = user != null ? user.id : UserHandle.myUserId();
501 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
502 Bitmap icon = BitmapFactory.decodeStream(avatarDataStream);
503 um.setUserIcon(userId, icon);
505 avatarDataStream.close();
506 } catch (IOException ioe) { }
510 public static String getMeProfileName(Context context, boolean full) {
512 return getProfileDisplayName(context);
514 return getShorterNameIfPossible(context);
518 private static String getShorterNameIfPossible(Context context) {
519 final String given = getLocalProfileGivenName(context);
520 return !TextUtils.isEmpty(given) ? given : getProfileDisplayName(context);
523 private static String getLocalProfileGivenName(Context context) {
524 final ContentResolver cr = context.getContentResolver();
526 // Find the raw contact ID for the local ME profile raw contact.
527 final long localRowProfileId;
528 final Cursor localRawProfile = cr.query(
529 Profile.CONTENT_RAW_CONTACTS_URI,
530 new String[] {RawContacts._ID},
531 RawContacts.ACCOUNT_TYPE + " IS NULL AND " +
532 RawContacts.ACCOUNT_NAME + " IS NULL",
534 if (localRawProfile == null) return null;
537 if (!localRawProfile.moveToFirst()) {
540 localRowProfileId = localRawProfile.getLong(0);
542 localRawProfile.close();
545 // Find the structured name for the raw contact.
546 final Cursor structuredName = cr.query(
547 Profile.CONTENT_URI.buildUpon().appendPath(Contacts.Data.CONTENT_DIRECTORY).build(),
548 new String[] {CommonDataKinds.StructuredName.GIVEN_NAME,
549 CommonDataKinds.StructuredName.FAMILY_NAME},
550 Data.RAW_CONTACT_ID + "=" + localRowProfileId,
552 if (structuredName == null) return null;
555 if (!structuredName.moveToFirst()) {
558 String partialName = structuredName.getString(0);
559 if (TextUtils.isEmpty(partialName)) {
560 partialName = structuredName.getString(1);
564 structuredName.close();
568 private static final String getProfileDisplayName(Context context) {
569 final ContentResolver cr = context.getContentResolver();
570 final Cursor profile = cr.query(Profile.CONTENT_URI,
571 new String[] {Profile.DISPLAY_NAME}, null, null, null);
572 if (profile == null) return null;
575 if (!profile.moveToFirst()) {
578 return profile.getString(0);
584 /** Not global warming, it's global change warning. */
585 public static Dialog buildGlobalChangeWarningDialog(final Context context, int titleResId,
586 final Runnable positiveAction) {
587 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
588 builder.setTitle(titleResId);
589 builder.setMessage(R.string.global_change_warning);
590 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
592 public void onClick(DialogInterface dialog, int which) {
593 positiveAction.run();
596 builder.setNegativeButton(android.R.string.cancel, null);
598 return builder.create();
601 public static boolean hasMultipleUsers(Context context) {
602 return ((UserManager) context.getSystemService(Context.USER_SERVICE))
603 .getUsers().size() > 1;
607 * Start a new instance of the activity, showing only the given fragment.
608 * When launched in this mode, the given preference fragment will be instantiated and fill the
611 * @param context The context.
612 * @param fragmentName The name of the fragment to display.
613 * @param args Optional arguments to supply to the fragment.
614 * @param resultTo Option fragment that should receive the result of the activity launch.
615 * @param resultRequestCode If resultTo is non-null, this is the request code in which
616 * to report the result.
617 * @param titleResId resource id for the String to display for the title of this set
619 * @param title String to display for the title of this set of preferences.
621 public static void startWithFragment(Context context, String fragmentName, Bundle args,
622 Fragment resultTo, int resultRequestCode, int titleResId,
623 CharSequence title) {
624 startWithFragment(context, fragmentName, args, resultTo, resultRequestCode,
625 null /* titleResPackageName */, titleResId, title, false /* not a shortcut */);
629 * Start a new instance of the activity, showing only the given fragment.
630 * When launched in this mode, the given preference fragment will be instantiated and fill the
633 * @param context The context.
634 * @param fragmentName The name of the fragment to display.
635 * @param args Optional arguments to supply to the fragment.
636 * @param resultTo Option fragment that should receive the result of the activity launch.
637 * @param resultRequestCode If resultTo is non-null, this is the request code in which
638 * to report the result.
639 * @param titleResPackageName Optional package name for the resource id of the title.
640 * @param titleResId resource id for the String to display for the title of this set
642 * @param title String to display for the title of this set of preferences.
644 public static void startWithFragment(Context context, String fragmentName, Bundle args,
645 Fragment resultTo, int resultRequestCode, String titleResPackageName, int titleResId,
646 CharSequence title) {
647 startWithFragment(context, fragmentName, args, resultTo, resultRequestCode,
648 titleResPackageName, titleResId, title, false /* not a shortcut */);
651 public static void startWithFragment(Context context, String fragmentName, Bundle args,
652 Fragment resultTo, int resultRequestCode, int titleResId,
653 CharSequence title, boolean isShortcut) {
654 Intent intent = onBuildStartFragmentIntent(context, fragmentName, args,
655 null /* titleResPackageName */, titleResId, title, isShortcut);
656 if (resultTo == null) {
657 context.startActivity(intent);
659 resultTo.startActivityForResult(intent, resultRequestCode);
663 public static void startWithFragment(Context context, String fragmentName, Bundle args,
664 Fragment resultTo, int resultRequestCode, String titleResPackageName, int titleResId,
665 CharSequence title, boolean isShortcut) {
666 Intent intent = onBuildStartFragmentIntent(context, fragmentName, args, titleResPackageName,
667 titleResId, title, isShortcut);
668 if (resultTo == null) {
669 context.startActivity(intent);
671 resultTo.startActivityForResult(intent, resultRequestCode);
675 public static void startWithFragmentAsUser(Context context, String fragmentName, Bundle args,
676 int titleResId, CharSequence title, boolean isShortcut,
677 UserHandle userHandle) {
678 Intent intent = onBuildStartFragmentIntent(context, fragmentName, args,
679 null /* titleResPackageName */, titleResId, title, isShortcut);
680 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
681 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
682 context.startActivityAsUser(intent, userHandle);
685 public static void startWithFragmentAsUser(Context context, String fragmentName, Bundle args,
686 String titleResPackageName, int titleResId, CharSequence title, boolean isShortcut,
687 UserHandle userHandle) {
688 Intent intent = onBuildStartFragmentIntent(context, fragmentName, args, titleResPackageName,
689 titleResId, title, isShortcut);
690 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
691 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
692 context.startActivityAsUser(intent, userHandle);
696 * Build an Intent to launch a new activity showing the selected fragment.
697 * The implementation constructs an Intent that re-launches the current activity with the
698 * appropriate arguments to display the fragment.
701 * @param context The Context.
702 * @param fragmentName The name of the fragment to display.
703 * @param args Optional arguments to supply to the fragment.
704 * @param titleResPackageName Optional package name for the resource id of the title.
705 * @param titleResId Optional title resource id to show for this item.
706 * @param title Optional title to show for this item.
707 * @param isShortcut tell if this is a Launcher Shortcut or not
708 * @return Returns an Intent that can be launched to display the given
711 public static Intent onBuildStartFragmentIntent(Context context, String fragmentName,
712 Bundle args, String titleResPackageName, int titleResId, CharSequence title,
713 boolean isShortcut) {
714 Intent intent = new Intent(Intent.ACTION_MAIN);
715 intent.setClass(context, SubSettings.class);
716 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, fragmentName);
717 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
718 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME,
719 titleResPackageName);
720 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, titleResId);
721 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE, title);
722 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, isShortcut);
727 * Returns the managed profile of the current user or null if none found.
729 public static UserHandle getManagedProfile(UserManager userManager) {
730 List<UserHandle> userProfiles = userManager.getUserProfiles();
731 final int count = userProfiles.size();
732 for (int i = 0; i < count; i++) {
733 final UserHandle profile = userProfiles.get(i);
734 if (profile.getIdentifier() == userManager.getUserHandle()) {
737 final UserInfo userInfo = userManager.getUserInfo(profile.getIdentifier());
738 if (userInfo.isManagedProfile()) {
746 * Returns true if the current profile is a managed one.
748 public static boolean isManagedProfile(UserManager userManager) {
749 UserInfo currentUser = userManager.getUserInfo(userManager.getUserHandle());
750 return currentUser.isManagedProfile();
754 * Creates a {@link UserAdapter} if there is more than one profile on the device.
756 * <p> The adapter can be used to populate a spinner that switches between the Settings
757 * app on the different profiles.
759 * @return a {@link UserAdapter} or null if there is only one profile.
761 public static UserAdapter createUserSpinnerAdapter(UserManager userManager,
763 List<UserHandle> userProfiles = userManager.getUserProfiles();
764 if (userProfiles.size() < 2) {
768 UserHandle myUserHandle = new UserHandle(UserHandle.myUserId());
769 // The first option should be the current profile
770 userProfiles.remove(myUserHandle);
771 userProfiles.add(0, myUserHandle);
773 return createUserAdapter(userManager, context, userProfiles);
776 public static UserAdapter createUserAdapter(UserManager userManager,
777 Context context, List<UserHandle> userProfiles) {
778 ArrayList<UserDetails> userDetails = new ArrayList<UserDetails>(userProfiles.size());
779 final int count = userProfiles.size();
780 for (int i = 0; i < count; i++) {
781 userDetails.add(new UserDetails(userProfiles.get(i), userManager, context));
783 return new UserAdapter(context, userDetails);
787 * Returns the target user for a Settings activity.
789 * The target user can be either the current user, the user that launched this activity or
790 * the user contained as an extra in the arguments or intent extras.
792 * Note: This is secure in the sense that it only returns a target user different to the current
793 * one if the app launching this activity is the Settings app itself, running in the same user
794 * or in one that is in the same profile group, or if the user id is provided by the system.
796 public static UserHandle getSecureTargetUser(IBinder activityToken,
797 UserManager um, @Nullable Bundle arguments, @Nullable Bundle intentExtras) {
798 UserHandle currentUser = new UserHandle(UserHandle.myUserId());
799 IActivityManager am = ActivityManagerNative.getDefault();
801 String launchedFromPackage = am.getLaunchedFromPackage(activityToken);
802 boolean launchedFromSettingsApp = SETTINGS_PACKAGE_NAME.equals(launchedFromPackage);
804 UserHandle launchedFromUser = new UserHandle(UserHandle.getUserId(
805 am.getLaunchedFromUid(activityToken)));
806 if (launchedFromUser != null && !launchedFromUser.equals(currentUser)) {
808 if (isProfileOf(um, launchedFromUser)) {
809 return launchedFromUser;
812 UserHandle extrasUser = intentExtras != null
813 ? (UserHandle) intentExtras.getParcelable(EXTRA_USER) : null;
814 if (extrasUser != null && !extrasUser.equals(currentUser)) {
816 if (launchedFromSettingsApp && isProfileOf(um, extrasUser)) {
820 UserHandle argumentsUser = arguments != null
821 ? (UserHandle) arguments.getParcelable(EXTRA_USER) : null;
822 if (argumentsUser != null && !argumentsUser.equals(currentUser)) {
824 if (launchedFromSettingsApp && isProfileOf(um, argumentsUser)) {
825 return argumentsUser;
828 } catch (RemoteException e) {
830 Log.v(TAG, "Could not talk to activity manager.", e);
836 * Returns the target user for a Settings activity.
838 * The target user can be either the current user, the user that launched this activity or
839 * the user contained as an extra in the arguments or intent extras.
841 * You should use {@link #getSecureTargetUser(IBinder, UserManager, Bundle, Bundle)} if
844 * @see #getInsecureTargetUser(IBinder, Bundle, Bundle)
846 public static UserHandle getInsecureTargetUser(IBinder activityToken, @Nullable Bundle arguments,
847 @Nullable Bundle intentExtras) {
848 UserHandle currentUser = new UserHandle(UserHandle.myUserId());
849 IActivityManager am = ActivityManagerNative.getDefault();
851 UserHandle launchedFromUser = new UserHandle(UserHandle.getUserId(
852 am.getLaunchedFromUid(activityToken)));
853 if (launchedFromUser != null && !launchedFromUser.equals(currentUser)) {
854 return launchedFromUser;
856 UserHandle extrasUser = intentExtras != null
857 ? (UserHandle) intentExtras.getParcelable(EXTRA_USER) : null;
858 if (extrasUser != null && !extrasUser.equals(currentUser)) {
861 UserHandle argumentsUser = arguments != null
862 ? (UserHandle) arguments.getParcelable(EXTRA_USER) : null;
863 if (argumentsUser != null && !argumentsUser.equals(currentUser)) {
864 return argumentsUser;
866 } catch (RemoteException e) {
868 Log.v(TAG, "Could not talk to activity manager.", e);
875 * Returns true if the user provided is in the same profiles group as the current user.
877 private static boolean isProfileOf(UserManager um, UserHandle otherUser) {
878 if (um == null || otherUser == null) return false;
879 return (UserHandle.myUserId() == otherUser.getIdentifier())
880 || um.getUserProfiles().contains(otherUser);
885 * Returns whether or not this device is able to be OEM unlocked.
887 static boolean isOemUnlockEnabled(Context context) {
888 PersistentDataBlockManager manager =(PersistentDataBlockManager)
889 context.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
890 return manager.getOemUnlockEnabled();
894 * Allows enabling or disabling OEM unlock on this device. OEM unlocked
895 * devices allow users to flash other OSes to them.
897 static void setOemUnlockEnabled(Context context, boolean enabled) {
898 PersistentDataBlockManager manager =(PersistentDataBlockManager)
899 context.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
900 manager.setOemUnlockEnabled(enabled);
904 * Returns a circular icon for a user.
906 public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) {
907 if (user.isManagedProfile()) {
908 // We use predefined values for managed profiles
909 Bitmap b = BitmapFactory.decodeResource(context.getResources(),
910 com.android.internal.R.drawable.ic_corp_icon);
911 return CircleFramedDrawable.getInstance(context, b);
913 if (user.iconPath != null) {
914 Bitmap icon = um.getUserIcon(user.id);
916 return CircleFramedDrawable.getInstance(context, icon);
919 return CircleFramedDrawable.getInstance(context, UserIcons.convertToBitmap(
920 UserIcons.getDefaultUserIcon(user.id, /* light= */ false)));
924 * Returns a label for the user, in the form of "User: user name" or "Work profile".
926 public static String getUserLabel(Context context, UserInfo info) {
927 if (info.isManagedProfile()) {
928 // We use predefined values for managed profiles
929 return context.getString(R.string.managed_user_title);
931 String name = info != null ? info.name : null;
932 if (name == null && info != null) {
933 name = Integer.toString(info.id);
934 } else if (info == null) {
935 name = context.getString(R.string.unknown);
937 return context.getResources().getString(R.string.running_process_item_user_label, name);
941 * Return whether or not the user should have a SIM Cards option in Settings.
942 * TODO: Change back to returning true if count is greater than one after testing.
943 * TODO: See bug 16533525.
945 public static boolean showSimCardTile(Context context) {
946 final TelephonyManager tm =
947 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
949 return tm.getSimCount() > 1;
953 * Determine whether a package is a "system package", in which case certain things (like
954 * disabling notifications or disabling the package altogether) should be disallowed.
956 public static boolean isSystemPackage(PackageManager pm, PackageInfo pkg) {
957 if (sSystemSignature == null) {
958 sSystemSignature = new Signature[]{ getSystemSignature(pm) };
960 return sSystemSignature[0] != null && sSystemSignature[0].equals(getFirstSignature(pkg));
963 private static Signature[] sSystemSignature;
965 private static Signature getFirstSignature(PackageInfo pkg) {
966 if (pkg != null && pkg.signatures != null && pkg.signatures.length > 0) {
967 return pkg.signatures[0];
972 private static Signature getSystemSignature(PackageManager pm) {
974 final PackageInfo sys = pm.getPackageInfo("android", PackageManager.GET_SIGNATURES);
975 return getFirstSignature(sys);
976 } catch (NameNotFoundException e) {
982 * Returns elapsed time for the given millis, in the following format:
984 * @param context the application context
985 * @param millis the elapsed time in milli seconds
986 * @param withSeconds include seconds?
987 * @return the formatted elapsed time
989 public static String formatElapsedTime(Context context, double millis, boolean withSeconds) {
990 StringBuilder sb = new StringBuilder();
991 int seconds = (int) Math.floor(millis / 1000);
997 int days = 0, hours = 0, minutes = 0;
998 if (seconds >= SECONDS_PER_DAY) {
999 days = seconds / SECONDS_PER_DAY;
1000 seconds -= days * SECONDS_PER_DAY;
1002 if (seconds >= SECONDS_PER_HOUR) {
1003 hours = seconds / SECONDS_PER_HOUR;
1004 seconds -= hours * SECONDS_PER_HOUR;
1006 if (seconds >= SECONDS_PER_MINUTE) {
1007 minutes = seconds / SECONDS_PER_MINUTE;
1008 seconds -= minutes * SECONDS_PER_MINUTE;
1012 sb.append(context.getString(R.string.battery_history_days,
1013 days, hours, minutes, seconds));
1014 } else if (hours > 0) {
1015 sb.append(context.getString(R.string.battery_history_hours,
1016 hours, minutes, seconds));
1017 } else if (minutes > 0) {
1018 sb.append(context.getString(R.string.battery_history_minutes, minutes, seconds));
1020 sb.append(context.getString(R.string.battery_history_seconds, seconds));
1024 sb.append(context.getString(R.string.battery_history_days_no_seconds,
1025 days, hours, minutes));
1026 } else if (hours > 0) {
1027 sb.append(context.getString(R.string.battery_history_hours_no_seconds,
1030 sb.append(context.getString(R.string.battery_history_minutes_no_seconds, minutes));
1033 return sb.toString();
1037 * Queries for the UserInfo of a user. Returns null if the user doesn't exist (was removed).
1038 * @param userManager Instance of UserManager
1039 * @param checkUser The user to check the existence of.
1040 * @return UserInfo of the user or null for non-existent user.
1042 public static UserInfo getExistingUser(UserManager userManager, UserHandle checkUser) {
1043 final List<UserInfo> users = userManager.getUsers(true /* excludeDying */);
1044 final int checkUserId = checkUser.getIdentifier();
1045 for (UserInfo user : users) {
1046 if (user.id == checkUserId) {
1053 public static View inflateCategoryHeader(LayoutInflater inflater, ViewGroup parent) {
1054 final TypedArray a = inflater.getContext().obtainStyledAttributes(null,
1055 com.android.internal.R.styleable.Preference,
1056 com.android.internal.R.attr.preferenceCategoryStyle, 0);
1057 final int resId = a.getResourceId(com.android.internal.R.styleable.Preference_layout,
1060 return inflater.inflate(resId, parent, false);
1064 * Return if we are running low on storage space or not.
1066 * @param context The context
1067 * @return true if we are running low on storage space
1069 public static boolean isLowStorage(Context context) {
1070 final StorageManager sm = StorageManager.from(context);
1071 return (sm.getStorageBytesUntilLow(context.getFilesDir()) < 0);
1075 * Returns a default user icon (as a {@link Bitmap}) for the given user.
1077 * Note that for guest users, you should pass in {@code UserHandle.USER_NULL}.
1078 * @param userId the user id or {@code UserHandle.USER_NULL} for a non-user specific icon
1080 public static Bitmap getDefaultUserIconAsBitmap(int userId) {
1081 Bitmap bitmap = null;
1082 // Try finding the corresponding bitmap in the dark bitmap cache
1083 bitmap = sDarkDefaultUserBitmapCache.get(userId);
1084 if (bitmap == null) {
1085 bitmap = UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(userId, false));
1087 sDarkDefaultUserBitmapCache.put(userId, bitmap);
1092 public static boolean hasUsbDefaults(IUsbManager usbManager, String packageName) {
1094 if (usbManager != null) {
1095 return usbManager.hasDefaults(packageName, UserHandle.myUserId());
1097 } catch (RemoteException e) {
1098 Log.e(TAG, "mUsbManager.hasDefaults", e);
1103 public static boolean hasPreferredActivities(PackageManager pm, String packageName) {
1104 // Get list of preferred activities
1105 List<ComponentName> prefActList = new ArrayList<>();
1106 // Intent list cannot be null. so pass empty list
1107 List<IntentFilter> intentList = new ArrayList<>();
1108 pm.getPreferredActivities(intentList, prefActList, packageName);
1109 Log.d(TAG, "Have " + prefActList.size() + " number of activities in preferred list");
1110 return prefActList.size() > 0;
1113 public static CharSequence getLaunchByDeafaultSummary(ApplicationsState.AppEntry appEntry,
1114 IUsbManager usbManager, PackageManager pm, Context context) {
1115 String packageName = appEntry.info.packageName;
1116 boolean hasPreferred = hasPreferredActivities(pm, packageName)
1117 || hasUsbDefaults(usbManager, packageName);
1118 int status = pm.getIntentVerificationStatus(packageName, UserHandle.myUserId());
1119 boolean hasDomainURLsPreference =
1120 (status == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) ||
1121 (status == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER);
1122 return context.getString(hasPreferred || hasDomainURLsPreference
1123 ? R.string.launch_defaults_some
1124 : R.string.launch_defaults_none);
1127 public static void handleLoadingContainer(View loading, View doneLoading, boolean done,
1129 setViewShown(loading, !done, animate);
1130 setViewShown(doneLoading, done, animate);
1133 private static void setViewShown(final View view, boolean shown, boolean animate) {
1135 Animation animation = AnimationUtils.loadAnimation(view.getContext(),
1136 shown ? android.R.anim.fade_in : android.R.anim.fade_out);
1138 view.setVisibility(View.VISIBLE);
1140 animation.setAnimationListener(new AnimationListener() {
1142 public void onAnimationStart(Animation animation) {
1146 public void onAnimationRepeat(Animation animation) {
1150 public void onAnimationEnd(Animation animation) {
1151 view.setVisibility(View.INVISIBLE);
1155 view.startAnimation(animation);
1157 view.clearAnimation();
1158 view.setVisibility(shown ? View.VISIBLE : View.INVISIBLE);
1163 * Returns the application info of the currently installed MDM package.
1165 public static ApplicationInfo getAdminApplicationInfo(Context context, int profileId) {
1166 DevicePolicyManager dpm =
1167 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
1168 ComponentName mdmPackage = dpm.getProfileOwnerAsUser(profileId);
1169 if (mdmPackage == null) {
1172 String mdmPackageName = mdmPackage.getPackageName();
1174 IPackageManager ipm = AppGlobals.getPackageManager();
1175 ApplicationInfo mdmApplicationInfo =
1176 ipm.getApplicationInfo(mdmPackageName, 0, profileId);
1177 return mdmApplicationInfo;
1178 } catch (RemoteException e) {
1179 Log.e(TAG, "Error while retrieving application info for package " + mdmPackageName
1180 + ", userId " + profileId, e);
1185 public static boolean isBandwidthControlEnabled() {
1186 final INetworkManagementService netManager = INetworkManagementService.Stub
1187 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1189 return netManager.isBandwidthControlEnabled();
1190 } catch (RemoteException e) {