1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy 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, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tv.settings.library.util; 18 19 import android.annotation.ColorInt; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.pm.ApplicationInfo; 23 import android.content.pm.PackageInfo; 24 import android.content.pm.PackageManager; 25 import android.content.pm.ResolveInfo; 26 import android.content.pm.Signature; 27 import android.content.res.ColorStateList; 28 import android.content.res.Resources; 29 import android.content.res.TypedArray; 30 import android.graphics.Bitmap; 31 import android.graphics.Canvas; 32 import android.graphics.Color; 33 import android.graphics.ColorFilter; 34 import android.graphics.ColorMatrix; 35 import android.graphics.ColorMatrixColorFilter; 36 import android.graphics.drawable.Drawable; 37 import android.os.BatteryManager; 38 import android.os.UserHandle; 39 import android.print.PrintManager; 40 import android.text.TextUtils; 41 import android.widget.Toast; 42 43 import androidx.annotation.NonNull; 44 import androidx.core.graphics.drawable.RoundedBitmapDrawable; 45 import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; 46 47 import com.android.internal.annotations.VisibleForTesting; 48 49 import java.text.NumberFormat; 50 51 public class LibUtils { 52 53 @VisibleForTesting 54 static final String STORAGE_MANAGER_ENABLED_PROPERTY = 55 "ro.storage_manager.enabled"; 56 57 58 public static final String PROPERTY_APP_HIBERNATION_ENABLED = 59 "app_hibernation_enabled"; 60 61 private static Signature[] sSystemSignature; 62 private static String sPermissionControllerPackageName; 63 private static String sServicesSystemSharedLibPackageName; 64 private static String sSharedSystemSharedLibPackageName; 65 66 67 68 /** Formats a double from 0.0..100.0 with an option to round **/ formatPercentage(double percentage, boolean round)69 public static String formatPercentage(double percentage, boolean round) { 70 final int localPercentage = round ? Math.round((float) percentage) : (int) percentage; 71 return formatPercentage(localPercentage); 72 } 73 74 /** Formats the ratio of amount/total as a percentage. */ formatPercentage(long amount, long total)75 public static String formatPercentage(long amount, long total) { 76 return formatPercentage(((double) amount) / total); 77 } 78 79 /** Formats an integer from 0..100 as a percentage. */ formatPercentage(int percentage)80 public static String formatPercentage(int percentage) { 81 return formatPercentage(((double) percentage) / 100.0); 82 } 83 84 /** Formats a double from 0.0..1.0 as a percentage. */ formatPercentage(double percentage)85 public static String formatPercentage(double percentage) { 86 return NumberFormat.getPercentInstance().format(percentage); 87 } 88 getBatteryLevel(Intent batteryChangedIntent)89 public static int getBatteryLevel(Intent batteryChangedIntent) { 90 int level = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0); 91 int scale = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 100); 92 return (level * 100) / scale; 93 } 94 getColorAccent(Context context)95 public static ColorStateList getColorAccent(Context context) { 96 return getColorAttr(context, android.R.attr.colorAccent); 97 } 98 getColorError(Context context)99 public static ColorStateList getColorError(Context context) { 100 return getColorAttr(context, android.R.attr.colorError); 101 } 102 103 @ColorInt getColorAccentDefaultColor(Context context)104 public static int getColorAccentDefaultColor(Context context) { 105 return getColorAttrDefaultColor(context, android.R.attr.colorAccent); 106 } 107 108 @ColorInt getColorErrorDefaultColor(Context context)109 public static int getColorErrorDefaultColor(Context context) { 110 return getColorAttrDefaultColor(context, android.R.attr.colorError); 111 } 112 113 @ColorInt getColorStateListDefaultColor(Context context, int resId)114 public static int getColorStateListDefaultColor(Context context, int resId) { 115 final ColorStateList list = 116 context.getResources().getColorStateList(resId, context.getTheme()); 117 return list.getDefaultColor(); 118 } 119 120 /** 121 * This method computes disabled color from normal color 122 * 123 * @param context the context 124 * @param inputColor normal color. 125 * @return disabled color. 126 */ 127 @ColorInt getDisabled(Context context, int inputColor)128 public static int getDisabled(Context context, int inputColor) { 129 return applyAlphaAttr(context, android.R.attr.disabledAlpha, inputColor); 130 } 131 132 @ColorInt applyAlphaAttr(Context context, int attr, int inputColor)133 public static int applyAlphaAttr(Context context, int attr, int inputColor) { 134 TypedArray ta = context.obtainStyledAttributes(new int[]{attr}); 135 float alpha = ta.getFloat(0, 0); 136 ta.recycle(); 137 return applyAlpha(alpha, inputColor); 138 } 139 140 @ColorInt applyAlpha(float alpha, int inputColor)141 public static int applyAlpha(float alpha, int inputColor) { 142 alpha *= Color.alpha(inputColor); 143 return Color.argb((int) (alpha), Color.red(inputColor), Color.green(inputColor), 144 Color.blue(inputColor)); 145 } 146 147 @ColorInt getColorAttrDefaultColor(Context context, int attr)148 public static int getColorAttrDefaultColor(Context context, int attr) { 149 TypedArray ta = context.obtainStyledAttributes(new int[]{attr}); 150 @ColorInt int colorAccent = ta.getColor(0, 0); 151 ta.recycle(); 152 return colorAccent; 153 } 154 getColorAttr(Context context, int attr)155 public static ColorStateList getColorAttr(Context context, int attr) { 156 TypedArray ta = context.obtainStyledAttributes(new int[]{attr}); 157 ColorStateList stateList = null; 158 try { 159 stateList = ta.getColorStateList(0); 160 } finally { 161 ta.recycle(); 162 } 163 return stateList; 164 } 165 getThemeAttr(Context context, int attr)166 public static int getThemeAttr(Context context, int attr) { 167 return getThemeAttr(context, attr, 0); 168 } 169 getThemeAttr(Context context, int attr, int defaultValue)170 public static int getThemeAttr(Context context, int attr, int defaultValue) { 171 TypedArray ta = context.obtainStyledAttributes(new int[]{attr}); 172 int theme = ta.getResourceId(0, defaultValue); 173 ta.recycle(); 174 return theme; 175 } 176 getDrawable(Context context, int attr)177 public static Drawable getDrawable(Context context, int attr) { 178 TypedArray ta = context.obtainStyledAttributes(new int[]{attr}); 179 Drawable drawable = ta.getDrawable(0); 180 ta.recycle(); 181 return drawable; 182 } 183 184 /** 185 * Create a color matrix suitable for a ColorMatrixColorFilter that modifies only the color but 186 * preserves the alpha for a given drawable 187 * 188 * @return a color matrix that uses the source alpha and given color 189 */ getAlphaInvariantColorMatrixForColor(@olorInt int color)190 public static ColorMatrix getAlphaInvariantColorMatrixForColor(@ColorInt int color) { 191 int r = Color.red(color); 192 int g = Color.green(color); 193 int b = Color.blue(color); 194 195 ColorMatrix cm = new ColorMatrix(new float[]{ 196 0, 0, 0, 0, r, 197 0, 0, 0, 0, g, 198 0, 0, 0, 0, b, 199 0, 0, 0, 1, 0}); 200 201 return cm; 202 } 203 204 /** 205 * Create a ColorMatrixColorFilter to tint a drawable but retain its alpha characteristics 206 * 207 * @return a ColorMatrixColorFilter which changes the color of the output but is invariant on 208 * the source alpha 209 */ getAlphaInvariantColorFilterForColor(@olorInt int color)210 public static ColorFilter getAlphaInvariantColorFilterForColor(@ColorInt int color) { 211 return new ColorMatrixColorFilter(getAlphaInvariantColorMatrixForColor(color)); 212 } 213 214 /** 215 * Determine whether a package is a "system package", in which case certain things (like 216 * disabling notifications or disabling the package altogether) should be disallowed. 217 */ isSystemPackage(Resources resources, PackageManager pm, PackageInfo pkg)218 public static boolean isSystemPackage(Resources resources, PackageManager pm, PackageInfo pkg) { 219 if (sSystemSignature == null) { 220 sSystemSignature = new Signature[]{getSystemSignature(pm)}; 221 } 222 if (sPermissionControllerPackageName == null) { 223 sPermissionControllerPackageName = pm.getPermissionControllerPackageName(); 224 } 225 if (sServicesSystemSharedLibPackageName == null) { 226 sServicesSystemSharedLibPackageName = pm.getServicesSystemSharedLibraryPackageName(); 227 } 228 if (sSharedSystemSharedLibPackageName == null) { 229 sSharedSystemSharedLibPackageName = pm.getSharedSystemSharedLibraryPackageName(); 230 } 231 return (sSystemSignature[0] != null 232 && sSystemSignature[0].equals(getFirstSignature(pkg))) 233 || pkg.packageName.equals(sPermissionControllerPackageName) 234 || pkg.packageName.equals(sServicesSystemSharedLibPackageName) 235 || pkg.packageName.equals(sSharedSystemSharedLibPackageName) 236 || pkg.packageName.equals(PrintManager.PRINT_SPOOLER_PACKAGE_NAME) 237 || isDeviceProvisioningPackage(resources, pkg.packageName); 238 } 239 240 /** 241 * Returns {@code true} if the supplied package is the device provisioning app. Otherwise, 242 * returns {@code false}. 243 */ isDeviceProvisioningPackage(Resources resources, String packageName)244 public static boolean isDeviceProvisioningPackage(Resources resources, String packageName) { 245 String deviceProvisioningPackage = resources.getString( 246 resources.getIdentifier("config_deviceProvisioningPackage", 247 "string", "android")); 248 return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName); 249 } 250 getFirstSignature(PackageInfo pkg)251 private static Signature getFirstSignature(PackageInfo pkg) { 252 if (pkg != null && pkg.signatures != null && pkg.signatures.length > 0) { 253 return pkg.signatures[0]; 254 } 255 return null; 256 } 257 getSystemSignature(PackageManager pm)258 private static Signature getSystemSignature(PackageManager pm) { 259 try { 260 final PackageInfo sys = pm.getPackageInfo("android", PackageManager.GET_SIGNATURES); 261 return getFirstSignature(sys); 262 } catch (PackageManager.NameNotFoundException e) { 263 } 264 return null; 265 } 266 267 268 /** Get the corresponding adaptive icon drawable. */ getBadgedIcon(Context context, Drawable icon, UserHandle user)269 public static Drawable getBadgedIcon(Context context, Drawable icon, UserHandle user) { 270 return icon; 271 } 272 273 /** Get the {@link Drawable} that represents the app icon */ getBadgedIcon(Context context, ApplicationInfo appInfo)274 public static Drawable getBadgedIcon(Context context, ApplicationInfo appInfo) { 275 return getBadgedIcon(context, appInfo.loadUnbadgedIcon(context.getPackageManager()), 276 UserHandle.getUserHandleForUid(appInfo.uid)); 277 } 278 279 /** 280 * Returns a bitmap with rounded corner. 281 * 282 * @param context application context. 283 * @param source bitmap to apply round corner. 284 * @param cornerRadius corner radius value. 285 */ convertCornerRadiusBitmap(@onNull Context context, @NonNull Bitmap source, @NonNull float cornerRadius)286 public static Bitmap convertCornerRadiusBitmap(@NonNull Context context, 287 @NonNull Bitmap source, @NonNull float cornerRadius) { 288 final Bitmap roundedBitmap = Bitmap.createBitmap(source.getWidth(), source.getHeight(), 289 Bitmap.Config.ARGB_8888); 290 final RoundedBitmapDrawable drawable = 291 RoundedBitmapDrawableFactory.create(context.getResources(), source); 292 drawable.setAntiAlias(true); 293 drawable.setCornerRadius(cornerRadius); 294 final Canvas canvas = new Canvas(roundedBitmap); 295 drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); 296 drawable.draw(canvas); 297 return roundedBitmap; 298 } 299 300 /** 301 * Returns the ResolveInfo for the system activity that matches given intent filter or null if 302 * no such activity exists. 303 * 304 * @param context Context of the caller 305 * @param intent The intent matching the desired system app 306 * @return ResolveInfo of the matching activity or null if no match exists 307 */ systemIntentIsHandled(Context context, Intent intent)308 public static ResolveInfo systemIntentIsHandled(Context context, Intent intent) { 309 if (intent == null) { 310 return null; 311 } 312 313 final PackageManager pm = context.getPackageManager(); 314 for (ResolveInfo info : pm.queryIntentActivities(intent, 0)) { 315 if (info.activityInfo != null 316 && (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) 317 == ApplicationInfo.FLAG_SYSTEM) { 318 return info; 319 } 320 } 321 return null; 322 } 323 showToast(Context context, String resName)324 public static void showToast(Context context, String resName) { 325 String toast = ResourcesUtil.getString(context, resName); 326 if (!TextUtils.isEmpty(toast)) { 327 Toast.makeText(context, toast, Toast.LENGTH_SHORT).show(); 328 } 329 } 330 } 331