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