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
17package com.android.systemui.recents.misc;
18
19import android.animation.Animator;
20import android.animation.AnimatorSet;
21import android.animation.RectEvaluator;
22import android.annotation.FloatRange;
23import android.app.Activity;
24import android.content.Context;
25import android.content.res.Configuration;
26import android.content.res.Resources;
27import android.graphics.Color;
28import android.graphics.Rect;
29import android.graphics.RectF;
30import android.graphics.drawable.Drawable;
31import android.os.Trace;
32import android.util.ArraySet;
33import android.util.IntProperty;
34import android.util.Property;
35import android.util.TypedValue;
36import android.view.View;
37import android.view.ViewGroup;
38import android.view.ViewParent;
39import android.view.ViewStub;
40
41import com.android.systemui.recents.model.Task;
42import com.android.systemui.recents.views.TaskViewTransform;
43
44import java.util.ArrayList;
45import java.util.Collections;
46import java.util.List;
47
48/* Common code */
49public 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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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 */
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. */
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. */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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