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