1 /*
2  * Copyright (C) 2014 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.systemui.recents.utilities;
18 
19 import android.animation.Animator;
20 import android.animation.AnimatorSet;
21 import android.animation.RectEvaluator;
22 import android.annotation.FloatRange;
23 import android.annotation.Nullable;
24 import android.app.Activity;
25 import android.content.Context;
26 import android.content.res.Configuration;
27 import android.content.res.Resources;
28 import android.graphics.Color;
29 import android.graphics.Rect;
30 import android.graphics.RectF;
31 import android.graphics.drawable.Drawable;
32 import android.os.Handler;
33 import android.os.Message;
34 import android.os.Trace;
35 import android.util.ArraySet;
36 import android.util.IntProperty;
37 import android.util.Property;
38 import android.util.TypedValue;
39 import android.view.Surface;
40 import android.view.View;
41 import android.view.ViewGroup;
42 import android.view.ViewParent;
43 import android.view.ViewRootImpl;
44 import android.view.ViewStub;
45 
46 import com.android.systemui.shared.recents.utilities.RectFEvaluator;
47 import java.util.ArrayList;
48 import java.util.Collections;
49 
50 /* Common code */
51 public class Utilities {
52 
53     public static final Property<Drawable, Integer> DRAWABLE_ALPHA =
54             new IntProperty<Drawable>("drawableAlpha") {
55                 @Override
56                 public void setValue(Drawable object, int alpha) {
57                     object.setAlpha(alpha);
58                 }
59 
60                 @Override
61                 public Integer get(Drawable object) {
62                     return object.getAlpha();
63                 }
64             };
65 
66     public static final Property<Drawable, Rect> DRAWABLE_RECT =
67             new Property<Drawable, Rect>(Rect.class, "drawableBounds") {
68                 @Override
69                 public void set(Drawable object, Rect bounds) {
70                     object.setBounds(bounds);
71                 }
72 
73                 @Override
74                 public Rect get(Drawable object) {
75                     return object.getBounds();
76                 }
77             };
78 
79     public static final RectFEvaluator RECTF_EVALUATOR = new RectFEvaluator();
80     public static final RectEvaluator RECT_EVALUATOR = new RectEvaluator(new Rect());
81 
82     /**
83      * @return the first parent walking up the view hierarchy that has the given class type.
84      *
85      * @param parentClass must be a class derived from {@link View}
86      */
findParent(View v, Class<T> parentClass)87     public static <T extends View> T findParent(View v, Class<T> parentClass) {
88         ViewParent parent = v.getParent();
89         while (parent != null) {
90             if (parentClass.isAssignableFrom(parent.getClass())) {
91                 return (T) parent;
92             }
93             parent = parent.getParent();
94         }
95         return null;
96     }
97 
98     /**
99      * Initializes the {@param setOut} with the given object.
100      */
objectToSet(T obj, ArraySet<T> setOut)101     public static <T> ArraySet<T> objectToSet(T obj, ArraySet<T> setOut) {
102         setOut.clear();
103         if (obj != null) {
104             setOut.add(obj);
105         }
106         return setOut;
107     }
108 
109     /**
110      * Replaces the contents of {@param setOut} with the contents of the {@param array}.
111      */
arrayToSet(T[] array, ArraySet<T> setOut)112     public static <T> ArraySet<T> arrayToSet(T[] array, ArraySet<T> setOut) {
113         setOut.clear();
114         if (array != null) {
115             Collections.addAll(setOut, array);
116         }
117         return setOut;
118     }
119 
120     /**
121      * @return the clamped {@param value} between the provided {@param min} and {@param max}.
122      */
clamp(int value, int min, int max)123     public static int clamp(int value, int min, int max) {
124         return Math.max(min, Math.min(max, value));
125     }
126 
127     /**
128      * @return the clamped {@param value} between 0 and 1.
129      */
clamp01(float value)130     public static float clamp01(float value) {
131         return Math.max(0f, Math.min(1f, value));
132     }
133 
134     /**
135      * Scales the {@param value} to be proportionally between the {@param min} and
136      * {@param max} values.
137      *
138      * @param value must be between 0 and 1
139      */
mapRange(@loatRangefrom=0.0,to=1.0) float value, float min, float max)140     public static float mapRange(@FloatRange(from=0.0,to=1.0) float value, float min, float max) {
141         return min + (value * (max - min));
142     }
143 
144     /**
145      * Scales the {@param value} proportionally from {@param min} and {@param max} to 0 and 1.
146      *
147      * @param value must be between {@param min} and {@param max}
148      */
unmapRange(float value, float min, float max)149     public static float unmapRange(float value, float min, float max) {
150         return (value - min) / (max - min);
151     }
152 
153     /** Scales a rect about its centroid */
scaleRectAboutCenter(RectF r, float scale)154     public static void scaleRectAboutCenter(RectF r, float scale) {
155         if (scale != 1.0f) {
156             float cx = r.centerX();
157             float cy = r.centerY();
158             r.offset(-cx, -cy);
159             r.left *= scale;
160             r.top *= scale;
161             r.right *= scale;
162             r.bottom *= scale;
163             r.offset(cx, cy);
164         }
165     }
166 
167     /** Returns the base color overlaid with another overlay color with a specified alpha. */
getColorWithOverlay(int baseColor, int overlayColor, float overlayAlpha)168     public static int getColorWithOverlay(int baseColor, int overlayColor, float overlayAlpha) {
169         return Color.rgb(
170             (int) (overlayAlpha * Color.red(baseColor) +
171                     (1f - overlayAlpha) * Color.red(overlayColor)),
172             (int) (overlayAlpha * Color.green(baseColor) +
173                     (1f - overlayAlpha) * Color.green(overlayColor)),
174             (int) (overlayAlpha * Color.blue(baseColor) +
175                     (1f - overlayAlpha) * Color.blue(overlayColor)));
176     }
177 
178     /**
179      * Cancels an animation ensuring that if it has listeners, onCancel and onEnd
180      * are not called.
181      */
cancelAnimationWithoutCallbacks(Animator animator)182     public static void cancelAnimationWithoutCallbacks(Animator animator) {
183         if (animator != null && animator.isStarted()) {
184             removeAnimationListenersRecursive(animator);
185             animator.cancel();
186         }
187     }
188 
189     /**
190      * Recursively removes all the listeners of all children of this animator
191      */
removeAnimationListenersRecursive(Animator animator)192     public static void removeAnimationListenersRecursive(Animator animator) {
193         if (animator instanceof AnimatorSet) {
194             ArrayList<Animator> animators = ((AnimatorSet) animator).getChildAnimations();
195             for (int i = animators.size() - 1; i >= 0; i--) {
196                 removeAnimationListenersRecursive(animators.get(i));
197             }
198         }
199         animator.removeAllListeners();
200     }
201 
202     /**
203      * Sets the given {@link View}'s frame from its current translation.
204      */
setViewFrameFromTranslation(View v)205     public static void setViewFrameFromTranslation(View v) {
206         RectF taskViewRect = new RectF(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
207         taskViewRect.offset(v.getTranslationX(), v.getTranslationY());
208         v.setTranslationX(0);
209         v.setTranslationY(0);
210         v.setLeftTopRightBottom((int) taskViewRect.left, (int) taskViewRect.top,
211                 (int) taskViewRect.right, (int) taskViewRect.bottom);
212     }
213 
214     /**
215      * Returns a view stub for the given view id.
216      */
findViewStubById(View v, int stubId)217     public static ViewStub findViewStubById(View v, int stubId) {
218         return (ViewStub) v.findViewById(stubId);
219     }
220 
221     /**
222      * Returns a view stub for the given view id.
223      */
findViewStubById(Activity a, int stubId)224     public static ViewStub findViewStubById(Activity a, int stubId) {
225         return (ViewStub) a.findViewById(stubId);
226     }
227 
228     /**
229      * Used for debugging, converts DP to PX.
230      */
dpToPx(Resources res, float dp)231     public static float dpToPx(Resources res, float dp) {
232         return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, res.getDisplayMetrics());
233     }
234 
235     /**
236      * Adds a trace event for debugging.
237      */
addTraceEvent(String event)238     public static void addTraceEvent(String event) {
239         Trace.traceBegin(Trace.TRACE_TAG_VIEW, event);
240         Trace.traceEnd(Trace.TRACE_TAG_VIEW);
241     }
242 
243     /**
244      * Returns whether this view, or one of its descendants have accessibility focus.
245      */
isDescendentAccessibilityFocused(View v)246     public static boolean isDescendentAccessibilityFocused(View v) {
247         if (v.isAccessibilityFocused()) {
248             return true;
249         }
250 
251         if (v instanceof ViewGroup) {
252             ViewGroup vg = (ViewGroup) v;
253             int childCount = vg.getChildCount();
254             for (int i = 0; i < childCount; i++) {
255                 if (isDescendentAccessibilityFocused(vg.getChildAt(i))) {
256                     return true;
257                 }
258             }
259         }
260         return false;
261     }
262 
263     /**
264      * Returns the application configuration, which is independent of the activity's current
265      * configuration in multiwindow.
266      */
getAppConfiguration(Context context)267     public static Configuration getAppConfiguration(Context context) {
268         return context.getApplicationContext().getResources().getConfiguration();
269     }
270 
271     /**
272      * @return The next frame name for the specified surface or -1 if the surface is no longer
273      *         valid.
274      */
getNextFrameNumber(Surface s)275     public static long getNextFrameNumber(Surface s) {
276         return s != null && s.isValid()
277                 ? s.getNextFrameNumber()
278                 : -1;
279 
280     }
281 
282     /**
283      * @return The surface for the specified view.
284      */
getSurface(View v)285     public static @Nullable Surface getSurface(View v) {
286         ViewRootImpl viewRoot = v.getViewRootImpl();
287         if (viewRoot == null) {
288             return null;
289         }
290         return viewRoot.mSurface;
291     }
292 
293     /**
294      * Returns a lightweight dump of a rect.
295      */
dumpRect(Rect r)296     public static String dumpRect(Rect r) {
297         if (r == null) {
298             return "N:0,0-0,0";
299         }
300         return r.left + "," + r.top + "-" + r.right + "," + r.bottom;
301     }
302 
303     /**
304      * Posts a runnable on a handler at the front of the queue ignoring any sync barriers.
305      */
postAtFrontOfQueueAsynchronously(Handler h, Runnable r)306     public static void postAtFrontOfQueueAsynchronously(Handler h, Runnable r) {
307         Message msg = h.obtainMessage().setCallback(r);
308         h.sendMessageAtFrontOfQueue(msg);
309     }
310 
311     /** Calculates the constrast between two colors, using the algorithm provided by the WCAG v2. */
computeContrastBetweenColors(int bg, int fg)312     public static float computeContrastBetweenColors(int bg, int fg) {
313         float bgR = Color.red(bg) / 255f;
314         float bgG = Color.green(bg) / 255f;
315         float bgB = Color.blue(bg) / 255f;
316         bgR = (bgR < 0.03928f) ? bgR / 12.92f : (float) Math.pow((bgR + 0.055f) / 1.055f, 2.4f);
317         bgG = (bgG < 0.03928f) ? bgG / 12.92f : (float) Math.pow((bgG + 0.055f) / 1.055f, 2.4f);
318         bgB = (bgB < 0.03928f) ? bgB / 12.92f : (float) Math.pow((bgB + 0.055f) / 1.055f, 2.4f);
319         float bgL = 0.2126f * bgR + 0.7152f * bgG + 0.0722f * bgB;
320 
321         float fgR = Color.red(fg) / 255f;
322         float fgG = Color.green(fg) / 255f;
323         float fgB = Color.blue(fg) / 255f;
324         fgR = (fgR < 0.03928f) ? fgR / 12.92f : (float) Math.pow((fgR + 0.055f) / 1.055f, 2.4f);
325         fgG = (fgG < 0.03928f) ? fgG / 12.92f : (float) Math.pow((fgG + 0.055f) / 1.055f, 2.4f);
326         fgB = (fgB < 0.03928f) ? fgB / 12.92f : (float) Math.pow((fgB + 0.055f) / 1.055f, 2.4f);
327         float fgL = 0.2126f * fgR + 0.7152f * fgG + 0.0722f * fgB;
328 
329         return Math.abs((fgL + 0.05f) / (bgL + 0.05f));
330     }
331 
332     /**
333      * @return the clamped {@param value} between the provided {@param min} and {@param max}.
334      */
clamp(float value, float min, float max)335     public static float clamp(float value, float min, float max) {
336         return Math.max(min, Math.min(max, value));
337     }
338 
339 }
340