blob: ff58eba71e8db40d933558e544084a163ee921b7 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.recents.utilities;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.RectEvaluator;
import android.annotation.FloatRange;
import android.annotation.Nullable;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.os.Trace;
import android.util.ArraySet;
import android.util.IntProperty;
import android.util.Property;
import android.util.TypedValue;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.ViewRootImpl;
import android.view.ViewStub;
import com.android.systemui.shared.recents.utilities.RectFEvaluator;
import java.util.ArrayList;
import java.util.Collections;
/* Common code */
public class Utilities {
public static final Property<Drawable, Integer> DRAWABLE_ALPHA =
new IntProperty<Drawable>("drawableAlpha") {
@Override
public void setValue(Drawable object, int alpha) {
object.setAlpha(alpha);
}
@Override
public Integer get(Drawable object) {
return object.getAlpha();
}
};
public static final Property<Drawable, Rect> DRAWABLE_RECT =
new Property<Drawable, Rect>(Rect.class, "drawableBounds") {
@Override
public void set(Drawable object, Rect bounds) {
object.setBounds(bounds);
}
@Override
public Rect get(Drawable object) {
return object.getBounds();
}
};
public static final RectFEvaluator RECTF_EVALUATOR = new RectFEvaluator();
public static final RectEvaluator RECT_EVALUATOR = new RectEvaluator(new Rect());
/**
* @return the first parent walking up the view hierarchy that has the given class type.
*
* @param parentClass must be a class derived from {@link View}
*/
public static <T extends View> T findParent(View v, Class<T> parentClass) {
ViewParent parent = v.getParent();
while (parent != null) {
if (parentClass.isAssignableFrom(parent.getClass())) {
return (T) parent;
}
parent = parent.getParent();
}
return null;
}
/**
* Initializes the {@param setOut} with the given object.
*/
public static <T> ArraySet<T> objectToSet(T obj, ArraySet<T> setOut) {
setOut.clear();
if (obj != null) {
setOut.add(obj);
}
return setOut;
}
/**
* Replaces the contents of {@param setOut} with the contents of the {@param array}.
*/
public static <T> ArraySet<T> arrayToSet(T[] array, ArraySet<T> setOut) {
setOut.clear();
if (array != null) {
Collections.addAll(setOut, array);
}
return setOut;
}
/**
* @return the clamped {@param value} between the provided {@param min} and {@param max}.
*/
public static int clamp(int value, int min, int max) {
return Math.max(min, Math.min(max, value));
}
/**
* @return the clamped {@param value} between 0 and 1.
*/
public static float clamp01(float value) {
return Math.max(0f, Math.min(1f, value));
}
/**
* Scales the {@param value} to be proportionally between the {@param min} and
* {@param max} values.
*
* @param value must be between 0 and 1
*/
public static float mapRange(@FloatRange(from=0.0,to=1.0) float value, float min, float max) {
return min + (value * (max - min));
}
/**
* Scales the {@param value} proportionally from {@param min} and {@param max} to 0 and 1.
*
* @param value must be between {@param min} and {@param max}
*/
public static float unmapRange(float value, float min, float max) {
return (value - min) / (max - min);
}
/** Scales a rect about its centroid */
public static void scaleRectAboutCenter(RectF r, float scale) {
if (scale != 1.0f) {
float cx = r.centerX();
float cy = r.centerY();
r.offset(-cx, -cy);
r.left *= scale;
r.top *= scale;
r.right *= scale;
r.bottom *= scale;
r.offset(cx, cy);
}
}
/** Returns the base color overlaid with another overlay color with a specified alpha. */
public static int getColorWithOverlay(int baseColor, int overlayColor, float overlayAlpha) {
return Color.rgb(
(int) (overlayAlpha * Color.red(baseColor) +
(1f - overlayAlpha) * Color.red(overlayColor)),
(int) (overlayAlpha * Color.green(baseColor) +
(1f - overlayAlpha) * Color.green(overlayColor)),
(int) (overlayAlpha * Color.blue(baseColor) +
(1f - overlayAlpha) * Color.blue(overlayColor)));
}
/**
* Cancels an animation ensuring that if it has listeners, onCancel and onEnd
* are not called.
*/
public static void cancelAnimationWithoutCallbacks(Animator animator) {
if (animator != null && animator.isStarted()) {
removeAnimationListenersRecursive(animator);
animator.cancel();
}
}
/**
* Recursively removes all the listeners of all children of this animator
*/
public static void removeAnimationListenersRecursive(Animator animator) {
if (animator instanceof AnimatorSet) {
ArrayList<Animator> animators = ((AnimatorSet) animator).getChildAnimations();
for (int i = animators.size() - 1; i >= 0; i--) {
removeAnimationListenersRecursive(animators.get(i));
}
}
animator.removeAllListeners();
}
/**
* Sets the given {@link View}'s frame from its current translation.
*/
public static void setViewFrameFromTranslation(View v) {
RectF taskViewRect = new RectF(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
taskViewRect.offset(v.getTranslationX(), v.getTranslationY());
v.setTranslationX(0);
v.setTranslationY(0);
v.setLeftTopRightBottom((int) taskViewRect.left, (int) taskViewRect.top,
(int) taskViewRect.right, (int) taskViewRect.bottom);
}
/**
* Returns a view stub for the given view id.
*/
public static ViewStub findViewStubById(View v, int stubId) {
return (ViewStub) v.findViewById(stubId);
}
/**
* Returns a view stub for the given view id.
*/
public static ViewStub findViewStubById(Activity a, int stubId) {
return (ViewStub) a.findViewById(stubId);
}
/**
* Used for debugging, converts DP to PX.
*/
public static float dpToPx(Resources res, float dp) {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, res.getDisplayMetrics());
}
/**
* Adds a trace event for debugging.
*/
public static void addTraceEvent(String event) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, event);
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
/**
* Returns whether this view, or one of its descendants have accessibility focus.
*/
public static boolean isDescendentAccessibilityFocused(View v) {
if (v.isAccessibilityFocused()) {
return true;
}
if (v instanceof ViewGroup) {
ViewGroup vg = (ViewGroup) v;
int childCount = vg.getChildCount();
for (int i = 0; i < childCount; i++) {
if (isDescendentAccessibilityFocused(vg.getChildAt(i))) {
return true;
}
}
}
return false;
}
/**
* Returns the application configuration, which is independent of the activity's current
* configuration in multiwindow.
*/
public static Configuration getAppConfiguration(Context context) {
return context.getApplicationContext().getResources().getConfiguration();
}
/**
* @return The next frame name for the specified surface or -1 if the surface is no longer
* valid.
*/
public static long getNextFrameNumber(Surface s) {
return s != null && s.isValid()
? s.getNextFrameNumber()
: -1;
}
/**
* @return The surface for the specified view.
*/
public static @Nullable Surface getSurface(View v) {
ViewRootImpl viewRoot = v.getViewRootImpl();
if (viewRoot == null) {
return null;
}
return viewRoot.mSurface;
}
/**
* Returns a lightweight dump of a rect.
*/
public static String dumpRect(Rect r) {
if (r == null) {
return "N:0,0-0,0";
}
return r.left + "," + r.top + "-" + r.right + "," + r.bottom;
}
/**
* Posts a runnable on a handler at the front of the queue ignoring any sync barriers.
*/
public static void postAtFrontOfQueueAsynchronously(Handler h, Runnable r) {
Message msg = h.obtainMessage().setCallback(r);
h.sendMessageAtFrontOfQueue(msg);
}
/** Calculates the constrast between two colors, using the algorithm provided by the WCAG v2. */
public static float computeContrastBetweenColors(int bg, int fg) {
float bgR = Color.red(bg) / 255f;
float bgG = Color.green(bg) / 255f;
float bgB = Color.blue(bg) / 255f;
bgR = (bgR < 0.03928f) ? bgR / 12.92f : (float) Math.pow((bgR + 0.055f) / 1.055f, 2.4f);
bgG = (bgG < 0.03928f) ? bgG / 12.92f : (float) Math.pow((bgG + 0.055f) / 1.055f, 2.4f);
bgB = (bgB < 0.03928f) ? bgB / 12.92f : (float) Math.pow((bgB + 0.055f) / 1.055f, 2.4f);
float bgL = 0.2126f * bgR + 0.7152f * bgG + 0.0722f * bgB;
float fgR = Color.red(fg) / 255f;
float fgG = Color.green(fg) / 255f;
float fgB = Color.blue(fg) / 255f;
fgR = (fgR < 0.03928f) ? fgR / 12.92f : (float) Math.pow((fgR + 0.055f) / 1.055f, 2.4f);
fgG = (fgG < 0.03928f) ? fgG / 12.92f : (float) Math.pow((fgG + 0.055f) / 1.055f, 2.4f);
fgB = (fgB < 0.03928f) ? fgB / 12.92f : (float) Math.pow((fgB + 0.055f) / 1.055f, 2.4f);
float fgL = 0.2126f * fgR + 0.7152f * fgG + 0.0722f * fgB;
return Math.abs((fgL + 0.05f) / (bgL + 0.05f));
}
/**
* @return the clamped {@param value} between the provided {@param min} and {@param max}.
*/
public static float clamp(float value, float min, float max) {
return Math.max(min, Math.min(max, value));
}
}