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