Utilities.java revision 353c0b91daa97fa87f077be0f403fe7db8dedb40
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.content.Intent;
21import android.graphics.Color;
22import android.graphics.Matrix;
23import android.graphics.Rect;
24import android.view.View;
25import com.android.systemui.recents.RecentsConfiguration;
26
27import java.lang.reflect.InvocationTargetException;
28import java.lang.reflect.Method;
29import java.util.ArrayList;
30
31/* Common code */
32public class Utilities {
33
34    // Reflection methods for altering shadows
35    private static Method sPropertyMethod;
36    static {
37        try {
38            Class<?> c = Class.forName("android.view.GLES20Canvas");
39            sPropertyMethod = c.getDeclaredMethod("setProperty", String.class, String.class);
40            if (!sPropertyMethod.isAccessible()) sPropertyMethod.setAccessible(true);
41        } catch (ClassNotFoundException e) {
42            e.printStackTrace();
43        } catch (NoSuchMethodException e) {
44            e.printStackTrace();
45        }
46    }
47
48    /**
49     * Calculates a consistent animation duration (ms) for all animations depending on the movement
50     * of the object being animated.
51     */
52    public static int calculateTranslationAnimationDuration(int distancePx) {
53        return calculateTranslationAnimationDuration(distancePx, 100);
54    }
55    public static int calculateTranslationAnimationDuration(int distancePx, int minDuration) {
56        RecentsConfiguration config = RecentsConfiguration.getInstance();
57        return Math.max(minDuration, (int) (1000f /* ms/s */ *
58                (Math.abs(distancePx) / config.animationPxMovementPerSecond)));
59    }
60
61    /** Scales a rect about its centroid */
62    public static void scaleRectAboutCenter(Rect r, float scale) {
63        if (scale != 1.0f) {
64            int cx = r.centerX();
65            int cy = r.centerY();
66            r.offset(-cx, -cy);
67            r.left = (int) (r.left * scale + 0.5f);
68            r.top = (int) (r.top * scale + 0.5f);
69            r.right = (int) (r.right * scale + 0.5f);
70            r.bottom = (int) (r.bottom * scale + 0.5f);
71            r.offset(cx, cy);
72        }
73    }
74
75    /** Maps a coorindate in a descendant view into the parent. */
76    public static float mapCoordInDescendentToSelf(View descendant, View root,
77            float[] coord, boolean includeRootScroll) {
78        ArrayList<View> ancestorChain = new ArrayList<View>();
79
80        float[] pt = {coord[0], coord[1]};
81
82        View v = descendant;
83        while(v != root && v != null) {
84            ancestorChain.add(v);
85            v = (View) v.getParent();
86        }
87        ancestorChain.add(root);
88
89        float scale = 1.0f;
90        int count = ancestorChain.size();
91        for (int i = 0; i < count; i++) {
92            View v0 = ancestorChain.get(i);
93            // For TextViews, scroll has a meaning which relates to the text position
94            // which is very strange... ignore the scroll.
95            if (v0 != descendant || includeRootScroll) {
96                pt[0] -= v0.getScrollX();
97                pt[1] -= v0.getScrollY();
98            }
99
100            v0.getMatrix().mapPoints(pt);
101            pt[0] += v0.getLeft();
102            pt[1] += v0.getTop();
103            scale *= v0.getScaleX();
104        }
105
106        coord[0] = pt[0];
107        coord[1] = pt[1];
108        return scale;
109    }
110
111    /** Maps a coordinate in the root to a descendent. */
112    public static float mapCoordInSelfToDescendent(View descendant, View root,
113            float[] coord, Matrix tmpInverseMatrix) {
114        ArrayList<View> ancestorChain = new ArrayList<View>();
115
116        float[] pt = {coord[0], coord[1]};
117
118        View v = descendant;
119        while(v != root) {
120            ancestorChain.add(v);
121            v = (View) v.getParent();
122        }
123        ancestorChain.add(root);
124
125        float scale = 1.0f;
126        int count = ancestorChain.size();
127        tmpInverseMatrix.set(Matrix.IDENTITY_MATRIX);
128        for (int i = count - 1; i >= 0; i--) {
129            View ancestor = ancestorChain.get(i);
130            View next = i > 0 ? ancestorChain.get(i-1) : null;
131
132            pt[0] += ancestor.getScrollX();
133            pt[1] += ancestor.getScrollY();
134
135            if (next != null) {
136                pt[0] -= next.getLeft();
137                pt[1] -= next.getTop();
138                next.getMatrix().invert(tmpInverseMatrix);
139                tmpInverseMatrix.mapPoints(pt);
140                scale *= next.getScaleX();
141            }
142        }
143
144        coord[0] = pt[0];
145        coord[1] = pt[1];
146        return scale;
147    }
148
149    /** Calculates the constrast between two colors, using the algorithm provided by the WCAG v2. */
150    public static float computeContrastBetweenColors(int bg, int fg) {
151        float bgR = Color.red(bg) / 255f;
152        float bgG = Color.green(bg) / 255f;
153        float bgB = Color.blue(bg) / 255f;
154        bgR = (bgR < 0.03928f) ? bgR / 12.92f : (float) Math.pow((bgR + 0.055f) / 1.055f, 2.4f);
155        bgG = (bgG < 0.03928f) ? bgG / 12.92f : (float) Math.pow((bgG + 0.055f) / 1.055f, 2.4f);
156        bgB = (bgB < 0.03928f) ? bgB / 12.92f : (float) Math.pow((bgB + 0.055f) / 1.055f, 2.4f);
157        float bgL = 0.2126f * bgR + 0.7152f * bgG + 0.0722f * bgB;
158
159        float fgR = Color.red(fg) / 255f;
160        float fgG = Color.green(fg) / 255f;
161        float fgB = Color.blue(fg) / 255f;
162        fgR = (fgR < 0.03928f) ? fgR / 12.92f : (float) Math.pow((fgR + 0.055f) / 1.055f, 2.4f);
163        fgG = (fgG < 0.03928f) ? fgG / 12.92f : (float) Math.pow((fgG + 0.055f) / 1.055f, 2.4f);
164        fgB = (fgB < 0.03928f) ? fgB / 12.92f : (float) Math.pow((fgB + 0.055f) / 1.055f, 2.4f);
165        float fgL = 0.2126f * fgR + 0.7152f * fgG + 0.0722f * fgB;
166
167        return Math.abs((fgL + 0.05f) / (bgL + 0.05f));
168    }
169
170    /** Returns the base color overlaid with another overlay color with a specified alpha. */
171    public static int getColorWithOverlay(int baseColor, int overlayColor, float overlayAlpha) {
172        return Color.rgb(
173            (int) (overlayAlpha * Color.red(baseColor) +
174                    (1f - overlayAlpha) * Color.red(overlayColor)),
175            (int) (overlayAlpha * Color.green(baseColor) +
176                    (1f - overlayAlpha) * Color.green(overlayColor)),
177            (int) (overlayAlpha * Color.blue(baseColor) +
178                    (1f - overlayAlpha) * Color.blue(overlayColor)));
179    }
180
181    /** Sets some private shadow properties. */
182    public static void setShadowProperty(String property, String value)
183            throws IllegalAccessException, InvocationTargetException {
184        sPropertyMethod.invoke(null, property, value);
185    }
186
187    /** Returns whether the specified intent is a document. */
188    public static boolean isDocument(Intent intent) {
189        int flags = intent.getFlags();
190        return (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) == Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
191    }
192
193    /**
194     * Cancels an animation ensuring that if it has listeners, onCancel and onEnd
195     * are not called.
196     */
197    public static void cancelAnimationWithoutCallbacks(Animator animator) {
198        if (animator != null) {
199            animator.removeAllListeners();
200            animator.cancel();
201        }
202    }
203}
204