1 package com.android.settingslib; 2 3 import android.annotation.ColorInt; 4 import android.content.Context; 5 import android.content.Intent; 6 import android.content.pm.ApplicationInfo; 7 import android.content.pm.PackageInfo; 8 import android.content.pm.PackageManager; 9 import android.content.pm.PackageManager.NameNotFoundException; 10 import android.content.pm.Signature; 11 import android.content.pm.UserInfo; 12 import android.content.res.ColorStateList; 13 import android.content.res.Resources; 14 import android.content.res.TypedArray; 15 import android.graphics.Bitmap; 16 import android.graphics.Canvas; 17 import android.graphics.Color; 18 import android.graphics.ColorFilter; 19 import android.graphics.ColorMatrix; 20 import android.graphics.ColorMatrixColorFilter; 21 import android.graphics.drawable.BitmapDrawable; 22 import android.graphics.drawable.Drawable; 23 import android.location.LocationManager; 24 import android.media.AudioManager; 25 import android.net.ConnectivityManager; 26 import android.os.BatteryManager; 27 import android.os.SystemProperties; 28 import android.os.UserHandle; 29 import android.os.UserManager; 30 import android.print.PrintManager; 31 import android.provider.Settings; 32 import android.telephony.AccessNetworkConstants; 33 import android.telephony.NetworkRegistrationInfo; 34 import android.telephony.ServiceState; 35 36 import androidx.annotation.NonNull; 37 import androidx.core.graphics.drawable.RoundedBitmapDrawable; 38 import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; 39 40 import com.android.internal.annotations.VisibleForTesting; 41 import com.android.internal.util.UserIcons; 42 import com.android.launcher3.icons.IconFactory; 43 import com.android.settingslib.drawable.UserIconDrawable; 44 import com.android.settingslib.fuelgauge.BatteryStatus; 45 46 import java.text.NumberFormat; 47 48 public class Utils { 49 50 @VisibleForTesting 51 static final String STORAGE_MANAGER_ENABLED_PROPERTY = 52 "ro.storage_manager.enabled"; 53 54 private static Signature[] sSystemSignature; 55 private static String sPermissionControllerPackageName; 56 private static String sServicesSystemSharedLibPackageName; 57 private static String sSharedSystemSharedLibPackageName; 58 59 static final int[] WIFI_PIE = { 60 com.android.internal.R.drawable.ic_wifi_signal_0, 61 com.android.internal.R.drawable.ic_wifi_signal_1, 62 com.android.internal.R.drawable.ic_wifi_signal_2, 63 com.android.internal.R.drawable.ic_wifi_signal_3, 64 com.android.internal.R.drawable.ic_wifi_signal_4 65 }; 66 67 static final int[] SHOW_X_WIFI_PIE = { 68 R.drawable.ic_show_x_wifi_signal_0, 69 R.drawable.ic_show_x_wifi_signal_1, 70 R.drawable.ic_show_x_wifi_signal_2, 71 R.drawable.ic_show_x_wifi_signal_3, 72 R.drawable.ic_show_x_wifi_signal_4 73 }; 74 updateLocationEnabled(Context context, boolean enabled, int userId, int source)75 public static void updateLocationEnabled(Context context, boolean enabled, int userId, 76 int source) { 77 Settings.Secure.putIntForUser( 78 context.getContentResolver(), Settings.Secure.LOCATION_CHANGER, source, 79 userId); 80 81 LocationManager locationManager = context.getSystemService(LocationManager.class); 82 locationManager.setLocationEnabledForUser(enabled, UserHandle.of(userId)); 83 } 84 85 /** 86 * Return string resource that best describes combination of tethering 87 * options available on this device. 88 */ getTetheringLabel(ConnectivityManager cm)89 public static int getTetheringLabel(ConnectivityManager cm) { 90 String[] usbRegexs = cm.getTetherableUsbRegexs(); 91 String[] wifiRegexs = cm.getTetherableWifiRegexs(); 92 String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs(); 93 94 boolean usbAvailable = usbRegexs.length != 0; 95 boolean wifiAvailable = wifiRegexs.length != 0; 96 boolean bluetoothAvailable = bluetoothRegexs.length != 0; 97 98 if (wifiAvailable && usbAvailable && bluetoothAvailable) { 99 return R.string.tether_settings_title_all; 100 } else if (wifiAvailable && usbAvailable) { 101 return R.string.tether_settings_title_all; 102 } else if (wifiAvailable && bluetoothAvailable) { 103 return R.string.tether_settings_title_all; 104 } else if (wifiAvailable) { 105 return R.string.tether_settings_title_wifi; 106 } else if (usbAvailable && bluetoothAvailable) { 107 return R.string.tether_settings_title_usb_bluetooth; 108 } else if (usbAvailable) { 109 return R.string.tether_settings_title_usb; 110 } else { 111 return R.string.tether_settings_title_bluetooth; 112 } 113 } 114 115 /** 116 * Returns a label for the user, in the form of "User: user name" or "Work profile". 117 */ getUserLabel(Context context, UserInfo info)118 public static String getUserLabel(Context context, UserInfo info) { 119 String name = info != null ? info.name : null; 120 if (info.isManagedProfile()) { 121 // We use predefined values for managed profiles 122 return context.getString(R.string.managed_user_title); 123 } else if (info.isGuest()) { 124 name = context.getString(R.string.user_guest); 125 } 126 if (name == null && info != null) { 127 name = Integer.toString(info.id); 128 } else if (info == null) { 129 name = context.getString(R.string.unknown); 130 } 131 return context.getResources().getString(R.string.running_process_item_user_label, name); 132 } 133 134 /** 135 * Returns a circular icon for a user. 136 */ getUserIcon(Context context, UserManager um, UserInfo user)137 public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) { 138 final int iconSize = UserIconDrawable.getSizeForList(context); 139 if (user.isManagedProfile()) { 140 Drawable drawable = UserIconDrawable.getManagedUserDrawable(context); 141 drawable.setBounds(0, 0, iconSize, iconSize); 142 return drawable; 143 } 144 if (user.iconPath != null) { 145 Bitmap icon = um.getUserIcon(user.id); 146 if (icon != null) { 147 return new UserIconDrawable(iconSize).setIcon(icon).bake(); 148 } 149 } 150 return new UserIconDrawable(iconSize).setIconDrawable( 151 UserIcons.getDefaultUserIcon(context.getResources(), user.id, /* light= */ false)) 152 .bake(); 153 } 154 155 /** Formats a double from 0.0..100.0 with an option to round **/ formatPercentage(double percentage, boolean round)156 public static String formatPercentage(double percentage, boolean round) { 157 final int localPercentage = round ? Math.round((float) percentage) : (int) percentage; 158 return formatPercentage(localPercentage); 159 } 160 161 /** Formats the ratio of amount/total as a percentage. */ formatPercentage(long amount, long total)162 public static String formatPercentage(long amount, long total) { 163 return formatPercentage(((double) amount) / total); 164 } 165 166 /** Formats an integer from 0..100 as a percentage. */ formatPercentage(int percentage)167 public static String formatPercentage(int percentage) { 168 return formatPercentage(((double) percentage) / 100.0); 169 } 170 171 /** Formats a double from 0.0..1.0 as a percentage. */ formatPercentage(double percentage)172 public static String formatPercentage(double percentage) { 173 return NumberFormat.getPercentInstance().format(percentage); 174 } 175 getBatteryLevel(Intent batteryChangedIntent)176 public static int getBatteryLevel(Intent batteryChangedIntent) { 177 int level = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0); 178 int scale = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 100); 179 return (level * 100) / scale; 180 } 181 182 /** 183 * Get battery status string 184 * 185 * @param context the context 186 * @param batteryChangedIntent battery broadcast intent received from {@link 187 * Intent.ACTION_BATTERY_CHANGED}. 188 * @return battery status string 189 */ getBatteryStatus(Context context, Intent batteryChangedIntent)190 public static String getBatteryStatus(Context context, Intent batteryChangedIntent) { 191 final int status = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_STATUS, 192 BatteryManager.BATTERY_STATUS_UNKNOWN); 193 final Resources res = context.getResources(); 194 195 String statusString = res.getString(R.string.battery_info_status_unknown); 196 final BatteryStatus batteryStatus = new BatteryStatus(batteryChangedIntent); 197 198 if (batteryStatus.isCharged()) { 199 statusString = res.getString(R.string.battery_info_status_full); 200 } else { 201 if (status == BatteryManager.BATTERY_STATUS_CHARGING) { 202 switch (batteryStatus.getChargingSpeed(context)) { 203 case BatteryStatus.CHARGING_FAST: 204 statusString = res.getString(R.string.battery_info_status_charging_fast); 205 break; 206 case BatteryStatus.CHARGING_SLOWLY: 207 statusString = res.getString(R.string.battery_info_status_charging_slow); 208 break; 209 default: 210 statusString = res.getString(R.string.battery_info_status_charging); 211 break; 212 } 213 214 } else if (status == BatteryManager.BATTERY_STATUS_DISCHARGING) { 215 statusString = res.getString(R.string.battery_info_status_discharging); 216 } else if (status == BatteryManager.BATTERY_STATUS_NOT_CHARGING) { 217 statusString = res.getString(R.string.battery_info_status_not_charging); 218 } 219 } 220 221 return statusString; 222 } 223 getColorAccent(Context context)224 public static ColorStateList getColorAccent(Context context) { 225 return getColorAttr(context, android.R.attr.colorAccent); 226 } 227 getColorError(Context context)228 public static ColorStateList getColorError(Context context) { 229 return getColorAttr(context, android.R.attr.colorError); 230 } 231 232 @ColorInt getColorAccentDefaultColor(Context context)233 public static int getColorAccentDefaultColor(Context context) { 234 return getColorAttrDefaultColor(context, android.R.attr.colorAccent); 235 } 236 237 @ColorInt getColorErrorDefaultColor(Context context)238 public static int getColorErrorDefaultColor(Context context) { 239 return getColorAttrDefaultColor(context, android.R.attr.colorError); 240 } 241 242 @ColorInt getColorStateListDefaultColor(Context context, int resId)243 public static int getColorStateListDefaultColor(Context context, int resId) { 244 final ColorStateList list = 245 context.getResources().getColorStateList(resId, context.getTheme()); 246 return list.getDefaultColor(); 247 } 248 249 /** 250 * This method computes disabled color from normal color 251 * 252 * @param context the context 253 * @param inputColor normal color. 254 * @return disabled color. 255 */ 256 @ColorInt getDisabled(Context context, int inputColor)257 public static int getDisabled(Context context, int inputColor) { 258 return applyAlphaAttr(context, android.R.attr.disabledAlpha, inputColor); 259 } 260 261 @ColorInt applyAlphaAttr(Context context, int attr, int inputColor)262 public static int applyAlphaAttr(Context context, int attr, int inputColor) { 263 TypedArray ta = context.obtainStyledAttributes(new int[]{attr}); 264 float alpha = ta.getFloat(0, 0); 265 ta.recycle(); 266 return applyAlpha(alpha, inputColor); 267 } 268 269 @ColorInt applyAlpha(float alpha, int inputColor)270 public static int applyAlpha(float alpha, int inputColor) { 271 alpha *= Color.alpha(inputColor); 272 return Color.argb((int) (alpha), Color.red(inputColor), Color.green(inputColor), 273 Color.blue(inputColor)); 274 } 275 276 @ColorInt getColorAttrDefaultColor(Context context, int attr)277 public static int getColorAttrDefaultColor(Context context, int attr) { 278 TypedArray ta = context.obtainStyledAttributes(new int[]{attr}); 279 @ColorInt int colorAccent = ta.getColor(0, 0); 280 ta.recycle(); 281 return colorAccent; 282 } 283 getColorAttr(Context context, int attr)284 public static ColorStateList getColorAttr(Context context, int attr) { 285 TypedArray ta = context.obtainStyledAttributes(new int[]{attr}); 286 ColorStateList stateList = null; 287 try { 288 stateList = ta.getColorStateList(0); 289 } finally { 290 ta.recycle(); 291 } 292 return stateList; 293 } 294 getThemeAttr(Context context, int attr)295 public static int getThemeAttr(Context context, int attr) { 296 return getThemeAttr(context, attr, 0); 297 } 298 getThemeAttr(Context context, int attr, int defaultValue)299 public static int getThemeAttr(Context context, int attr, int defaultValue) { 300 TypedArray ta = context.obtainStyledAttributes(new int[]{attr}); 301 int theme = ta.getResourceId(0, defaultValue); 302 ta.recycle(); 303 return theme; 304 } 305 getDrawable(Context context, int attr)306 public static Drawable getDrawable(Context context, int attr) { 307 TypedArray ta = context.obtainStyledAttributes(new int[]{attr}); 308 Drawable drawable = ta.getDrawable(0); 309 ta.recycle(); 310 return drawable; 311 } 312 313 /** 314 * Create a color matrix suitable for a ColorMatrixColorFilter that modifies only the color but 315 * preserves the alpha for a given drawable 316 * @param color 317 * @return a color matrix that uses the source alpha and given color 318 */ getAlphaInvariantColorMatrixForColor(@olorInt int color)319 public static ColorMatrix getAlphaInvariantColorMatrixForColor(@ColorInt int color) { 320 int r = Color.red(color); 321 int g = Color.green(color); 322 int b = Color.blue(color); 323 324 ColorMatrix cm = new ColorMatrix(new float[] { 325 0, 0, 0, 0, r, 326 0, 0, 0, 0, g, 327 0, 0, 0, 0, b, 328 0, 0, 0, 1, 0 }); 329 330 return cm; 331 } 332 333 /** 334 * Create a ColorMatrixColorFilter to tint a drawable but retain its alpha characteristics 335 * 336 * @return a ColorMatrixColorFilter which changes the color of the output but is invariant on 337 * the source alpha 338 */ getAlphaInvariantColorFilterForColor(@olorInt int color)339 public static ColorFilter getAlphaInvariantColorFilterForColor(@ColorInt int color) { 340 return new ColorMatrixColorFilter(getAlphaInvariantColorMatrixForColor(color)); 341 } 342 343 /** 344 * Determine whether a package is a "system package", in which case certain things (like 345 * disabling notifications or disabling the package altogether) should be disallowed. 346 */ isSystemPackage(Resources resources, PackageManager pm, PackageInfo pkg)347 public static boolean isSystemPackage(Resources resources, PackageManager pm, PackageInfo pkg) { 348 if (sSystemSignature == null) { 349 sSystemSignature = new Signature[]{getSystemSignature(pm)}; 350 } 351 if (sPermissionControllerPackageName == null) { 352 sPermissionControllerPackageName = pm.getPermissionControllerPackageName(); 353 } 354 if (sServicesSystemSharedLibPackageName == null) { 355 sServicesSystemSharedLibPackageName = pm.getServicesSystemSharedLibraryPackageName(); 356 } 357 if (sSharedSystemSharedLibPackageName == null) { 358 sSharedSystemSharedLibPackageName = pm.getSharedSystemSharedLibraryPackageName(); 359 } 360 return (sSystemSignature[0] != null 361 && sSystemSignature[0].equals(getFirstSignature(pkg))) 362 || pkg.packageName.equals(sPermissionControllerPackageName) 363 || pkg.packageName.equals(sServicesSystemSharedLibPackageName) 364 || pkg.packageName.equals(sSharedSystemSharedLibPackageName) 365 || pkg.packageName.equals(PrintManager.PRINT_SPOOLER_PACKAGE_NAME) 366 || isDeviceProvisioningPackage(resources, pkg.packageName); 367 } 368 getFirstSignature(PackageInfo pkg)369 private static Signature getFirstSignature(PackageInfo pkg) { 370 if (pkg != null && pkg.signatures != null && pkg.signatures.length > 0) { 371 return pkg.signatures[0]; 372 } 373 return null; 374 } 375 getSystemSignature(PackageManager pm)376 private static Signature getSystemSignature(PackageManager pm) { 377 try { 378 final PackageInfo sys = pm.getPackageInfo("android", PackageManager.GET_SIGNATURES); 379 return getFirstSignature(sys); 380 } catch (NameNotFoundException e) { 381 } 382 return null; 383 } 384 385 /** 386 * Returns {@code true} if the supplied package is the device provisioning app. Otherwise, 387 * returns {@code false}. 388 */ isDeviceProvisioningPackage(Resources resources, String packageName)389 public static boolean isDeviceProvisioningPackage(Resources resources, String packageName) { 390 String deviceProvisioningPackage = resources.getString( 391 com.android.internal.R.string.config_deviceProvisioningPackage); 392 return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName); 393 } 394 395 /** 396 * Returns the Wifi icon resource for a given RSSI level. 397 * 398 * @param level The number of bars to show (0-4) 399 * @throws IllegalArgumentException if an invalid RSSI level is given. 400 */ getWifiIconResource(int level)401 public static int getWifiIconResource(int level) { 402 return getWifiIconResource(false /* showX */, level); 403 } 404 405 /** 406 * Returns the Wifi icon resource for a given RSSI level. 407 * 408 * @param showX True if a connected Wi-Fi network has the problem which should show Pie+x 409 * signal icon to users. 410 * @param level The number of bars to show (0-4) 411 * @throws IllegalArgumentException if an invalid RSSI level is given. 412 */ getWifiIconResource(boolean showX, int level)413 public static int getWifiIconResource(boolean showX, int level) { 414 if (level < 0 || level >= WIFI_PIE.length) { 415 throw new IllegalArgumentException("No Wifi icon found for level: " + level); 416 } 417 return showX ? SHOW_X_WIFI_PIE[level] : WIFI_PIE[level]; 418 } 419 getDefaultStorageManagerDaysToRetain(Resources resources)420 public static int getDefaultStorageManagerDaysToRetain(Resources resources) { 421 int defaultDays = Settings.Secure.AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_DEFAULT; 422 try { 423 defaultDays = 424 resources.getInteger( 425 com.android 426 .internal 427 .R 428 .integer 429 .config_storageManagerDaystoRetainDefault); 430 } catch (Resources.NotFoundException e) { 431 // We are likely in a test environment. 432 } 433 return defaultDays; 434 } 435 isWifiOnly(Context context)436 public static boolean isWifiOnly(Context context) { 437 return !context.getSystemService(ConnectivityManager.class) 438 .isNetworkSupported(ConnectivityManager.TYPE_MOBILE); 439 } 440 441 /** Returns if the automatic storage management feature is turned on or not. **/ isStorageManagerEnabled(Context context)442 public static boolean isStorageManagerEnabled(Context context) { 443 boolean isDefaultOn; 444 try { 445 isDefaultOn = SystemProperties.getBoolean(STORAGE_MANAGER_ENABLED_PROPERTY, false); 446 } catch (Resources.NotFoundException e) { 447 isDefaultOn = false; 448 } 449 return Settings.Secure.getInt(context.getContentResolver(), 450 Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED, 451 isDefaultOn ? 1 : 0) 452 != 0; 453 } 454 455 /** 456 * get that {@link AudioManager#getMode()} is in ringing/call/communication(VoIP) status. 457 */ isAudioModeOngoingCall(Context context)458 public static boolean isAudioModeOngoingCall(Context context) { 459 final AudioManager audioManager = context.getSystemService(AudioManager.class); 460 final int audioMode = audioManager.getMode(); 461 return audioMode == AudioManager.MODE_RINGTONE 462 || audioMode == AudioManager.MODE_IN_CALL 463 || audioMode == AudioManager.MODE_IN_COMMUNICATION; 464 } 465 466 /** 467 * Return the service state is in-service or not. 468 * To make behavior consistent with SystemUI and Settings/AboutPhone/SIM status UI 469 * 470 * @param serviceState Service state. {@link ServiceState} 471 */ isInService(ServiceState serviceState)472 public static boolean isInService(ServiceState serviceState) { 473 if (serviceState == null) { 474 return false; 475 } 476 int state = getCombinedServiceState(serviceState); 477 if (state == ServiceState.STATE_POWER_OFF 478 || state == ServiceState.STATE_OUT_OF_SERVICE 479 || state == ServiceState.STATE_EMERGENCY_ONLY) { 480 return false; 481 } else { 482 return true; 483 } 484 } 485 486 /** 487 * Return the combined service state. 488 * To make behavior consistent with SystemUI and Settings/AboutPhone/SIM status UI 489 * 490 * @param serviceState Service state. {@link ServiceState} 491 */ getCombinedServiceState(ServiceState serviceState)492 public static int getCombinedServiceState(ServiceState serviceState) { 493 if (serviceState == null) { 494 return ServiceState.STATE_OUT_OF_SERVICE; 495 } 496 497 // Consider the device to be in service if either voice or data 498 // service is available. Some SIM cards are marketed as data-only 499 // and do not support voice service, and on these SIM cards, we 500 // want to show signal bars for data service as well as the "no 501 // service" or "emergency calls only" text that indicates that voice 502 // is not available. Note that we ignore the IWLAN service state 503 // because that state indicates the use of VoWIFI and not cell service 504 final int state = serviceState.getState(); 505 final int dataState = serviceState.getDataRegistrationState(); 506 507 if (state == ServiceState.STATE_OUT_OF_SERVICE 508 || state == ServiceState.STATE_EMERGENCY_ONLY) { 509 if (dataState == ServiceState.STATE_IN_SERVICE && isNotInIwlan(serviceState)) { 510 return ServiceState.STATE_IN_SERVICE; 511 } 512 } 513 return state; 514 } 515 516 /** Get the corresponding adaptive icon drawable. */ getBadgedIcon(Context context, Drawable icon, UserHandle user)517 public static Drawable getBadgedIcon(Context context, Drawable icon, UserHandle user) { 518 try (IconFactory iconFactory = IconFactory.obtain(context)) { 519 final Bitmap iconBmp = iconFactory.createBadgedIconBitmap(icon, user, 520 true /* shrinkNonAdaptiveIcons */).icon; 521 return new BitmapDrawable(context.getResources(), iconBmp); 522 } 523 } 524 525 /** Get the {@link Drawable} that represents the app icon */ getBadgedIcon(Context context, ApplicationInfo appInfo)526 public static Drawable getBadgedIcon(Context context, ApplicationInfo appInfo) { 527 return getBadgedIcon(context, appInfo.loadUnbadgedIcon(context.getPackageManager()), 528 UserHandle.getUserHandleForUid(appInfo.uid)); 529 } 530 isNotInIwlan(ServiceState serviceState)531 private static boolean isNotInIwlan(ServiceState serviceState) { 532 final NetworkRegistrationInfo networkRegWlan = serviceState.getNetworkRegistrationInfo( 533 NetworkRegistrationInfo.DOMAIN_PS, 534 AccessNetworkConstants.TRANSPORT_TYPE_WLAN); 535 if (networkRegWlan == null) { 536 return true; 537 } 538 539 final boolean isInIwlan = (networkRegWlan.getRegistrationState() 540 == NetworkRegistrationInfo.REGISTRATION_STATE_HOME) 541 || (networkRegWlan.getRegistrationState() 542 == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING); 543 return !isInIwlan; 544 } 545 546 /** 547 * Returns a bitmap with rounded corner. 548 * 549 * @param context application context. 550 * @param source bitmap to apply round corner. 551 * @param cornerRadius corner radius value. 552 */ convertCornerRadiusBitmap(@onNull Context context, @NonNull Bitmap source, @NonNull float cornerRadius)553 public static Bitmap convertCornerRadiusBitmap(@NonNull Context context, 554 @NonNull Bitmap source, @NonNull float cornerRadius) { 555 final Bitmap roundedBitmap = Bitmap.createBitmap(source.getWidth(), source.getHeight(), 556 Bitmap.Config.ARGB_8888); 557 final RoundedBitmapDrawable drawable = 558 RoundedBitmapDrawableFactory.create(context.getResources(), source); 559 drawable.setAntiAlias(true); 560 drawable.setCornerRadius(cornerRadius); 561 final Canvas canvas = new Canvas(roundedBitmap); 562 drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); 563 drawable.draw(canvas); 564 return roundedBitmap; 565 } 566 } 567