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