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.shared.recents.utilities; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorSet; 21 import android.animation.RectEvaluator; 22 import android.annotation.FloatRange; 23 import android.annotation.Nullable; 24 import android.app.Activity; 25 import android.content.Context; 26 import android.content.res.Configuration; 27 import android.content.res.Resources; 28 import android.graphics.Color; 29 import android.graphics.Rect; 30 import android.graphics.RectF; 31 import android.graphics.drawable.Drawable; 32 import android.os.Handler; 33 import android.os.Message; 34 import android.os.Trace; 35 import android.util.ArraySet; 36 import android.util.IntProperty; 37 import android.util.Property; 38 import android.util.TypedValue; 39 import android.view.Surface; 40 import android.view.View; 41 import android.view.ViewGroup; 42 import android.view.ViewParent; 43 import android.view.ViewRootImpl; 44 import android.view.ViewStub; 45 46 import java.util.ArrayList; 47 import java.util.Collections; 48 49 /* Common code */ 50 public class Utilities { 51 52 public static final Property<Drawable, Integer> DRAWABLE_ALPHA = 53 new IntProperty<Drawable>("drawableAlpha") { 54 @Override 55 public void setValue(Drawable object, int alpha) { 56 object.setAlpha(alpha); 57 } 58 59 @Override 60 public Integer get(Drawable object) { 61 return object.getAlpha(); 62 } 63 }; 64 65 public static final Property<Drawable, Rect> DRAWABLE_RECT = 66 new Property<Drawable, Rect>(Rect.class, "drawableBounds") { 67 @Override 68 public void set(Drawable object, Rect bounds) { 69 object.setBounds(bounds); 70 } 71 72 @Override 73 public Rect get(Drawable object) { 74 return object.getBounds(); 75 } 76 }; 77 78 public static final RectFEvaluator RECTF_EVALUATOR = new RectFEvaluator(); 79 public static final RectEvaluator RECT_EVALUATOR = new RectEvaluator(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 (parentClass.isAssignableFrom(parent.getClass())) { 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 * Used for debugging, converts DP to PX. 257 */ dpToPx(Resources res, float dp)258 public static float dpToPx(Resources res, float dp) { 259 return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, res.getDisplayMetrics()); 260 } 261 262 /** 263 * Adds a trace event for debugging. 264 */ addTraceEvent(String event)265 public static void addTraceEvent(String event) { 266 Trace.traceBegin(Trace.TRACE_TAG_VIEW, event); 267 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 268 } 269 270 /** 271 * Returns whether this view, or one of its descendants have accessibility focus. 272 */ isDescendentAccessibilityFocused(View v)273 public static boolean isDescendentAccessibilityFocused(View v) { 274 if (v.isAccessibilityFocused()) { 275 return true; 276 } 277 278 if (v instanceof ViewGroup) { 279 ViewGroup vg = (ViewGroup) v; 280 int childCount = vg.getChildCount(); 281 for (int i = 0; i < childCount; i++) { 282 if (isDescendentAccessibilityFocused(vg.getChildAt(i))) { 283 return true; 284 } 285 } 286 } 287 return false; 288 } 289 290 /** 291 * Returns the application configuration, which is independent of the activity's current 292 * configuration in multiwindow. 293 */ getAppConfiguration(Context context)294 public static Configuration getAppConfiguration(Context context) { 295 return context.getApplicationContext().getResources().getConfiguration(); 296 } 297 298 /** 299 * @return The next frame name for the specified surface or -1 if the surface is no longer 300 * valid. 301 */ getNextFrameNumber(Surface s)302 public static long getNextFrameNumber(Surface s) { 303 return s != null && s.isValid() 304 ? s.getNextFrameNumber() 305 : -1; 306 307 } 308 309 /** 310 * @return The surface for the specified view. 311 */ getSurface(View v)312 public static @Nullable Surface getSurface(View v) { 313 ViewRootImpl viewRoot = v.getViewRootImpl(); 314 if (viewRoot == null) { 315 return null; 316 } 317 return viewRoot.mSurface; 318 } 319 320 /** 321 * Returns a lightweight dump of a rect. 322 */ dumpRect(Rect r)323 public static String dumpRect(Rect r) { 324 if (r == null) { 325 return "N:0,0-0,0"; 326 } 327 return r.left + "," + r.top + "-" + r.right + "," + r.bottom; 328 } 329 330 /** 331 * Posts a runnable on a handler at the front of the queue ignoring any sync barriers. 332 */ postAtFrontOfQueueAsynchronously(Handler h, Runnable r)333 public static void postAtFrontOfQueueAsynchronously(Handler h, Runnable r) { 334 Message msg = h.obtainMessage().setCallback(r); 335 h.sendMessageAtFrontOfQueue(msg); 336 } 337 } 338