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