1 package com.android.tv.settings.library.about; 2 3 import android.annotation.ColorInt; 4 import android.content.Context; 5 import android.content.Intent; 6 import android.content.pm.PackageInfo; 7 import android.content.pm.PackageManager; 8 import android.content.pm.PackageManager.NameNotFoundException; 9 import android.content.pm.Signature; 10 import android.content.res.ColorStateList; 11 import android.content.res.TypedArray; 12 import android.graphics.Bitmap; 13 import android.graphics.Canvas; 14 import android.graphics.Color; 15 import android.graphics.ColorFilter; 16 import android.graphics.ColorMatrix; 17 import android.graphics.ColorMatrixColorFilter; 18 import android.graphics.drawable.Drawable; 19 import android.media.AudioManager; 20 import android.os.BatteryManager; 21 import android.telephony.AccessNetworkConstants; 22 import android.telephony.NetworkRegistrationInfo; 23 import android.telephony.ServiceState; 24 import android.telephony.TelephonyManager; 25 26 import androidx.annotation.NonNull; 27 import androidx.core.graphics.drawable.RoundedBitmapDrawable; 28 import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; 29 30 import java.text.NumberFormat; 31 32 public class Utils { 33 34 /** Formats a double from 0.0..100.0 with an option to round **/ formatPercentage(double percentage, boolean round)35 public static String formatPercentage(double percentage, boolean round) { 36 final int localPercentage = round ? Math.round((float) percentage) : (int) percentage; 37 return formatPercentage(localPercentage); 38 } 39 40 /** Formats the ratio of amount/total as a percentage. */ formatPercentage(long amount, long total)41 public static String formatPercentage(long amount, long total) { 42 return formatPercentage(((double) amount) / total); 43 } 44 45 /** Formats an integer from 0..100 as a percentage. */ formatPercentage(int percentage)46 public static String formatPercentage(int percentage) { 47 return formatPercentage(((double) percentage) / 100.0); 48 } 49 50 /** Formats a double from 0.0..1.0 as a percentage. */ formatPercentage(double percentage)51 public static String formatPercentage(double percentage) { 52 return NumberFormat.getPercentInstance().format(percentage); 53 } 54 getBatteryLevel(Intent batteryChangedIntent)55 public static int getBatteryLevel(Intent batteryChangedIntent) { 56 int level = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0); 57 int scale = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 100); 58 return (level * 100) / scale; 59 } 60 getColorAccent(Context context)61 public static ColorStateList getColorAccent(Context context) { 62 return getColorAttr(context, android.R.attr.colorAccent); 63 } 64 getColorError(Context context)65 public static ColorStateList getColorError(Context context) { 66 return getColorAttr(context, android.R.attr.colorError); 67 } 68 69 @ColorInt getColorAccentDefaultColor(Context context)70 public static int getColorAccentDefaultColor(Context context) { 71 return getColorAttrDefaultColor(context, android.R.attr.colorAccent); 72 } 73 74 @ColorInt getColorErrorDefaultColor(Context context)75 public static int getColorErrorDefaultColor(Context context) { 76 return getColorAttrDefaultColor(context, android.R.attr.colorError); 77 } 78 79 @ColorInt getColorStateListDefaultColor(Context context, int resId)80 public static int getColorStateListDefaultColor(Context context, int resId) { 81 final ColorStateList list = 82 context.getResources().getColorStateList(resId, context.getTheme()); 83 return list.getDefaultColor(); 84 } 85 86 /** 87 * This method computes disabled color from normal color 88 * 89 * @param context the context 90 * @param inputColor normal color. 91 * @return disabled color. 92 */ 93 @ColorInt getDisabled(Context context, int inputColor)94 public static int getDisabled(Context context, int inputColor) { 95 return applyAlphaAttr(context, android.R.attr.disabledAlpha, inputColor); 96 } 97 98 @ColorInt applyAlphaAttr(Context context, int attr, int inputColor)99 public static int applyAlphaAttr(Context context, int attr, int inputColor) { 100 TypedArray ta = context.obtainStyledAttributes(new int[]{attr}); 101 float alpha = ta.getFloat(0, 0); 102 ta.recycle(); 103 return applyAlpha(alpha, inputColor); 104 } 105 106 @ColorInt applyAlpha(float alpha, int inputColor)107 public static int applyAlpha(float alpha, int inputColor) { 108 alpha *= Color.alpha(inputColor); 109 return Color.argb((int) (alpha), Color.red(inputColor), Color.green(inputColor), 110 Color.blue(inputColor)); 111 } 112 113 @ColorInt getColorAttrDefaultColor(Context context, int attr)114 public static int getColorAttrDefaultColor(Context context, int attr) { 115 TypedArray ta = context.obtainStyledAttributes(new int[]{attr}); 116 @ColorInt int colorAccent = ta.getColor(0, 0); 117 ta.recycle(); 118 return colorAccent; 119 } 120 getColorAttr(Context context, int attr)121 public static ColorStateList getColorAttr(Context context, int attr) { 122 TypedArray ta = context.obtainStyledAttributes(new int[]{attr}); 123 ColorStateList stateList = null; 124 try { 125 stateList = ta.getColorStateList(0); 126 } finally { 127 ta.recycle(); 128 } 129 return stateList; 130 } 131 getThemeAttr(Context context, int attr)132 public static int getThemeAttr(Context context, int attr) { 133 return getThemeAttr(context, attr, 0); 134 } 135 getThemeAttr(Context context, int attr, int defaultValue)136 public static int getThemeAttr(Context context, int attr, int defaultValue) { 137 TypedArray ta = context.obtainStyledAttributes(new int[]{attr}); 138 int theme = ta.getResourceId(0, defaultValue); 139 ta.recycle(); 140 return theme; 141 } 142 getDrawable(Context context, int attr)143 public static Drawable getDrawable(Context context, int attr) { 144 TypedArray ta = context.obtainStyledAttributes(new int[]{attr}); 145 Drawable drawable = ta.getDrawable(0); 146 ta.recycle(); 147 return drawable; 148 } 149 150 /** 151 * Create a color matrix suitable for a ColorMatrixColorFilter that modifies only the color but 152 * preserves the alpha for a given drawable 153 * 154 * @return a color matrix that uses the source alpha and given color 155 */ getAlphaInvariantColorMatrixForColor(@olorInt int color)156 public static ColorMatrix getAlphaInvariantColorMatrixForColor(@ColorInt int color) { 157 int r = Color.red(color); 158 int g = Color.green(color); 159 int b = Color.blue(color); 160 161 ColorMatrix cm = new ColorMatrix(new float[]{ 162 0, 0, 0, 0, r, 163 0, 0, 0, 0, g, 164 0, 0, 0, 0, b, 165 0, 0, 0, 1, 0}); 166 167 return cm; 168 } 169 170 /** 171 * Create a ColorMatrixColorFilter to tint a drawable but retain its alpha characteristics 172 * 173 * @return a ColorMatrixColorFilter which changes the color of the output but is invariant on 174 * the source alpha 175 */ getAlphaInvariantColorFilterForColor(@olorInt int color)176 public static ColorFilter getAlphaInvariantColorFilterForColor(@ColorInt int color) { 177 return new ColorMatrixColorFilter(getAlphaInvariantColorMatrixForColor(color)); 178 } 179 180 getFirstSignature(PackageInfo pkg)181 private static Signature getFirstSignature(PackageInfo pkg) { 182 if (pkg != null && pkg.signatures != null && pkg.signatures.length > 0) { 183 return pkg.signatures[0]; 184 } 185 return null; 186 } 187 getSystemSignature(PackageManager pm)188 private static Signature getSystemSignature(PackageManager pm) { 189 try { 190 final PackageInfo sys = pm.getPackageInfo("android", PackageManager.GET_SIGNATURES); 191 return getFirstSignature(sys); 192 } catch (NameNotFoundException e) { 193 } 194 return null; 195 } 196 isWifiOnly(Context context)197 public static boolean isWifiOnly(Context context) { 198 return !context.getSystemService(TelephonyManager.class).isDataCapable(); 199 } 200 201 202 203 /** 204 * get that {@link AudioManager#getMode()} is in ringing/call/communication(VoIP) status. 205 */ isAudioModeOngoingCall(Context context)206 public static boolean isAudioModeOngoingCall(Context context) { 207 final AudioManager audioManager = context.getSystemService(AudioManager.class); 208 final int audioMode = audioManager.getMode(); 209 return audioMode == AudioManager.MODE_RINGTONE 210 || audioMode == AudioManager.MODE_IN_CALL 211 || audioMode == AudioManager.MODE_IN_COMMUNICATION; 212 } 213 214 /** 215 * Return the service state is in-service or not. 216 * To make behavior consistent with SystemUI and Settings/AboutPhone/SIM status UI 217 * 218 * @param serviceState Service state. {@link ServiceState} 219 */ isInService(ServiceState serviceState)220 public static boolean isInService(ServiceState serviceState) { 221 if (serviceState == null) { 222 return false; 223 } 224 int state = getCombinedServiceState(serviceState); 225 return state != ServiceState.STATE_POWER_OFF 226 && state != ServiceState.STATE_OUT_OF_SERVICE 227 && state != ServiceState.STATE_EMERGENCY_ONLY; 228 } 229 230 /** 231 * Return the combined service state. 232 * To make behavior consistent with SystemUI and Settings/AboutPhone/SIM status UI 233 * 234 * @param serviceState Service state. {@link ServiceState} 235 */ getCombinedServiceState(ServiceState serviceState)236 public static int getCombinedServiceState(ServiceState serviceState) { 237 if (serviceState == null) { 238 return ServiceState.STATE_OUT_OF_SERVICE; 239 } 240 241 // Consider the device to be in service if either voice or data 242 // service is available. Some SIM cards are marketed as data-only 243 // and do not support voice service, and on these SIM cards, we 244 // want to show signal bars for data service as well as the "no 245 // service" or "emergency calls only" text that indicates that voice 246 // is not available. Note that we ignore the IWLAN service state 247 // because that state indicates the use of VoWIFI and not cell service 248 final int state = serviceState.getState(); 249 final int dataState = serviceState.getDataRegistrationState(); 250 251 if (state == ServiceState.STATE_OUT_OF_SERVICE 252 || state == ServiceState.STATE_EMERGENCY_ONLY) { 253 if (dataState == ServiceState.STATE_IN_SERVICE && isNotInIwlan(serviceState)) { 254 return ServiceState.STATE_IN_SERVICE; 255 } 256 } 257 return state; 258 } 259 isNotInIwlan(ServiceState serviceState)260 private static boolean isNotInIwlan(ServiceState serviceState) { 261 final NetworkRegistrationInfo networkRegWlan = serviceState.getNetworkRegistrationInfo( 262 NetworkRegistrationInfo.DOMAIN_PS, 263 AccessNetworkConstants.TRANSPORT_TYPE_WLAN); 264 if (networkRegWlan == null) { 265 return true; 266 } 267 268 final boolean isInIwlan = (networkRegWlan.getRegistrationState() 269 == NetworkRegistrationInfo.REGISTRATION_STATE_HOME) 270 || (networkRegWlan.getRegistrationState() 271 == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING); 272 return !isInIwlan; 273 } 274 275 /** 276 * Returns a bitmap with rounded corner. 277 * 278 * @param context application context. 279 * @param source bitmap to apply round corner. 280 * @param cornerRadius corner radius value. 281 */ convertCornerRadiusBitmap(@onNull Context context, @NonNull Bitmap source, @NonNull float cornerRadius)282 public static Bitmap convertCornerRadiusBitmap(@NonNull Context context, 283 @NonNull Bitmap source, @NonNull float cornerRadius) { 284 final Bitmap roundedBitmap = Bitmap.createBitmap(source.getWidth(), source.getHeight(), 285 Bitmap.Config.ARGB_8888); 286 final RoundedBitmapDrawable drawable = 287 RoundedBitmapDrawableFactory.create(context.getResources(), source); 288 drawable.setAntiAlias(true); 289 drawable.setCornerRadius(cornerRadius); 290 final Canvas canvas = new Canvas(roundedBitmap); 291 drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); 292 drawable.draw(canvas); 293 return roundedBitmap; 294 } 295 } 296