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