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