mirror of
https://gitlab.com/android_translation_layer/android_translation_layer.git
synced 2025-04-28 12:17:57 +03:00
borrow relevant parts of AOSP commit dbee9bb342cdfaa5155b1918f90262c05e2464cb
Our SVG-based VectorDrawable implementation still relies on `getDefaultColor` but exceptions are no longer thrown when parsing VectorDrawable xml files which use gradients as colors.
This commit is contained in:
parent
7d59407138
commit
279c95becb
17 changed files with 3074 additions and 420 deletions
|
@ -0,0 +1,33 @@
|
|||
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||
#include <jni.h>
|
||||
/* Header for class android_graphics_SweepGradient */
|
||||
|
||||
#ifndef _Included_android_graphics_SweepGradient
|
||||
#define _Included_android_graphics_SweepGradient
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#undef android_graphics_SweepGradient_TYPE_COLORS_AND_POSITIONS
|
||||
#define android_graphics_SweepGradient_TYPE_COLORS_AND_POSITIONS 1L
|
||||
#undef android_graphics_SweepGradient_TYPE_COLOR_START_AND_COLOR_END
|
||||
#define android_graphics_SweepGradient_TYPE_COLOR_START_AND_COLOR_END 2L
|
||||
/*
|
||||
* Class: android_graphics_SweepGradient
|
||||
* Method: nativeCreate1
|
||||
* Signature: (FF[I[F)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_android_graphics_SweepGradient_nativeCreate1
|
||||
(JNIEnv *, jclass, jfloat, jfloat, jintArray, jfloatArray);
|
||||
|
||||
/*
|
||||
* Class: android_graphics_SweepGradient
|
||||
* Method: nativeCreate2
|
||||
* Signature: (FFII)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_android_graphics_SweepGradient_nativeCreate2
|
||||
(JNIEnv *, jclass, jfloat, jfloat, jint, jint);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -817,8 +817,6 @@ public final class AssetManager {
|
|||
int defStyleRes, int[] inValues,
|
||||
int[] inAttrs, int[] outValues,
|
||||
int[] outIndices);
|
||||
/*package*/ native final boolean retrieveAttributes(int xmlParser, int[] inAttrs,
|
||||
int[] outValues, int[] outIndices);
|
||||
/*package*/ native final int getArraySize(int resource);
|
||||
/*package*/ native final int retrieveArray(int resource, int[] outValues);
|
||||
private native final int getStringBlockCount();
|
||||
|
|
|
@ -16,12 +16,22 @@
|
|||
|
||||
package android.content.res;
|
||||
|
||||
import android.content.Context;
|
||||
//import android.annotation.ColorInt;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.res.Resources.Theme;
|
||||
import android.graphics.Color;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.MathUtils;
|
||||
import android.util.SparseArray;
|
||||
import android.util.StateSet;
|
||||
import android.util.Xml;
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.internal.util.GrowingArrayUtils;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Arrays;
|
||||
|
@ -55,106 +65,174 @@ import org.xmlpull.v1.XmlPullParserException;
|
|||
* href="{@docRoot}guide/topics/resources/color-list-resource.html">Color State
|
||||
* List Resource</a>.</p>
|
||||
*/
|
||||
public class ColorStateList {
|
||||
|
||||
private int[][] mStateSpecs; // must be parallel to mColors
|
||||
private int[] mColors; // must be parallel to mStateSpecs
|
||||
private int mDefaultColor = 0xffff0000;
|
||||
public class ColorStateList extends ComplexColor implements Parcelable {
|
||||
private static final String TAG = "ColorStateList";
|
||||
|
||||
private static final int DEFAULT_COLOR = Color.RED;
|
||||
private static final int[][] EMPTY = new int[][] {new int[0]};
|
||||
private static final SparseArray<WeakReference<ColorStateList>> sCache =
|
||||
new SparseArray<WeakReference<ColorStateList>>();
|
||||
|
||||
private ColorStateList() {}
|
||||
/**
|
||||
* Thread-safe cache of single-color ColorStateLists.
|
||||
*/
|
||||
private static final SparseArray<WeakReference<ColorStateList>> sCache = new SparseArray<>();
|
||||
|
||||
/**
|
||||
* Lazily-created factory for this color state list.
|
||||
*/
|
||||
private ColorStateListFactory mFactory;
|
||||
|
||||
private int[][] mThemeAttrs;
|
||||
private int mChangingConfigurations;
|
||||
|
||||
private int[][] mStateSpecs;
|
||||
private int[] mColors;
|
||||
private int mDefaultColor;
|
||||
private boolean mIsOpaque;
|
||||
|
||||
private ColorStateList() {
|
||||
// Not publicly instantiable.
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ColorStateList that returns the specified mapping from
|
||||
* states to colors.
|
||||
*/
|
||||
public ColorStateList(int[][] states, int[] colors) {
|
||||
public ColorStateList(int[][] states, /*@ColorInt*/ int[] colors) {
|
||||
mStateSpecs = states;
|
||||
mColors = colors;
|
||||
|
||||
if (states.length > 0) {
|
||||
mDefaultColor = colors[0];
|
||||
|
||||
for (int i = 0; i < states.length; i++) {
|
||||
if (states[i].length == 0) {
|
||||
mDefaultColor = colors[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
onColorsChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or retrieves a ColorStateList that always returns a single color.
|
||||
* @return A ColorStateList containing a single color.
|
||||
*/
|
||||
public static ColorStateList valueOf(int color) {
|
||||
// TODO: should we collect these eventually?
|
||||
@NonNull
|
||||
public static ColorStateList valueOf(/*@ColorInt*/ int color) {
|
||||
synchronized (sCache) {
|
||||
WeakReference<ColorStateList> ref = sCache.get(color);
|
||||
ColorStateList csl = ref != null ? ref.get() : null;
|
||||
final int index = sCache.indexOfKey(color);
|
||||
if (index >= 0) {
|
||||
final ColorStateList cached = sCache.valueAt(index).get();
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
if (csl != null) {
|
||||
return csl;
|
||||
// Prune missing entry.
|
||||
sCache.removeAt(index);
|
||||
}
|
||||
|
||||
csl = new ColorStateList(EMPTY, new int[] {color});
|
||||
sCache.put(color, new WeakReference<ColorStateList>(csl));
|
||||
// Prune the cache before adding new items.
|
||||
final int N = sCache.size();
|
||||
for (int i = N - 1; i >= 0; i--) {
|
||||
if (sCache.valueAt(i).get() == null) {
|
||||
sCache.removeAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
final ColorStateList csl = new ColorStateList(EMPTY, new int[] {color});
|
||||
sCache.put(color, new WeakReference<>(csl));
|
||||
return csl;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ColorStateList from an XML document, given a set of {@link Resources}.
|
||||
* Creates a ColorStateList with the same properties as another
|
||||
* ColorStateList.
|
||||
* <p>
|
||||
* The properties of the new ColorStateList can be modified without
|
||||
* affecting the source ColorStateList.
|
||||
*
|
||||
* @param orig the source color state list
|
||||
*/
|
||||
private ColorStateList(ColorStateList orig) {
|
||||
if (orig != null) {
|
||||
mChangingConfigurations = orig.mChangingConfigurations;
|
||||
mStateSpecs = orig.mStateSpecs;
|
||||
mDefaultColor = orig.mDefaultColor;
|
||||
mIsOpaque = orig.mIsOpaque;
|
||||
|
||||
// Deep copy, these may change due to applyTheme().
|
||||
mThemeAttrs = orig.mThemeAttrs.clone();
|
||||
mColors = orig.mColors.clone();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ColorStateList from an XML document.
|
||||
*
|
||||
* @param r Resources against which the ColorStateList should be inflated.
|
||||
* @param parser Parser for the XML document defining the ColorStateList.
|
||||
* @return A new color state list.
|
||||
*
|
||||
* @deprecated Use #createFromXml(Resources, XmlPullParser parser, Theme)
|
||||
*/
|
||||
@NonNull
|
||||
@Deprecated
|
||||
public static ColorStateList createFromXml(Resources r, XmlPullParser parser)
|
||||
throws XmlPullParserException, IOException {
|
||||
return createFromXml(r, parser, null);
|
||||
}
|
||||
|
||||
AttributeSet attrs = Xml.asAttributeSet(parser);
|
||||
/**
|
||||
* Creates a ColorStateList from an XML document using given a set of
|
||||
* {@link Resources} and a {@link Theme}.
|
||||
*
|
||||
* @param r Resources against which the ColorStateList should be inflated.
|
||||
* @param parser Parser for the XML document defining the ColorStateList.
|
||||
* @param theme Optional theme to apply to the color state list, may be
|
||||
* {@code null}.
|
||||
* @return A new color state list.
|
||||
*/
|
||||
@NonNull
|
||||
public static ColorStateList createFromXml(@NonNull Resources r, @NonNull XmlPullParser parser,
|
||||
@Nullable Theme theme) throws XmlPullParserException, IOException {
|
||||
final AttributeSet attrs = Xml.asAttributeSet(parser);
|
||||
|
||||
int type;
|
||||
while ((type = parser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) {
|
||||
// Seek parser to start tag.
|
||||
}
|
||||
|
||||
if (type != XmlPullParser.START_TAG) {
|
||||
throw new XmlPullParserException("No start tag found");
|
||||
}
|
||||
|
||||
return createFromXmlInner(r, parser, attrs);
|
||||
return createFromXmlInner(r, parser, attrs, theme);
|
||||
}
|
||||
|
||||
/* Create from inside an XML document. Called on a parser positioned at
|
||||
* a tag in an XML document, tries to create a ColorStateList from that tag.
|
||||
* Returns null if the tag is not a valid ColorStateList.
|
||||
/**
|
||||
* Create from inside an XML document. Called on a parser positioned at a
|
||||
* tag in an XML document, tries to create a ColorStateList from that tag.
|
||||
*
|
||||
* @throws XmlPullParserException if the current tag is not <selector>
|
||||
* @return A new color state list for the current tag.
|
||||
*/
|
||||
private static ColorStateList createFromXmlInner(Resources r, XmlPullParser parser,
|
||||
AttributeSet attrs) throws XmlPullParserException, IOException {
|
||||
|
||||
ColorStateList colorStateList;
|
||||
|
||||
@NonNull
|
||||
static ColorStateList createFromXmlInner(@NonNull Resources r,
|
||||
@NonNull XmlPullParser parser, @NonNull AttributeSet attrs, @Nullable Theme theme)
|
||||
throws XmlPullParserException, IOException {
|
||||
final String name = parser.getName();
|
||||
|
||||
if (name.equals("selector")) {
|
||||
colorStateList = new ColorStateList();
|
||||
} else {
|
||||
if (!name.equals("selector")) {
|
||||
throw new XmlPullParserException(
|
||||
parser.getPositionDescription() + ": invalid drawable tag " + name);
|
||||
parser.getPositionDescription() + ": invalid color state list tag " + name);
|
||||
}
|
||||
|
||||
colorStateList.inflate(r, parser, attrs);
|
||||
final ColorStateList colorStateList = new ColorStateList();
|
||||
colorStateList.inflate(r, parser, attrs, theme);
|
||||
return colorStateList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ColorStateList that has the same states and
|
||||
* colors as this one but where each color has the specified alpha value
|
||||
* (0-255).
|
||||
* Creates a new ColorStateList that has the same states and colors as this
|
||||
* one but where each color has the specified alpha value (0-255).
|
||||
*
|
||||
* @param alpha The new alpha channel value (0-255).
|
||||
* @return A new color state list.
|
||||
*/
|
||||
@NonNull
|
||||
public ColorStateList withAlpha(int alpha) {
|
||||
int[] colors = new int[mColors.length];
|
||||
|
||||
int len = colors.length;
|
||||
final int[] colors = new int[mColors.length];
|
||||
final int len = colors.length;
|
||||
for (int i = 0; i < len; i++) {
|
||||
colors[i] = (mColors[i] & 0xFFFFFF) | (alpha << 24);
|
||||
}
|
||||
|
@ -165,95 +243,254 @@ public class ColorStateList {
|
|||
/**
|
||||
* Fill in this object based on the contents of an XML "selector" element.
|
||||
*/
|
||||
private void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
|
||||
private void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
|
||||
@NonNull AttributeSet attrs, @Nullable Theme theme)
|
||||
throws XmlPullParserException, IOException {
|
||||
|
||||
int type;
|
||||
|
||||
final int innerDepth = parser.getDepth() + 1;
|
||||
int depth;
|
||||
int type;
|
||||
|
||||
int listAllocated = 20;
|
||||
int changingConfigurations = 0;
|
||||
int defaultColor = DEFAULT_COLOR;
|
||||
|
||||
boolean hasUnresolvedAttrs = false;
|
||||
|
||||
int[][] stateSpecList = ArrayUtils.newUnpaddedArray(int[].class, 20);
|
||||
int[][] themeAttrsList = new int[stateSpecList.length][];
|
||||
int[] colorList = new int[stateSpecList.length];
|
||||
int listSize = 0;
|
||||
int[] colorList = new int[listAllocated];
|
||||
int[][] stateSpecList = new int[listAllocated][];
|
||||
|
||||
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
|
||||
if (type != XmlPullParser.START_TAG) {
|
||||
if (type != XmlPullParser.START_TAG || depth > innerDepth || !parser.getName().equals("item")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (depth > innerDepth || !parser.getName().equals("item")) {
|
||||
continue;
|
||||
}
|
||||
final TypedArray a = Resources.obtainAttributes(r, theme, attrs,
|
||||
R.styleable.ColorStateListItem);
|
||||
final int[] themeAttrs = a.extractThemeAttrs();
|
||||
final int baseColor = a.getColor(R.styleable.ColorStateListItem_color, Color.MAGENTA);
|
||||
final float alphaMod = a.getFloat(R.styleable.ColorStateListItem_alpha, 1.0f);
|
||||
|
||||
int color = 0xffff0000;
|
||||
changingConfigurations |= a.getChangingConfigurations();
|
||||
|
||||
int i;
|
||||
a.recycle();
|
||||
|
||||
// Parse all unrecognized attributes as state specifiers.
|
||||
int j = 0;
|
||||
final int numAttrs = attrs.getAttributeCount();
|
||||
int[] stateSpec = new int[numAttrs];
|
||||
TypedArray a = Context.this_application.obtainStyledAttributes(attrs, com.android.internal.R.styleable.ColorStateListItem);
|
||||
color = a.getColor(com.android.internal.R.styleable.ColorStateListItem_color, color);
|
||||
a.recycle();
|
||||
for (i = 0; i < numAttrs; i++) {
|
||||
for (int i = 0; i < numAttrs; i++) {
|
||||
final int stateResId = attrs.getAttributeNameResource(i);
|
||||
if (stateResId == 0)
|
||||
break;
|
||||
if (stateResId == com.android.internal.R.attr.color) {
|
||||
} else {
|
||||
stateSpec[j++] = attrs.getAttributeBooleanValue(i, false)
|
||||
? stateResId
|
||||
: -stateResId;
|
||||
switch (stateResId) {
|
||||
case R.attr.color:
|
||||
case R.attr.alpha:
|
||||
// Recognized attribute, ignore.
|
||||
break;
|
||||
default:
|
||||
stateSpec[j++] = attrs.getAttributeBooleanValue(i, false)
|
||||
? stateResId
|
||||
: -stateResId;
|
||||
}
|
||||
}
|
||||
stateSpec = StateSet.trimStateSet(stateSpec, j);
|
||||
|
||||
// Apply alpha modulation. If we couldn't resolve the color or
|
||||
// alpha yet, the default values leave us enough information to
|
||||
// modulate again during applyTheme().
|
||||
final int color = modulateColorAlpha(baseColor, alphaMod);
|
||||
if (listSize == 0 || stateSpec.length == 0) {
|
||||
mDefaultColor = color;
|
||||
defaultColor = color;
|
||||
}
|
||||
|
||||
if (listSize + 1 >= listAllocated) {
|
||||
listAllocated = ArrayUtils.idealIntArraySize(listSize + 1);
|
||||
|
||||
int[] ncolor = new int[listAllocated];
|
||||
System.arraycopy(colorList, 0, ncolor, 0, listSize);
|
||||
|
||||
int[][] nstate = new int[listAllocated][];
|
||||
System.arraycopy(stateSpecList, 0, nstate, 0, listSize);
|
||||
|
||||
colorList = ncolor;
|
||||
stateSpecList = nstate;
|
||||
if (themeAttrs != null) {
|
||||
hasUnresolvedAttrs = true;
|
||||
}
|
||||
|
||||
colorList[listSize] = color;
|
||||
stateSpecList[listSize] = stateSpec;
|
||||
colorList = GrowingArrayUtils.append(colorList, listSize, color);
|
||||
themeAttrsList = GrowingArrayUtils.append(themeAttrsList, listSize, themeAttrs);
|
||||
stateSpecList = GrowingArrayUtils.append(stateSpecList, listSize, stateSpec);
|
||||
listSize++;
|
||||
}
|
||||
|
||||
mChangingConfigurations = changingConfigurations;
|
||||
mDefaultColor = defaultColor;
|
||||
|
||||
if (hasUnresolvedAttrs) {
|
||||
mThemeAttrs = new int[listSize][];
|
||||
System.arraycopy(themeAttrsList, 0, mThemeAttrs, 0, listSize);
|
||||
} else {
|
||||
mThemeAttrs = null;
|
||||
}
|
||||
|
||||
mColors = new int[listSize];
|
||||
mStateSpecs = new int[listSize][];
|
||||
System.arraycopy(colorList, 0, mColors, 0, listSize);
|
||||
System.arraycopy(stateSpecList, 0, mStateSpecs, 0, listSize);
|
||||
|
||||
onColorsChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a theme can be applied to this color state list, which
|
||||
* usually indicates that the color state list has unresolved theme
|
||||
* attributes.
|
||||
*
|
||||
* @return whether a theme can be applied to this color state list
|
||||
* @hide only for resource preloading
|
||||
*/
|
||||
@Override
|
||||
public boolean canApplyTheme() {
|
||||
return mThemeAttrs != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a theme to this color state list.
|
||||
* <p>
|
||||
* <strong>Note:</strong> Applying a theme may affect the changing
|
||||
* configuration parameters of this color state list. After calling this
|
||||
* method, any dependent configurations must be updated by obtaining the
|
||||
* new configuration mask from {@link #getChangingConfigurations()}.
|
||||
*
|
||||
* @param t the theme to apply
|
||||
*/
|
||||
private void applyTheme(Theme t) {
|
||||
if (mThemeAttrs == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean hasUnresolvedAttrs = false;
|
||||
|
||||
final int[][] themeAttrsList = mThemeAttrs;
|
||||
final int N = themeAttrsList.length;
|
||||
for (int i = 0; i < N; i++) {
|
||||
if (themeAttrsList[i] != null) {
|
||||
final TypedArray a = t.resolveAttributes(themeAttrsList[i],
|
||||
R.styleable.ColorStateListItem);
|
||||
|
||||
final float defaultAlphaMod;
|
||||
if (themeAttrsList[i][R.styleable.ColorStateListItem_color] != 0) {
|
||||
// If the base color hasn't been resolved yet, the current
|
||||
// color's alpha channel is either full-opacity (if we
|
||||
// haven't resolved the alpha modulation yet) or
|
||||
// pre-modulated. Either is okay as a default value.
|
||||
defaultAlphaMod = Color.alpha(mColors[i]) / 255.0f;
|
||||
} else {
|
||||
// Otherwise, the only correct default value is 1. Even if
|
||||
// nothing is resolved during this call, we can apply this
|
||||
// multiple times without losing of information.
|
||||
defaultAlphaMod = 1.0f;
|
||||
}
|
||||
|
||||
// Extract the theme attributes, if any, before attempting to
|
||||
// read from the typed array. This prevents a crash if we have
|
||||
// unresolved attrs.
|
||||
themeAttrsList[i] = a.extractThemeAttrs(themeAttrsList[i]);
|
||||
if (themeAttrsList[i] != null) {
|
||||
hasUnresolvedAttrs = true;
|
||||
}
|
||||
|
||||
final int baseColor = a.getColor(
|
||||
R.styleable.ColorStateListItem_color, mColors[i]);
|
||||
final float alphaMod = a.getFloat(
|
||||
R.styleable.ColorStateListItem_alpha, defaultAlphaMod);
|
||||
mColors[i] = modulateColorAlpha(baseColor, alphaMod);
|
||||
|
||||
// Account for any configuration changes.
|
||||
mChangingConfigurations |= a.getChangingConfigurations();
|
||||
|
||||
a.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasUnresolvedAttrs) {
|
||||
mThemeAttrs = null;
|
||||
}
|
||||
|
||||
onColorsChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an appropriately themed color state list.
|
||||
*
|
||||
* @param t the theme to apply
|
||||
* @return a copy of the color state list with the theme applied, or the
|
||||
* color state list itself if there were no unresolved theme
|
||||
* attributes
|
||||
* @hide only for resource preloading
|
||||
*/
|
||||
@Override
|
||||
public ColorStateList obtainForTheme(Theme t) {
|
||||
if (t == null || !canApplyTheme()) {
|
||||
return this;
|
||||
}
|
||||
|
||||
final ColorStateList clone = new ColorStateList(this);
|
||||
clone.applyTheme(t);
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a mask of the configuration parameters for which this color
|
||||
* state list may change, requiring that it be re-created.
|
||||
*
|
||||
* @return a mask of the changing configuration parameters, as defined by
|
||||
* {@link android.content.pm.ActivityInfo}
|
||||
*
|
||||
* @see android.content.pm.ActivityInfo
|
||||
*/
|
||||
public int getChangingConfigurations() {
|
||||
return mChangingConfigurations;
|
||||
}
|
||||
|
||||
private int modulateColorAlpha(int baseColor, float alphaMod) {
|
||||
if (alphaMod == 1.0f) {
|
||||
return baseColor;
|
||||
}
|
||||
|
||||
final int baseAlpha = Color.alpha(baseColor);
|
||||
final int alpha = MathUtils.constrain((int)(baseAlpha * alphaMod + 0.5f), 0, 255);
|
||||
return (baseColor & 0xFFFFFF) | (alpha << 24);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this color state list contains more than one state spec
|
||||
* and will change color based on state.
|
||||
*
|
||||
* @return True if this color state list changes color based on state, false
|
||||
* otherwise.
|
||||
* @see #getColorForState(int[], int)
|
||||
*/
|
||||
@Override
|
||||
public boolean isStateful() {
|
||||
return mStateSpecs.length > 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the color associated with the given set of {@link android.view.View} states.
|
||||
* Indicates whether this color state list is opaque, which means that every
|
||||
* color returned from {@link #getColorForState(int[], int)} has an alpha
|
||||
* value of 255.
|
||||
*
|
||||
* @return True if this color state list is opaque.
|
||||
*/
|
||||
public boolean isOpaque() {
|
||||
return mIsOpaque;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the color associated with the given set of
|
||||
* {@link android.view.View} states.
|
||||
*
|
||||
* @param stateSet an array of {@link android.view.View} states
|
||||
* @param defaultColor the color to return if there's not state spec in this
|
||||
* {@link ColorStateList} that matches the stateSet.
|
||||
* @param defaultColor the color to return if there's no matching state
|
||||
* spec in this {@link ColorStateList} that matches the
|
||||
* stateSet.
|
||||
*
|
||||
* @return the color associated with that set of states in this {@link ColorStateList}.
|
||||
*/
|
||||
public int getColorForState(int[] stateSet, int defaultColor) {
|
||||
public int getColorForState(@Nullable int[] stateSet, int defaultColor) {
|
||||
final int setLength = mStateSpecs.length;
|
||||
for (int i = 0; i < setLength; i++) {
|
||||
int[] stateSpec = mStateSpecs[i];
|
||||
final int[] stateSpec = mStateSpecs[i];
|
||||
if (StateSet.stateSetMatches(stateSpec, stateSet)) {
|
||||
return mColors[i];
|
||||
}
|
||||
|
@ -266,19 +503,170 @@ public class ColorStateList {
|
|||
*
|
||||
* @return the default color in this {@link ColorStateList}.
|
||||
*/
|
||||
//@ColorInt
|
||||
public int getDefaultColor() {
|
||||
return mDefaultColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the states in this {@link ColorStateList}. The returned array
|
||||
* should not be modified.
|
||||
*
|
||||
* @return the states in this {@link ColorStateList}
|
||||
* @hide
|
||||
*/
|
||||
public int[][] getStates() {
|
||||
return mStateSpecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the colors in this {@link ColorStateList}. The returned array
|
||||
* should not be modified.
|
||||
*
|
||||
* @return the colors in this {@link ColorStateList}
|
||||
* @hide
|
||||
*/
|
||||
public int[] getColors() {
|
||||
return mColors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the specified state is referenced in any of the state
|
||||
* specs contained within this ColorStateList.
|
||||
* <p>
|
||||
* Any reference, either positive or negative {ex. ~R.attr.state_enabled},
|
||||
* will cause this method to return {@code true}. Wildcards are not counted
|
||||
* as references.
|
||||
*
|
||||
* @param state the state to search for
|
||||
* @return {@code true} if the state if referenced, {@code false} otherwise
|
||||
* @hide Use only as directed. For internal use only.
|
||||
*/
|
||||
public boolean hasState(int state) {
|
||||
final int[][] stateSpecs = mStateSpecs;
|
||||
final int specCount = stateSpecs.length;
|
||||
for (int specIndex = 0; specIndex < specCount; specIndex++) {
|
||||
final int[] states = stateSpecs[specIndex];
|
||||
final int stateCount = states.length;
|
||||
for (int stateIndex = 0; stateIndex < stateCount; stateIndex++) {
|
||||
if (states[stateIndex] == state || states[stateIndex] == ~state) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ColorStateList{"
|
||||
+
|
||||
"mThemeAttrs=" + Arrays.deepToString(mThemeAttrs) +
|
||||
"mChangingConfigurations=" + mChangingConfigurations +
|
||||
"mStateSpecs=" + Arrays.deepToString(mStateSpecs) +
|
||||
"mColors=" + Arrays.toString(mColors) +
|
||||
"mDefaultColor=" + mDefaultColor + '}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the default color and opacity.
|
||||
*/
|
||||
private void onColorsChanged() {
|
||||
int defaultColor = DEFAULT_COLOR;
|
||||
boolean isOpaque = true;
|
||||
|
||||
final int[][] states = mStateSpecs;
|
||||
final int[] colors = mColors;
|
||||
final int N = states.length;
|
||||
if (N > 0) {
|
||||
defaultColor = colors[0];
|
||||
|
||||
for (int i = N - 1; i > 0; i--) {
|
||||
if (states[i].length == 0) {
|
||||
defaultColor = colors[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
if (Color.alpha(colors[i]) != 0xFF) {
|
||||
isOpaque = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mDefaultColor = defaultColor;
|
||||
mIsOpaque = isOpaque;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a factory that can create new instances of this ColorStateList
|
||||
* @hide only for resource preloading
|
||||
*/
|
||||
public ConstantState<ComplexColor> getConstantState() {
|
||||
if (mFactory == null) {
|
||||
mFactory = new ColorStateListFactory(this);
|
||||
}
|
||||
return mFactory;
|
||||
}
|
||||
|
||||
private static class ColorStateListFactory extends ConstantState<ComplexColor> {
|
||||
private final ColorStateList mSrc;
|
||||
|
||||
public ColorStateListFactory(ColorStateList src) {
|
||||
mSrc = src;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChangingConfigurations() {
|
||||
return mSrc.mChangingConfigurations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorStateList newInstance() {
|
||||
return mSrc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorStateList newInstance(Resources res, Theme theme) {
|
||||
return (ColorStateList)mSrc.obtainForTheme(theme);
|
||||
}
|
||||
}
|
||||
|
||||
/*@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
if (canApplyTheme()) {
|
||||
Log.w(TAG, "Wrote partially-resolved ColorStateList to parcel!");
|
||||
}
|
||||
final int N = mStateSpecs.length;
|
||||
dest.writeInt(N);
|
||||
for (int i = 0; i < N; i++) {
|
||||
dest.writeIntArray(mStateSpecs[i]);
|
||||
}
|
||||
dest.writeIntArray(mColors);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<ColorStateList> CREATOR = new Parcelable.Creator<ColorStateList>() {
|
||||
@Override
|
||||
public ColorStateList[] newArray(int size) {
|
||||
return new ColorStateList[size];
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorStateList createFromParcel(Parcel source) {
|
||||
final int N = source.readInt();
|
||||
final int[][] stateSpecs = new int[N][];
|
||||
for (int i = 0; i < N; i++) {
|
||||
stateSpecs[i] = source.createIntArray();
|
||||
}
|
||||
final int[] colors = source.createIntArray();
|
||||
return new ColorStateList(stateSpecs, colors);
|
||||
}
|
||||
};*/
|
||||
}
|
||||
|
|
55
src/api-impl/android/content/res/ComplexColor.java
Normal file
55
src/api-impl/android/content/res/ComplexColor.java
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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 android.content.res;
|
||||
|
||||
//import android.annotation.ColorInt;
|
||||
import android.content.res.Resources.Theme;
|
||||
import android.graphics.Color;
|
||||
|
||||
/**
|
||||
* Defines an abstract class for the complex color information, like
|
||||
* {@link android.content.res.ColorStateList} or {@link android.content.res.GradientColor}
|
||||
*/
|
||||
public abstract class ComplexColor {
|
||||
/**
|
||||
* @return {@code true} if this ComplexColor changes color based on state, {@code false}
|
||||
* otherwise.
|
||||
*/
|
||||
public boolean isStateful() { return false; }
|
||||
|
||||
/**
|
||||
* @return the default color.
|
||||
*/
|
||||
//@ColorInt
|
||||
public abstract int getDefaultColor();
|
||||
|
||||
/**
|
||||
* @hide only for resource preloading
|
||||
*
|
||||
*/
|
||||
public abstract ConstantState<ComplexColor> getConstantState();
|
||||
|
||||
/**
|
||||
* @hide only for resource preloading
|
||||
*/
|
||||
public abstract boolean canApplyTheme();
|
||||
|
||||
/**
|
||||
* @hide only for resource preloading
|
||||
*/
|
||||
public abstract ComplexColor obtainForTheme(Theme t);
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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 android.content.res;
|
||||
|
||||
/**
|
||||
* A Cache class which can be used to cache resource objects that are easy to clone but more
|
||||
* expensive to inflate.
|
||||
*
|
||||
* @hide For internal use only.
|
||||
*/
|
||||
public class ConfigurationBoundResourceCache<T> extends ThemedResourceCache<ConstantState<T>> {
|
||||
private final Resources mResources;
|
||||
|
||||
/**
|
||||
* Creates a cache for the given Resources instance.
|
||||
*
|
||||
* @param resources the resources to use when creating new instances
|
||||
*/
|
||||
public ConfigurationBoundResourceCache(Resources resources) {
|
||||
mResources = resources;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the resource is cached, creates and returns a new instance of it.
|
||||
*
|
||||
* @param key a key that uniquely identifies the drawable resource
|
||||
* @param theme the theme where the resource will be used
|
||||
* @return a new instance of the resource, or {@code null} if not in
|
||||
* the cache
|
||||
*/
|
||||
public T getInstance(long key, Resources.Theme theme) {
|
||||
final ConstantState<T> entry = get(key, theme);
|
||||
if (entry != null) {
|
||||
return entry.newInstance(mResources, theme);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldInvalidateEntry(ConstantState<T> entry, int configChanges) {
|
||||
return Configuration.needNewResources(configChanges, entry.getChangingConfigurations());
|
||||
}
|
||||
}
|
61
src/api-impl/android/content/res/ConstantState.java
Normal file
61
src/api-impl/android/content/res/ConstantState.java
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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 android.content.res;
|
||||
|
||||
/**
|
||||
* A cache class that can provide new instances of a particular resource which may change
|
||||
* depending on the current {@link Resources.Theme} or {@link Configuration}.
|
||||
* <p>
|
||||
* A constant state should be able to return a bitmask of changing configurations, which
|
||||
* identifies the type of configuration changes that may invalidate this resource. These
|
||||
* configuration changes can be obtained from {@link android.util.TypedValue}. Entities such as
|
||||
* {@link android.animation.Animator} also provide a changing configuration method to include
|
||||
* their dependencies (e.g. An AnimatorSet's changing configuration is the union of the
|
||||
* changing configurations of each Animator in the set)
|
||||
* @hide
|
||||
*/
|
||||
abstract public class ConstantState<T> {
|
||||
|
||||
/**
|
||||
* Return a bit mask of configuration changes that will impact
|
||||
* this resource (and thus require completely reloading it).
|
||||
*/
|
||||
abstract public int getChangingConfigurations();
|
||||
|
||||
/**
|
||||
* Create a new instance without supplying resources the caller
|
||||
* is running in.
|
||||
*/
|
||||
public abstract T newInstance();
|
||||
|
||||
/**
|
||||
* Create a new instance from its constant state. This
|
||||
* must be implemented for resources that change based on the target
|
||||
* density of their caller (that is depending on whether it is
|
||||
* in compatibility mode).
|
||||
*/
|
||||
public T newInstance(Resources res) {
|
||||
return newInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance from its constant state. This must be
|
||||
* implemented for resources that can have a theme applied.
|
||||
*/
|
||||
public T newInstance(Resources res, Resources.Theme theme) {
|
||||
return newInstance(res);
|
||||
}
|
||||
}
|
512
src/api-impl/android/content/res/GradientColor.java
Normal file
512
src/api-impl/android/content/res/GradientColor.java
Normal file
|
@ -0,0 +1,512 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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 android.content.res;
|
||||
|
||||
//import android.annotation.ColorInt;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.res.Resources.Theme;
|
||||
import android.graphics.LinearGradient;
|
||||
import android.graphics.RadialGradient;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.SweepGradient;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.Xml;
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.util.GrowingArrayUtils;
|
||||
import java.io.IOException;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
public class GradientColor extends ComplexColor {
|
||||
private static final String TAG = "GradientColor";
|
||||
|
||||
private static final boolean DBG_GRADIENT = false;
|
||||
|
||||
/**
|
||||
* Lazily-created factory for this GradientColor.
|
||||
*/
|
||||
private GradientColorFactory mFactory;
|
||||
|
||||
private int mChangingConfigurations;
|
||||
private int mDefaultColor;
|
||||
|
||||
// After parsing all the attributes from XML, this shader is the ultimate result containing
|
||||
// all the XML information.
|
||||
private Shader mShader = null;
|
||||
|
||||
// Below are the attributes at the root element <gradient>
|
||||
private int mGradientType = GradientDrawable.LINEAR_GRADIENT;
|
||||
|
||||
private float mCenterX = 0f;
|
||||
private float mCenterY = 0f;
|
||||
|
||||
private float mStartX = 0f;
|
||||
private float mStartY = 0f;
|
||||
private float mEndX = 0f;
|
||||
private float mEndY = 0f;
|
||||
|
||||
private int mStartColor = 0;
|
||||
private int mCenterColor = 0;
|
||||
private int mEndColor = 0;
|
||||
private boolean mHasCenterColor = false;
|
||||
|
||||
private float mGradientRadius = 0f;
|
||||
|
||||
// Below are the attributes for the <item> element.
|
||||
private int[] mItemColors;
|
||||
private float[] mItemOffsets;
|
||||
|
||||
// Theme attributes for the root and item elements.
|
||||
private int[] mThemeAttrs;
|
||||
private int[][] mItemsThemeAttrs;
|
||||
|
||||
private GradientColor() {
|
||||
}
|
||||
|
||||
private GradientColor(GradientColor copy) {
|
||||
if (copy != null) {
|
||||
mChangingConfigurations = copy.mChangingConfigurations;
|
||||
mDefaultColor = copy.mDefaultColor;
|
||||
mShader = copy.mShader;
|
||||
mGradientType = copy.mGradientType;
|
||||
mCenterX = copy.mCenterX;
|
||||
mCenterY = copy.mCenterY;
|
||||
mStartX = copy.mStartX;
|
||||
mStartY = copy.mStartY;
|
||||
mEndX = copy.mEndX;
|
||||
mEndY = copy.mEndY;
|
||||
mStartColor = copy.mStartColor;
|
||||
mCenterColor = copy.mCenterColor;
|
||||
mEndColor = copy.mEndColor;
|
||||
mHasCenterColor = copy.mHasCenterColor;
|
||||
mGradientRadius = copy.mGradientRadius;
|
||||
|
||||
if (copy.mItemColors != null) {
|
||||
mItemColors = copy.mItemColors.clone();
|
||||
}
|
||||
if (copy.mItemOffsets != null) {
|
||||
mItemOffsets = copy.mItemOffsets.clone();
|
||||
}
|
||||
|
||||
if (copy.mThemeAttrs != null) {
|
||||
mThemeAttrs = copy.mThemeAttrs.clone();
|
||||
}
|
||||
if (copy.mItemsThemeAttrs != null) {
|
||||
mItemsThemeAttrs = copy.mItemsThemeAttrs.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the root level's attributes, either for inflate or applyTheme.
|
||||
*/
|
||||
private void updateRootElementState(TypedArray a) {
|
||||
// Extract the theme attributes, if any.
|
||||
mThemeAttrs = a.extractThemeAttrs();
|
||||
|
||||
mStartX = a.getFloat(
|
||||
R.styleable.GradientColor_startX, mStartX);
|
||||
mStartY = a.getFloat(
|
||||
R.styleable.GradientColor_startY, mStartY);
|
||||
mEndX = a.getFloat(
|
||||
R.styleable.GradientColor_endX, mEndX);
|
||||
mEndY = a.getFloat(
|
||||
R.styleable.GradientColor_endY, mEndY);
|
||||
|
||||
mCenterX = a.getFloat(
|
||||
R.styleable.GradientColor_centerX, mCenterX);
|
||||
mCenterY = a.getFloat(
|
||||
R.styleable.GradientColor_centerY, mCenterY);
|
||||
|
||||
mGradientType = a.getInt(
|
||||
R.styleable.GradientColor_type, mGradientType);
|
||||
|
||||
mStartColor = a.getColor(
|
||||
R.styleable.GradientColor_startColor, mStartColor);
|
||||
mHasCenterColor |= a.hasValue(
|
||||
R.styleable.GradientColor_centerColor);
|
||||
mCenterColor = a.getColor(
|
||||
R.styleable.GradientColor_centerColor, mCenterColor);
|
||||
mEndColor = a.getColor(
|
||||
R.styleable.GradientColor_endColor, mEndColor);
|
||||
|
||||
if (DBG_GRADIENT) {
|
||||
Log.v(TAG, "hasCenterColor is " + mHasCenterColor);
|
||||
if (mHasCenterColor) {
|
||||
Log.v(TAG, "centerColor:" + mCenterColor);
|
||||
}
|
||||
Log.v(TAG, "startColor: " + mStartColor);
|
||||
Log.v(TAG, "endColor: " + mEndColor);
|
||||
}
|
||||
|
||||
mGradientRadius = a.getFloat(R.styleable.GradientColor_gradientRadius,
|
||||
mGradientRadius);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the XML content is valid.
|
||||
*
|
||||
* @throws XmlPullParserException if errors were found.
|
||||
*/
|
||||
private void validateXmlContent() throws XmlPullParserException {
|
||||
if (mGradientRadius <= 0 && mGradientType == GradientDrawable.RADIAL_GRADIENT) {
|
||||
throw new XmlPullParserException(
|
||||
"<gradient> tag requires 'gradientRadius' "
|
||||
+ "attribute with radial type");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The shader information will be applied to the native VectorDrawable's path.
|
||||
* @hide
|
||||
*/
|
||||
public Shader getShader() {
|
||||
return mShader;
|
||||
}
|
||||
|
||||
/**
|
||||
* A public method to create GradientColor from a XML resource.
|
||||
*/
|
||||
public static GradientColor createFromXml(Resources r, XmlResourceParser parser, Theme theme)
|
||||
throws XmlPullParserException, IOException {
|
||||
final AttributeSet attrs = Xml.asAttributeSet(parser);
|
||||
|
||||
int type;
|
||||
while ((type = parser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) {
|
||||
// Seek parser to start tag.
|
||||
}
|
||||
|
||||
if (type != XmlPullParser.START_TAG) {
|
||||
throw new XmlPullParserException("No start tag found");
|
||||
}
|
||||
|
||||
return createFromXmlInner(r, parser, attrs, theme);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create from inside an XML document. Called on a parser positioned at a
|
||||
* tag in an XML document, tries to create a GradientColor from that tag.
|
||||
*
|
||||
* @return A new GradientColor for the current tag.
|
||||
* @throws XmlPullParserException if the current tag is not <gradient>
|
||||
*/
|
||||
@NonNull
|
||||
static GradientColor createFromXmlInner(@NonNull Resources r,
|
||||
@NonNull XmlPullParser parser, @NonNull AttributeSet attrs, @Nullable Theme theme)
|
||||
throws XmlPullParserException, IOException {
|
||||
final String name = parser.getName();
|
||||
if (!name.equals("gradient")) {
|
||||
throw new XmlPullParserException(
|
||||
parser.getPositionDescription() + ": invalid gradient color tag " + name);
|
||||
}
|
||||
|
||||
final GradientColor gradientColor = new GradientColor();
|
||||
gradientColor.inflate(r, parser, attrs, theme);
|
||||
return gradientColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill in this object based on the contents of an XML "gradient" element.
|
||||
*/
|
||||
private void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
|
||||
@NonNull AttributeSet attrs, @Nullable Theme theme)
|
||||
throws XmlPullParserException, IOException {
|
||||
final TypedArray a = Resources.obtainAttributes(r, theme, attrs, R.styleable.GradientColor);
|
||||
updateRootElementState(a);
|
||||
mChangingConfigurations |= a.getChangingConfigurations();
|
||||
a.recycle();
|
||||
|
||||
// Check correctness and throw exception if errors found.
|
||||
validateXmlContent();
|
||||
|
||||
inflateChildElements(r, parser, attrs, theme);
|
||||
|
||||
onColorsChange();
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates child elements "item"s for each color stop.
|
||||
*
|
||||
* Note that at root level, we need to save ThemeAttrs for theme applied later.
|
||||
* Here similarly, at each child item, we need to save the theme's attributes, and apply theme
|
||||
* later as applyItemsAttrsTheme().
|
||||
*/
|
||||
private void inflateChildElements(@NonNull Resources r, @NonNull XmlPullParser parser,
|
||||
@NonNull AttributeSet attrs, @NonNull Theme theme)
|
||||
throws XmlPullParserException, IOException {
|
||||
final int innerDepth = parser.getDepth() + 1;
|
||||
int type;
|
||||
int depth;
|
||||
|
||||
// Pre-allocate the array with some size, for better performance.
|
||||
float[] offsetList = new float[20];
|
||||
int[] colorList = new int[offsetList.length];
|
||||
int[][] themeAttrsList = new int[offsetList.length][];
|
||||
|
||||
int listSize = 0;
|
||||
boolean hasUnresolvedAttrs = false;
|
||||
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
|
||||
if (type != XmlPullParser.START_TAG) {
|
||||
continue;
|
||||
}
|
||||
if (depth > innerDepth || !parser.getName().equals("item")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final TypedArray a = Resources.obtainAttributes(r, theme, attrs,
|
||||
R.styleable.GradientColorItem);
|
||||
boolean hasColor = a.hasValue(R.styleable.GradientColorItem_color);
|
||||
boolean hasOffset = a.hasValue(R.styleable.GradientColorItem_offset);
|
||||
if (!hasColor || !hasOffset) {
|
||||
throw new XmlPullParserException(
|
||||
parser.getPositionDescription() + ": <item> tag requires a 'color' attribute and a 'offset' "
|
||||
+ "attribute!");
|
||||
}
|
||||
|
||||
final int[] themeAttrs = a.extractThemeAttrs();
|
||||
int color = a.getColor(R.styleable.GradientColorItem_color, 0);
|
||||
float offset = a.getFloat(R.styleable.GradientColorItem_offset, 0);
|
||||
|
||||
if (DBG_GRADIENT) {
|
||||
Log.v(TAG, "new item color " + color + " " + Integer.toHexString(color));
|
||||
Log.v(TAG, "offset" + offset);
|
||||
}
|
||||
mChangingConfigurations |= a.getChangingConfigurations();
|
||||
a.recycle();
|
||||
|
||||
if (themeAttrs != null) {
|
||||
hasUnresolvedAttrs = true;
|
||||
}
|
||||
|
||||
colorList = GrowingArrayUtils.append(colorList, listSize, color);
|
||||
offsetList = GrowingArrayUtils.append(offsetList, listSize, offset);
|
||||
themeAttrsList = GrowingArrayUtils.append(themeAttrsList, listSize, themeAttrs);
|
||||
listSize++;
|
||||
}
|
||||
if (listSize > 0) {
|
||||
if (hasUnresolvedAttrs) {
|
||||
mItemsThemeAttrs = new int[listSize][];
|
||||
System.arraycopy(themeAttrsList, 0, mItemsThemeAttrs, 0, listSize);
|
||||
} else {
|
||||
mItemsThemeAttrs = null;
|
||||
}
|
||||
|
||||
mItemColors = new int[listSize];
|
||||
mItemOffsets = new float[listSize];
|
||||
System.arraycopy(colorList, 0, mItemColors, 0, listSize);
|
||||
System.arraycopy(offsetList, 0, mItemOffsets, 0, listSize);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply theme to all the items.
|
||||
*/
|
||||
private void applyItemsAttrsTheme(Theme t) {
|
||||
if (mItemsThemeAttrs == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean hasUnresolvedAttrs = false;
|
||||
|
||||
final int[][] themeAttrsList = mItemsThemeAttrs;
|
||||
final int N = themeAttrsList.length;
|
||||
for (int i = 0; i < N; i++) {
|
||||
if (themeAttrsList[i] != null) {
|
||||
final TypedArray a = t.resolveAttributes(themeAttrsList[i],
|
||||
R.styleable.GradientColorItem);
|
||||
|
||||
// Extract the theme attributes, if any, before attempting to
|
||||
// read from the typed array. This prevents a crash if we have
|
||||
// unresolved attrs.
|
||||
themeAttrsList[i] = a.extractThemeAttrs(themeAttrsList[i]);
|
||||
if (themeAttrsList[i] != null) {
|
||||
hasUnresolvedAttrs = true;
|
||||
}
|
||||
|
||||
mItemColors[i] = a.getColor(R.styleable.GradientColorItem_color, mItemColors[i]);
|
||||
mItemOffsets[i] = a.getFloat(R.styleable.GradientColorItem_offset, mItemOffsets[i]);
|
||||
if (DBG_GRADIENT) {
|
||||
Log.v(TAG, "applyItemsAttrsTheme Colors[i] " + i + " " +
|
||||
Integer.toHexString(mItemColors[i]));
|
||||
Log.v(TAG, "Offsets[i] " + i + " " + mItemOffsets[i]);
|
||||
}
|
||||
|
||||
// Account for any configuration changes.
|
||||
mChangingConfigurations |= a.getChangingConfigurations();
|
||||
|
||||
a.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasUnresolvedAttrs) {
|
||||
mItemsThemeAttrs = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void onColorsChange() {
|
||||
int[] tempColors = null;
|
||||
float[] tempOffsets = null;
|
||||
|
||||
if (mItemColors != null) {
|
||||
int length = mItemColors.length;
|
||||
tempColors = new int[length];
|
||||
tempOffsets = new float[length];
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
tempColors[i] = mItemColors[i];
|
||||
tempOffsets[i] = mItemOffsets[i];
|
||||
}
|
||||
} else {
|
||||
if (mHasCenterColor) {
|
||||
tempColors = new int[3];
|
||||
tempColors[0] = mStartColor;
|
||||
tempColors[1] = mCenterColor;
|
||||
tempColors[2] = mEndColor;
|
||||
|
||||
tempOffsets = new float[3];
|
||||
tempOffsets[0] = 0.0f;
|
||||
// Since 0.5f is default value, try to take the one that isn't 0.5f
|
||||
tempOffsets[1] = 0.5f;
|
||||
tempOffsets[2] = 1f;
|
||||
} else {
|
||||
tempColors = new int[2];
|
||||
tempColors[0] = mStartColor;
|
||||
tempColors[1] = mEndColor;
|
||||
}
|
||||
}
|
||||
if (tempColors.length < 2) {
|
||||
Log.w(TAG, "<gradient> tag requires 2 color values specified!" + tempColors.length + " " + tempColors);
|
||||
}
|
||||
|
||||
if (mGradientType == GradientDrawable.LINEAR_GRADIENT) {
|
||||
mShader = new LinearGradient(mStartX, mStartY, mEndX, mEndY, tempColors, tempOffsets,
|
||||
Shader.TileMode.CLAMP);
|
||||
} else {
|
||||
if (mGradientType == GradientDrawable.RADIAL_GRADIENT) {
|
||||
mShader = new RadialGradient(mCenterX, mCenterY, mGradientRadius, tempColors,
|
||||
tempOffsets, Shader.TileMode.CLAMP);
|
||||
} else {
|
||||
mShader = new SweepGradient(mCenterX, mCenterY, tempColors, tempOffsets);
|
||||
}
|
||||
}
|
||||
mDefaultColor = tempColors[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* For Gradient color, the default color is not very useful, since the gradient will override
|
||||
* the color information anyway.
|
||||
*/
|
||||
@Override
|
||||
//@ColorInt
|
||||
public int getDefaultColor() {
|
||||
return mDefaultColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to ColorStateList, setup constant state and its factory.
|
||||
* @hide only for resource preloading
|
||||
*/
|
||||
@Override
|
||||
public ConstantState<ComplexColor> getConstantState() {
|
||||
if (mFactory == null) {
|
||||
mFactory = new GradientColorFactory(this);
|
||||
}
|
||||
return mFactory;
|
||||
}
|
||||
|
||||
private static class GradientColorFactory extends ConstantState<ComplexColor> {
|
||||
private final GradientColor mSrc;
|
||||
|
||||
public GradientColorFactory(GradientColor src) {
|
||||
mSrc = src;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChangingConfigurations() {
|
||||
return mSrc.mChangingConfigurations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GradientColor newInstance() {
|
||||
return mSrc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GradientColor newInstance(Resources res, Theme theme) {
|
||||
return mSrc.obtainForTheme(theme);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an appropriately themed gradient color.
|
||||
*
|
||||
* @param t the theme to apply
|
||||
* @return a copy of the gradient color the theme applied, or the
|
||||
* gradient itself if there were no unresolved theme
|
||||
* attributes
|
||||
* @hide only for resource preloading
|
||||
*/
|
||||
@Override
|
||||
public GradientColor obtainForTheme(Theme t) {
|
||||
if (t == null || !canApplyTheme()) {
|
||||
return this;
|
||||
}
|
||||
|
||||
final GradientColor clone = new GradientColor(this);
|
||||
clone.applyTheme(t);
|
||||
return clone;
|
||||
}
|
||||
|
||||
private void applyTheme(Theme t) {
|
||||
if (mThemeAttrs != null) {
|
||||
applyRootAttrsTheme(t);
|
||||
}
|
||||
if (mItemsThemeAttrs != null) {
|
||||
applyItemsAttrsTheme(t);
|
||||
}
|
||||
onColorsChange();
|
||||
}
|
||||
|
||||
private void applyRootAttrsTheme(Theme t) {
|
||||
final TypedArray a = t.resolveAttributes(mThemeAttrs, R.styleable.GradientColor);
|
||||
// mThemeAttrs will be set to null if if there are no theme attributes in the
|
||||
// typed array.
|
||||
mThemeAttrs = a.extractThemeAttrs(mThemeAttrs);
|
||||
// merging the attributes update inside the updateRootElementState().
|
||||
updateRootElementState(a);
|
||||
|
||||
// Account for any configuration changes.
|
||||
mChangingConfigurations |= a.getChangingConfigurations();
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a theme can be applied to this gradient color, which
|
||||
* usually indicates that the gradient color has unresolved theme
|
||||
* attributes.
|
||||
*
|
||||
* @return whether a theme can be applied to this gradient color.
|
||||
* @hide only for resource preloading
|
||||
*/
|
||||
@Override
|
||||
public boolean canApplyTheme() {
|
||||
return mThemeAttrs != null || mItemsThemeAttrs != null;
|
||||
}
|
||||
}
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package android.content.res;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.icu.text.PluralRules;
|
||||
|
@ -33,6 +35,9 @@ import android.util.Log;
|
|||
import android.util.LongSparseArray;
|
||||
import android.util.Slog;
|
||||
import android.util.TypedValue;
|
||||
import android.util.Xml;
|
||||
import android.util.Pools.SynchronizedPool;
|
||||
import com.android.internal.util.GrowingArrayUtils;
|
||||
import com.android.internal.util.XmlUtils;
|
||||
// import android.view.DisplayAdjustments;
|
||||
import java.io.IOException;
|
||||
|
@ -91,7 +96,10 @@ public class Resources {
|
|||
// single-threaded, and after that these are immutable.
|
||||
private static final LongSparseArray<Drawable.ConstantState>[] sPreloadedDrawables;
|
||||
private static final LongSparseArray<Drawable.ConstantState> sPreloadedColorDrawables = new LongSparseArray<Drawable.ConstantState>();
|
||||
private static final LongSparseArray<ColorStateList> sPreloadedColorStateLists = new LongSparseArray<ColorStateList>();
|
||||
private static final LongSparseArray<android.content.res.ConstantState<ComplexColor>> sPreloadedComplexColors = new LongSparseArray<>();
|
||||
|
||||
// Pool of TypedArrays targeted to this Resources object.
|
||||
final SynchronizedPool<TypedArray> mTypedArrayPool = new SynchronizedPool<>(5);
|
||||
|
||||
private static boolean sPreloaded;
|
||||
private static int sPreloadedDensity;
|
||||
|
@ -102,7 +110,7 @@ public class Resources {
|
|||
/*package*/ final Configuration mTmpConfig = new Configuration();
|
||||
/*package*/ TypedValue mTmpValue = new TypedValue();
|
||||
/*package*/ final Map<Long,WeakReference<Drawable.ConstantState>> mDrawableCache = new HashMap<Long,WeakReference<Drawable.ConstantState>>(0);
|
||||
/*package*/ final LongSparseArray<WeakReference<ColorStateList>> mColorStateListCache = new LongSparseArray<WeakReference<ColorStateList>>(0);
|
||||
private final ConfigurationBoundResourceCache<ComplexColor> mComplexColorCache = new ConfigurationBoundResourceCache<>(this);
|
||||
/*package*/ final LongSparseArray<WeakReference<Drawable.ConstantState>> mColorDrawableCache = new LongSparseArray<WeakReference<Drawable.ConstantState>>(0);
|
||||
/*package*/ boolean mPreloading;
|
||||
|
||||
|
@ -807,6 +815,10 @@ public class Resources {
|
|||
* @return Returns a single color value in the form 0xAARRGGBB.
|
||||
*/
|
||||
public int getColor(int id) throws NotFoundException {
|
||||
return getColor(id, null);
|
||||
}
|
||||
|
||||
public int getColor(int id, Theme theme) throws NotFoundException {
|
||||
TypedValue value;
|
||||
synchronized (mAccessLock) {
|
||||
value = mTmpValue;
|
||||
|
@ -823,7 +835,7 @@ public class Resources {
|
|||
}
|
||||
mTmpValue = null;
|
||||
}
|
||||
ColorStateList csl = loadColorStateList(value, id);
|
||||
ColorStateList csl = loadColorStateList(value, id, theme);
|
||||
synchronized (mAccessLock) {
|
||||
if (mTmpValue == null) {
|
||||
mTmpValue = value;
|
||||
|
@ -832,11 +844,6 @@ public class Resources {
|
|||
return csl.getDefaultColor();
|
||||
}
|
||||
|
||||
public int getColor(int id, Theme theme) throws NotFoundException {
|
||||
// TODO fix it
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a color state list associated with a particular resource ID. The
|
||||
* resource may contain either a single raw color value, or a complex
|
||||
|
@ -867,7 +874,7 @@ public class Resources {
|
|||
}
|
||||
getValue(id, value, true);
|
||||
}
|
||||
ColorStateList res = loadColorStateList(value, id);
|
||||
ColorStateList res = loadColorStateList(value, id, theme);
|
||||
synchronized (mAccessLock) {
|
||||
if (mTmpValue == null) {
|
||||
mTmpValue = value;
|
||||
|
@ -1418,6 +1425,36 @@ public class Resources {
|
|||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the values for a set of attributes in the Theme. The
|
||||
* contents of the typed array are ultimately filled in by
|
||||
* {@link Resources#getValue}.
|
||||
*
|
||||
* @param values The base set of attribute values, must be equal in
|
||||
* length to {@code attrs}. All values must be of type
|
||||
* {@link TypedValue#TYPE_ATTRIBUTE}.
|
||||
* @param attrs The desired attributes to be retrieved.
|
||||
* @return Returns a TypedArray holding an array of the attribute
|
||||
* values. Be sure to call {@link TypedArray#recycle()}
|
||||
* when done with it.
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public TypedArray resolveAttributes(@NonNull int[] values, @NonNull int[] attrs) {
|
||||
final int len = attrs.length;
|
||||
if (values == null || len != values.length) {
|
||||
throw new IllegalArgumentException(
|
||||
"Base attribute values must the same length as attrs");
|
||||
}
|
||||
|
||||
final TypedArray array = TypedArray.obtain(Resources.this, len);
|
||||
AssetManager.resolveAttrs(theme, 0, 0, values, attrs, array.mData, array.mIndices);
|
||||
array.mTheme = this;
|
||||
array.mXml = null;
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the value of an attribute in the Theme. The contents of
|
||||
* <var>outValue</var> are ultimately filled in by
|
||||
|
@ -1461,6 +1498,8 @@ public class Resources {
|
|||
mAssets.releaseTheme(theme);
|
||||
}
|
||||
|
||||
private final ThemeKey mKey = new ThemeKey();
|
||||
|
||||
/*package*/ Theme() {
|
||||
mAssets = Resources.this.mAssets;
|
||||
theme = mAssets.createTheme();
|
||||
|
@ -1470,11 +1509,93 @@ public class Resources {
|
|||
return Resources.this;
|
||||
}
|
||||
|
||||
/*package*/ ThemeKey getKey() {
|
||||
return mKey;
|
||||
}
|
||||
|
||||
private final AssetManager mAssets;
|
||||
|
||||
public void rebase() {}
|
||||
}
|
||||
|
||||
static class ThemeKey implements Cloneable {
|
||||
int[] mResId;
|
||||
boolean[] mForce;
|
||||
int mCount;
|
||||
|
||||
private int mHashCode = 0;
|
||||
|
||||
public void append(int resId, boolean force) {
|
||||
if (mResId == null) {
|
||||
mResId = new int[4];
|
||||
}
|
||||
|
||||
if (mForce == null) {
|
||||
mForce = new boolean[4];
|
||||
}
|
||||
|
||||
mResId = GrowingArrayUtils.append(mResId, mCount, resId);
|
||||
mForce = GrowingArrayUtils.append(mForce, mCount, force);
|
||||
mCount++;
|
||||
|
||||
mHashCode = 31 * (31 * mHashCode + resId) + (force ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up this key as a deep copy of another key.
|
||||
*
|
||||
* @param other the key to deep copy into this key
|
||||
*/
|
||||
public void setTo(ThemeKey other) {
|
||||
mResId = other.mResId == null ? null : other.mResId.clone();
|
||||
mForce = other.mForce == null ? null : other.mForce.clone();
|
||||
mCount = other.mCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return mHashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (o == null || getClass() != o.getClass() || hashCode() != o.hashCode()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final ThemeKey t = (ThemeKey)o;
|
||||
if (mCount != t.mCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int N = mCount;
|
||||
for (int i = 0; i < N; i++) {
|
||||
if (mResId[i] != t.mResId[i] || mForce[i] != t.mForce[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a shallow copy of this key
|
||||
*/
|
||||
@Override
|
||||
public ThemeKey clone() {
|
||||
final ThemeKey other = new ThemeKey();
|
||||
other.mResId = mResId;
|
||||
other.mForce = mForce;
|
||||
other.mCount = mCount;
|
||||
other.mHashCode = mHashCode;
|
||||
return other;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new Theme object for this set of Resources. It initially
|
||||
* starts out empty.
|
||||
|
@ -1615,7 +1736,7 @@ public class Resources {
|
|||
// clearDrawableCacheLocked(mDrawableCache, configChanges);
|
||||
// clearDrawableCacheLocked(mColorDrawableCache, configChanges);
|
||||
|
||||
mColorStateListCache.clear();
|
||||
mComplexColorCache.onConfigurationChange(configChanges);
|
||||
|
||||
flushLayoutCache();
|
||||
}
|
||||
|
@ -2023,8 +2144,11 @@ public class Resources {
|
|||
static private final int LAYOUT_DIR_CONFIG = ActivityInfo.activityInfoConfigToNative(
|
||||
ActivityInfo.CONFIG_LAYOUT_DIRECTION);
|
||||
|
||||
/*package*/ Drawable loadDrawable(TypedValue value, int id)
|
||||
throws NotFoundException { /*
|
||||
/*package*/ Drawable loadDrawable(TypedValue value, int id) throws NotFoundException {
|
||||
return loadDrawable(value, id, null);
|
||||
}
|
||||
|
||||
/*package*/ Drawable loadDrawable(TypedValue value, int id, Theme theme) throws NotFoundException { /*
|
||||
|
||||
if (TRACE_FOR_PRELOAD) {
|
||||
// Log only framework resources
|
||||
|
@ -2189,7 +2313,80 @@ public class Resources {
|
|||
return null;
|
||||
}
|
||||
|
||||
/*package*/ ColorStateList loadColorStateList(TypedValue value, int id)
|
||||
/**
|
||||
* Given the value and id, we can get the XML filename as in value.data, based on that, we
|
||||
* first try to load CSL from the cache. If not found, try to get from the constant state.
|
||||
* Last, parse the XML and generate the CSL.
|
||||
*/
|
||||
private ComplexColor loadComplexColorFromName(Theme theme, TypedValue value, int id) {
|
||||
final long key = (((long)value.assetCookie) << 32) | value.data;
|
||||
final ConfigurationBoundResourceCache<ComplexColor> cache = mComplexColorCache;
|
||||
ComplexColor complexColor = cache.getInstance(key, theme);
|
||||
if (complexColor != null) {
|
||||
return complexColor;
|
||||
}
|
||||
|
||||
final android.content.res.ConstantState<ComplexColor> factory = sPreloadedComplexColors.get(key);
|
||||
|
||||
if (factory != null) {
|
||||
complexColor = factory.newInstance(this, theme);
|
||||
}
|
||||
if (complexColor == null) {
|
||||
complexColor = loadComplexColorForCookie(value, id, theme);
|
||||
}
|
||||
|
||||
if (complexColor != null) {
|
||||
if (mPreloading) {
|
||||
if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId, "color")) {
|
||||
sPreloadedComplexColors.put(key, complexColor.getConstantState());
|
||||
}
|
||||
} else {
|
||||
cache.put(key, theme, complexColor.getConstantState());
|
||||
}
|
||||
}
|
||||
return complexColor;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ComplexColor loadComplexColor(@NonNull TypedValue value, int id, Theme theme) {
|
||||
if (TRACE_FOR_PRELOAD) {
|
||||
// Log only framework resources
|
||||
if ((id >>> 24) == 0x1) {
|
||||
final String name = getResourceName(id);
|
||||
if (name != null)
|
||||
android.util.Log.d("loadComplexColor", name);
|
||||
}
|
||||
}
|
||||
|
||||
final long key = (((long)value.assetCookie) << 32) | value.data;
|
||||
|
||||
// Handle inline color definitions.
|
||||
if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
|
||||
return getColorStateListFromInt(value, key);
|
||||
}
|
||||
|
||||
final String file = value.string.toString();
|
||||
|
||||
ComplexColor complexColor;
|
||||
if (file.endsWith(".xml")) {
|
||||
try {
|
||||
complexColor = loadComplexColorFromName(theme, value, id);
|
||||
} catch (Exception e) {
|
||||
final NotFoundException rnf = new NotFoundException(
|
||||
"File " + file + " from complex color resource ID #0x" + Integer.toHexString(id));
|
||||
rnf.initCause(e);
|
||||
throw rnf;
|
||||
}
|
||||
} else {
|
||||
throw new NotFoundException(
|
||||
"File " + file + " from drawable resource ID #0x" + Integer.toHexString(id) + ": .xml extension required");
|
||||
}
|
||||
|
||||
return complexColor;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
ColorStateList loadColorStateList(TypedValue value, int id, Theme theme)
|
||||
throws NotFoundException {
|
||||
if (TRACE_FOR_PRELOAD) {
|
||||
// Log only framework resources
|
||||
|
@ -2202,99 +2399,112 @@ public class Resources {
|
|||
|
||||
final long key = (((long)value.assetCookie) << 32) | value.data;
|
||||
|
||||
// Handle inline color definitions.
|
||||
if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
|
||||
return getColorStateListFromInt(value, key);
|
||||
}
|
||||
|
||||
ComplexColor complexColor = loadComplexColorFromName(theme, value, id);
|
||||
if (complexColor != null && complexColor instanceof ColorStateList) {
|
||||
return (ColorStateList)complexColor;
|
||||
}
|
||||
|
||||
throw new NotFoundException(
|
||||
"Can't find ColorStateList from drawable resource ID #0x" + Integer.toHexString(id));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private ColorStateList getColorStateListFromInt(@NonNull TypedValue value, long key) {
|
||||
ColorStateList csl;
|
||||
|
||||
if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
|
||||
value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
|
||||
|
||||
csl = sPreloadedColorStateLists.get(key);
|
||||
if (csl != null) {
|
||||
return csl;
|
||||
}
|
||||
|
||||
csl = ColorStateList.valueOf(value.data);
|
||||
if (mPreloading) {
|
||||
if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
|
||||
"color")) {
|
||||
sPreloadedColorStateLists.put(key, csl);
|
||||
}
|
||||
}
|
||||
|
||||
return csl;
|
||||
final android.content.res.ConstantState<ComplexColor> factory =
|
||||
sPreloadedComplexColors.get(key);
|
||||
if (factory != null) {
|
||||
return (ColorStateList)factory.newInstance();
|
||||
}
|
||||
|
||||
csl = getCachedColorStateList(key);
|
||||
if (csl != null) {
|
||||
return csl;
|
||||
}
|
||||
csl = ColorStateList.valueOf(value.data);
|
||||
|
||||
csl = sPreloadedColorStateLists.get(key);
|
||||
if (csl != null) {
|
||||
return csl;
|
||||
}
|
||||
|
||||
if (value.string == null) {
|
||||
throw new NotFoundException(
|
||||
"Resource is not a ColorStateList (color or path): " + value);
|
||||
}
|
||||
|
||||
String file = value.string.toString();
|
||||
|
||||
if (file.endsWith(".xml")) {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
|
||||
try {
|
||||
XmlResourceParser rp = loadXmlResourceParser(
|
||||
file, id, value.assetCookie, "colorstatelist");
|
||||
csl = ColorStateList.createFromXml(this, rp);
|
||||
rp.close();
|
||||
} catch (Exception e) {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
|
||||
NotFoundException rnf = new NotFoundException(
|
||||
"File " + file + " from color state list resource ID #0x" + Integer.toHexString(id));
|
||||
rnf.initCause(e);
|
||||
throw rnf;
|
||||
}
|
||||
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
|
||||
} else {
|
||||
throw new NotFoundException(
|
||||
"File " + file + " from drawable resource ID #0x" + Integer.toHexString(id) + ": .xml extension required");
|
||||
}
|
||||
|
||||
if (csl != null) {
|
||||
if (mPreloading) {
|
||||
if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
|
||||
"color")) {
|
||||
sPreloadedColorStateLists.put(key, csl);
|
||||
}
|
||||
} else {
|
||||
synchronized (mAccessLock) {
|
||||
// Log.i(TAG, "Saving cached color state list @ #" +
|
||||
// Integer.toHexString(key.intValue())
|
||||
// + " in " + this + ": " + csl);
|
||||
mColorStateListCache.put(key, new WeakReference<ColorStateList>(csl));
|
||||
}
|
||||
if (mPreloading) {
|
||||
if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
|
||||
"color")) {
|
||||
sPreloadedComplexColors.put(key, csl.getConstantState());
|
||||
}
|
||||
}
|
||||
|
||||
return csl;
|
||||
}
|
||||
|
||||
private ColorStateList getCachedColorStateList(long key) {
|
||||
synchronized (mAccessLock) {
|
||||
WeakReference<ColorStateList> wr = mColorStateListCache.get(key);
|
||||
if (wr != null) { // we have the key
|
||||
ColorStateList entry = wr.get();
|
||||
if (entry != null) {
|
||||
// Log.i(TAG, "Returning cached color state list @ #" +
|
||||
// Integer.toHexString(((Integer)key).intValue())
|
||||
// + " in " + this + ": " + entry);
|
||||
return entry;
|
||||
} else { // our entry has been purged
|
||||
mColorStateListCache.delete(key);
|
||||
/**
|
||||
* Load a ComplexColor based on the XML file content. The result can be a GradientColor or
|
||||
* ColorStateList. Note that pure color will be wrapped into a ColorStateList.
|
||||
*
|
||||
* We deferred the parser creation to this function b/c we need to differentiate b/t gradient
|
||||
* and selector tag.
|
||||
*
|
||||
* @return a ComplexColor (GradientColor or ColorStateList) based on the XML file content.
|
||||
*/
|
||||
@Nullable
|
||||
private ComplexColor loadComplexColorForCookie(TypedValue value, int id, Theme theme) {
|
||||
if (value.string == null) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Can't convert to ComplexColor: type=0x" + value.type);
|
||||
}
|
||||
|
||||
final String file = value.string.toString();
|
||||
|
||||
if (TRACE_FOR_MISS_PRELOAD) {
|
||||
// Log only framework resources
|
||||
if ((id >>> 24) == 0x1) {
|
||||
final String name = getResourceName(id);
|
||||
if (name != null) {
|
||||
Log.d(TAG, "Loading framework ComplexColor #" + Integer.toHexString(id) + ": " + name + " at " + file);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
if (DEBUG_LOAD) {
|
||||
Log.v(TAG, "Loading ComplexColor for cookie " + value.assetCookie + ": " + file);
|
||||
}
|
||||
|
||||
ComplexColor complexColor = null;
|
||||
|
||||
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
|
||||
if (file.endsWith(".xml")) {
|
||||
try {
|
||||
final XmlResourceParser parser = loadXmlResourceParser(
|
||||
file, id, value.assetCookie, "ComplexColor");
|
||||
|
||||
final AttributeSet attrs = Xml.asAttributeSet(parser);
|
||||
int type;
|
||||
while ((type = parser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) {
|
||||
// Seek parser to start tag.
|
||||
}
|
||||
if (type != XmlPullParser.START_TAG) {
|
||||
throw new XmlPullParserException("No start tag found");
|
||||
}
|
||||
|
||||
final String name = parser.getName();
|
||||
if (name.equals("gradient")) {
|
||||
complexColor = GradientColor.createFromXmlInner(this, parser, attrs, theme);
|
||||
} else if (name.equals("selector")) {
|
||||
complexColor = ColorStateList.createFromXmlInner(this, parser, attrs, theme);
|
||||
}
|
||||
parser.close();
|
||||
} catch (Exception e) {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
|
||||
final NotFoundException rnf = new NotFoundException(
|
||||
"File " + file + " from ComplexColor resource ID #0x" + Integer.toHexString(id));
|
||||
rnf.initCause(e);
|
||||
throw rnf;
|
||||
}
|
||||
} else {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
|
||||
throw new NotFoundException(
|
||||
"File " + file + " from drawable resource ID #0x" + Integer.toHexString(id) + ": .xml extension required");
|
||||
}
|
||||
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
|
||||
|
||||
return complexColor;
|
||||
}
|
||||
|
||||
/*package*/ XmlResourceParser loadXmlResourceParser(int id, String type)
|
||||
|
@ -2321,95 +2531,111 @@ public class Resources {
|
|||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
/* if (id != 0) {
|
||||
try {
|
||||
// These may be compiled...
|
||||
synchronized (mCachedXmlBlockIds) {
|
||||
// First see if this block is in our cache.
|
||||
final int num = mCachedXmlBlockIds.length;
|
||||
for (int i=0; i<num; i++) {
|
||||
if (mCachedXmlBlockIds[i] == id) {
|
||||
//System.out.println("**** REUSING XML BLOCK! id="
|
||||
// + id + ", index=" + i);
|
||||
return mCachedXmlBlocks[i].newParser();
|
||||
}
|
||||
}
|
||||
/*if (id != 0) {
|
||||
try {
|
||||
// These may be compiled...
|
||||
synchronized (mCachedXmlBlockIds) {
|
||||
// First see if this block is in our cache.
|
||||
final int num = mCachedXmlBlockIds.length;
|
||||
for (int i=0; i<num; i++) {
|
||||
if (mCachedXmlBlockIds[i] == id) {
|
||||
//System.out.println("**** REUSING XML BLOCK! id="
|
||||
// + id + ", index=" + i);
|
||||
return mCachedXmlBlocks[i].newParser();
|
||||
}
|
||||
}
|
||||
|
||||
// Not in the cache, create a new block and put it at
|
||||
// the next slot in the cache.
|
||||
XmlBlock block = mAssets.openXmlBlockAsset(
|
||||
assetCookie, file);
|
||||
if (block != null) {
|
||||
int pos = mLastCachedXmlBlockIndex+1;
|
||||
if (pos >= num) pos = 0;
|
||||
mLastCachedXmlBlockIndex = pos;
|
||||
XmlBlock oldBlock = mCachedXmlBlocks[pos];
|
||||
if (oldBlock != null) {
|
||||
oldBlock.close();
|
||||
// Not in the cache, create a new block and put it at
|
||||
// the next slot in the cache.
|
||||
XmlBlock block = mAssets.openXmlBlockAsset(
|
||||
assetCookie, file);
|
||||
if (block != null) {
|
||||
int pos = mLastCachedXmlBlockIndex+1;
|
||||
if (pos >= num) pos = 0;
|
||||
mLastCachedXmlBlockIndex = pos;
|
||||
XmlBlock oldBlock = mCachedXmlBlocks[pos];
|
||||
if (oldBlock != null) {
|
||||
oldBlock.close();
|
||||
}
|
||||
mCachedXmlBlockIds[pos] = id;
|
||||
mCachedXmlBlocks[pos] = block;
|
||||
//System.out.println("**** CACHING NEW XML BLOCK! id="
|
||||
// + id + ", index=" + pos);
|
||||
return block.newParser();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
NotFoundException rnf = new NotFoundException("File " + file +
|
||||
" from xml type " + type +
|
||||
" resource ID #0x" + Integer.toHexString(id));
|
||||
rnf.initCause(e);
|
||||
throw rnf;
|
||||
}
|
||||
mCachedXmlBlockIds[pos] = id;
|
||||
mCachedXmlBlocks[pos] = block;
|
||||
//System.out.println("**** CACHING NEW XML BLOCK! id="
|
||||
// + id + ", index=" + pos);
|
||||
return block.newParser();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
NotFoundException rnf = new NotFoundException(
|
||||
|
||||
throw new NotFoundException(
|
||||
"File " + file + " from xml type " + type + " resource ID #0x"
|
||||
+ Integer.toHexString(id));
|
||||
rnf.initCause(e);
|
||||
throw rnf;
|
||||
}
|
||||
+ Integer.toHexString(id));*/
|
||||
}
|
||||
|
||||
throw new NotFoundException(
|
||||
"File " + file + " from xml type " + type + " resource ID #0x"
|
||||
+ Integer.toHexString(id));
|
||||
*/ }
|
||||
private TypedArray getCachedStyledAttributes(int len) {
|
||||
synchronized (mAccessLock) {
|
||||
TypedArray attrs = mCachedStyledAttributes;
|
||||
if (attrs != null) {
|
||||
mCachedStyledAttributes = null;
|
||||
if (DEBUG_ATTRIBUTES_CACHE) {
|
||||
mLastRetrievedAttrs = new RuntimeException("here");
|
||||
mLastRetrievedAttrs.fillInStackTrace();
|
||||
}
|
||||
|
||||
private TypedArray getCachedStyledAttributes(int len) {
|
||||
synchronized (mAccessLock) {
|
||||
TypedArray attrs = mCachedStyledAttributes;
|
||||
if (attrs != null) {
|
||||
mCachedStyledAttributes = null;
|
||||
if (DEBUG_ATTRIBUTES_CACHE) {
|
||||
mLastRetrievedAttrs = new RuntimeException("here");
|
||||
mLastRetrievedAttrs.fillInStackTrace();
|
||||
}
|
||||
|
||||
attrs.mLength = len;
|
||||
int fullLen = len * AssetManager.STYLE_NUM_ENTRIES;
|
||||
if (attrs.mData.length >= fullLen) {
|
||||
attrs.mLength = len;
|
||||
int fullLen = len * AssetManager.STYLE_NUM_ENTRIES;
|
||||
if (attrs.mData.length >= fullLen) {
|
||||
return attrs;
|
||||
}
|
||||
attrs.mData = new int[fullLen];
|
||||
attrs.mIndices = new int[1 + len];
|
||||
return attrs;
|
||||
}
|
||||
attrs.mData = new int[fullLen];
|
||||
attrs.mIndices = new int[1 + len];
|
||||
return attrs;
|
||||
}
|
||||
if (DEBUG_ATTRIBUTES_CACHE) {
|
||||
RuntimeException here = new RuntimeException("here");
|
||||
here.fillInStackTrace();
|
||||
if (mLastRetrievedAttrs != null) {
|
||||
Log.i(TAG, "Allocated new TypedArray of " + len + " in " + this, here);
|
||||
Log.i(TAG, "Last retrieved attributes here", mLastRetrievedAttrs);
|
||||
if (DEBUG_ATTRIBUTES_CACHE) {
|
||||
RuntimeException here = new RuntimeException("here");
|
||||
here.fillInStackTrace();
|
||||
if (mLastRetrievedAttrs != null) {
|
||||
Log.i(TAG, "Allocated new TypedArray of " + len + " in " + this, here);
|
||||
Log.i(TAG, "Last retrieved attributes here", mLastRetrievedAttrs);
|
||||
}
|
||||
mLastRetrievedAttrs = here;
|
||||
}
|
||||
mLastRetrievedAttrs = here;
|
||||
return new TypedArray(this,
|
||||
new int[len * AssetManager.STYLE_NUM_ENTRIES],
|
||||
new int[1 + len], len);
|
||||
}
|
||||
return new TypedArray(this,
|
||||
new int[len * AssetManager.STYLE_NUM_ENTRIES],
|
||||
new int[1 + len], len);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtains styled attributes from the theme, if available, or unstyled
|
||||
* resources if the theme is null.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static TypedArray obtainAttributes(
|
||||
Resources res, Theme theme, AttributeSet set, int[] attrs) {
|
||||
if (theme == null) {
|
||||
return res.obtainAttributes(set, attrs);
|
||||
}
|
||||
|
||||
return theme.obtainStyledAttributes(set, attrs, 0, 0);
|
||||
}
|
||||
|
||||
private Resources() {
|
||||
mAssets = AssetManager.getSystem();
|
||||
// NOTE: Intentionally leaving this uninitialized (all values set
|
||||
// to zero), so that anyone who tries to do something that requires
|
||||
// metrics will get a very wrong value.
|
||||
mConfiguration.setToDefaults();
|
||||
mMetrics.setToDefaults();
|
||||
updateConfiguration(null, null);
|
||||
mAssets.ensureStringBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
private Resources() {
|
||||
mAssets = AssetManager.getSystem();
|
||||
// NOTE: Intentionally leaving this uninitialized (all values set
|
||||
// to zero), so that anyone who tries to do something that requires
|
||||
// metrics will get a very wrong value.
|
||||
mConfiguration.setToDefaults();
|
||||
mMetrics.setToDefaults();
|
||||
updateConfiguration(null, null);
|
||||
mAssets.ensureStringBlocks();
|
||||
}
|
||||
}
|
||||
|
|
230
src/api-impl/android/content/res/ThemedResourceCache.java
Normal file
230
src/api-impl/android/content/res/ThemedResourceCache.java
Normal file
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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 android.content.res;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.res.Resources.Theme;
|
||||
import android.content.res.Resources.ThemeKey;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.LongSparseArray;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* Data structure used for caching data against themes.
|
||||
*
|
||||
* @param <T> type of data to cache
|
||||
*/
|
||||
abstract class ThemedResourceCache<T> {
|
||||
private ArrayMap<ThemeKey, LongSparseArray<WeakReference<T>>> mThemedEntries;
|
||||
private LongSparseArray<WeakReference<T>> mUnthemedEntries;
|
||||
private LongSparseArray<WeakReference<T>> mNullThemedEntries;
|
||||
|
||||
/**
|
||||
* Adds a new theme-dependent entry to the cache.
|
||||
*
|
||||
* @param key a key that uniquely identifies the entry
|
||||
* @param theme the theme against which this entry was inflated, or
|
||||
* {@code null} if the entry has no theme applied
|
||||
* @param entry the entry to cache
|
||||
*/
|
||||
public void put(long key, @Nullable Theme theme, @NonNull T entry) {
|
||||
put(key, theme, entry, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new entry to the cache.
|
||||
*
|
||||
* @param key a key that uniquely identifies the entry
|
||||
* @param theme the theme against which this entry was inflated, or
|
||||
* {@code null} if the entry has no theme applied
|
||||
* @param entry the entry to cache
|
||||
* @param usesTheme {@code true} if the entry is affected theme changes,
|
||||
* {@code false} otherwise
|
||||
*/
|
||||
public void put(long key, @Nullable Theme theme, @NonNull T entry, boolean usesTheme) {
|
||||
if (entry == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
final LongSparseArray<WeakReference<T>> entries;
|
||||
if (!usesTheme) {
|
||||
entries = getUnthemedLocked(true);
|
||||
} else {
|
||||
entries = getThemedLocked(theme, true);
|
||||
}
|
||||
if (entries != null) {
|
||||
entries.put(key, new WeakReference<>(entry));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an entry from the cache.
|
||||
*
|
||||
* @param key a key that uniquely identifies the entry
|
||||
* @param theme the theme where the entry will be used
|
||||
* @return a cached entry, or {@code null} if not in the cache
|
||||
*/
|
||||
@Nullable
|
||||
public T get(long key, @Nullable Theme theme) {
|
||||
// The themed (includes null-themed) and unthemed caches are mutually
|
||||
// exclusive, so we'll give priority to whichever one we think we'll
|
||||
// hit first. Since most of the framework drawables are themed, that's
|
||||
// probably going to be the themed cache.
|
||||
synchronized (this) {
|
||||
final LongSparseArray<WeakReference<T>> themedEntries = getThemedLocked(theme, false);
|
||||
if (themedEntries != null) {
|
||||
final WeakReference<T> themedEntry = themedEntries.get(key);
|
||||
if (themedEntry != null) {
|
||||
return themedEntry.get();
|
||||
}
|
||||
}
|
||||
|
||||
final LongSparseArray<WeakReference<T>> unthemedEntries = getUnthemedLocked(false);
|
||||
if (unthemedEntries != null) {
|
||||
final WeakReference<T> unthemedEntry = unthemedEntries.get(key);
|
||||
if (unthemedEntry != null) {
|
||||
return unthemedEntry.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prunes cache entries that have been invalidated by a configuration
|
||||
* change.
|
||||
*
|
||||
* @param configChanges a bitmask of configuration changes
|
||||
*/
|
||||
public void onConfigurationChange(int configChanges) {
|
||||
prune(configChanges);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a cached entry has been invalidated by a configuration
|
||||
* change.
|
||||
*
|
||||
* @param entry a cached entry
|
||||
* @param configChanges a non-zero bitmask of configuration changes
|
||||
* @return {@code true} if the entry is invalid, {@code false} otherwise
|
||||
*/
|
||||
protected abstract boolean shouldInvalidateEntry(@NonNull T entry, int configChanges);
|
||||
|
||||
/**
|
||||
* Returns the cached data for the specified theme, optionally creating a
|
||||
* new entry if one does not already exist.
|
||||
*
|
||||
* @param t the theme for which to return cached data
|
||||
* @param create {@code true} to create an entry if one does not already
|
||||
* exist, {@code false} otherwise
|
||||
* @return the cached data for the theme, or {@code null} if the cache is
|
||||
* empty and {@code create} was {@code false}
|
||||
*/
|
||||
@Nullable
|
||||
private LongSparseArray<WeakReference<T>> getThemedLocked(@Nullable Theme t, boolean create) {
|
||||
if (t == null) {
|
||||
if (mNullThemedEntries == null && create) {
|
||||
mNullThemedEntries = new LongSparseArray<>(1);
|
||||
}
|
||||
return mNullThemedEntries;
|
||||
}
|
||||
|
||||
if (mThemedEntries == null) {
|
||||
if (create) {
|
||||
mThemedEntries = new ArrayMap<>(1);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
final ThemeKey key = t.getKey();
|
||||
LongSparseArray<WeakReference<T>> cache = mThemedEntries.get(key);
|
||||
if (cache == null && create) {
|
||||
cache = new LongSparseArray<>(1);
|
||||
|
||||
final ThemeKey keyClone = key.clone();
|
||||
mThemedEntries.put(keyClone, cache);
|
||||
}
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the theme-agnostic cached data.
|
||||
*
|
||||
* @param create {@code true} to create an entry if one does not already
|
||||
* exist, {@code false} otherwise
|
||||
* @return the theme-agnostic cached data, or {@code null} if the cache is
|
||||
* empty and {@code create} was {@code false}
|
||||
*/
|
||||
@Nullable
|
||||
private LongSparseArray<WeakReference<T>> getUnthemedLocked(boolean create) {
|
||||
if (mUnthemedEntries == null && create) {
|
||||
mUnthemedEntries = new LongSparseArray<>(1);
|
||||
}
|
||||
return mUnthemedEntries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prunes cache entries affected by configuration changes or where weak
|
||||
* references have expired.
|
||||
*
|
||||
* @param configChanges a bitmask of configuration changes, or {@code 0} to
|
||||
* simply prune missing weak references
|
||||
* @return {@code true} if the cache is completely empty after pruning
|
||||
*/
|
||||
private boolean prune(int configChanges) {
|
||||
synchronized (this) {
|
||||
if (mThemedEntries != null) {
|
||||
for (int i = mThemedEntries.size() - 1; i >= 0; i--) {
|
||||
if (pruneEntriesLocked(mThemedEntries.valueAt(i), configChanges)) {
|
||||
mThemedEntries.removeAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pruneEntriesLocked(mNullThemedEntries, configChanges);
|
||||
pruneEntriesLocked(mUnthemedEntries, configChanges);
|
||||
|
||||
return mThemedEntries == null && mNullThemedEntries == null && mUnthemedEntries == null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean pruneEntriesLocked(@Nullable LongSparseArray<WeakReference<T>> entries,
|
||||
int configChanges) {
|
||||
if (entries == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = entries.size() - 1; i >= 0; i--) {
|
||||
final WeakReference<T> ref = entries.valueAt(i);
|
||||
if (ref == null || pruneEntryLocked(ref.get(), configChanges)) {
|
||||
entries.removeAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
return entries.size() == 0;
|
||||
}
|
||||
|
||||
private boolean pruneEntryLocked(@Nullable T entry, int configChanges) {
|
||||
return entry == null || (configChanges != 0 && shouldInvalidateEntry(entry, configChanges));
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -8,5 +8,11 @@ public class Shader {
|
|||
REPEAT
|
||||
}
|
||||
|
||||
protected void init(long ni) {
|
||||
}
|
||||
|
||||
public void setLocalMatrix(Matrix matrix) {}
|
||||
|
||||
|
||||
protected void copyLocalMatrix(Shader dest) {}
|
||||
}
|
||||
|
|
110
src/api-impl/android/graphics/SweepGradient.java
Normal file
110
src/api-impl/android/graphics/SweepGradient.java
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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 android.graphics;
|
||||
|
||||
public class SweepGradient extends Shader {
|
||||
|
||||
private static final int TYPE_COLORS_AND_POSITIONS = 1;
|
||||
private static final int TYPE_COLOR_START_AND_COLOR_END = 2;
|
||||
|
||||
/**
|
||||
* Type of the LinearGradient: can be either TYPE_COLORS_AND_POSITIONS or
|
||||
* TYPE_COLOR_START_AND_COLOR_END.
|
||||
*/
|
||||
private int mType;
|
||||
|
||||
private float mCx;
|
||||
private float mCy;
|
||||
private int[] mColors;
|
||||
private float[] mPositions;
|
||||
private int mColor0;
|
||||
private int mColor1;
|
||||
|
||||
/**
|
||||
* A subclass of Shader that draws a sweep gradient around a center point.
|
||||
*
|
||||
* @param cx The x-coordinate of the center
|
||||
* @param cy The y-coordinate of the center
|
||||
* @param colors The colors to be distributed between around the center.
|
||||
* There must be at least 2 colors in the array.
|
||||
* @param positions May be NULL. The relative position of
|
||||
* each corresponding color in the colors array, beginning
|
||||
* with 0 and ending with 1.0. If the values are not
|
||||
* monotonic, the drawing may produce unexpected results.
|
||||
* If positions is NULL, then the colors are automatically
|
||||
* spaced evenly.
|
||||
*/
|
||||
public SweepGradient(float cx, float cy,
|
||||
int colors[], float positions[]) {
|
||||
if (colors.length < 2) {
|
||||
throw new IllegalArgumentException("needs >= 2 number of colors");
|
||||
}
|
||||
if (positions != null && colors.length != positions.length) {
|
||||
throw new IllegalArgumentException(
|
||||
"color and position arrays must be of equal length");
|
||||
}
|
||||
mType = TYPE_COLORS_AND_POSITIONS;
|
||||
mCx = cx;
|
||||
mCy = cy;
|
||||
mColors = colors;
|
||||
mPositions = positions;
|
||||
init(nativeCreate1(cx, cy, colors, positions));
|
||||
}
|
||||
|
||||
/**
|
||||
* A subclass of Shader that draws a sweep gradient around a center point.
|
||||
*
|
||||
* @param cx The x-coordinate of the center
|
||||
* @param cy The y-coordinate of the center
|
||||
* @param color0 The color to use at the start of the sweep
|
||||
* @param color1 The color to use at the end of the sweep
|
||||
*/
|
||||
public SweepGradient(float cx, float cy, int color0, int color1) {
|
||||
mType = TYPE_COLOR_START_AND_COLOR_END;
|
||||
mCx = cx;
|
||||
mCy = cy;
|
||||
mColor0 = color0;
|
||||
mColor1 = color1;
|
||||
init(nativeCreate2(cx, cy, color0, color1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
//@Override
|
||||
protected Shader copy() {
|
||||
final SweepGradient copy;
|
||||
switch (mType) {
|
||||
case TYPE_COLORS_AND_POSITIONS:
|
||||
copy = new SweepGradient(mCx, mCy, mColors.clone(),
|
||||
mPositions != null ? mPositions.clone() : null);
|
||||
break;
|
||||
case TYPE_COLOR_START_AND_COLOR_END:
|
||||
copy = new SweepGradient(mCx, mCy, mColor0, mColor1);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("SweepGradient should be created with either "
|
||||
+
|
||||
"colors and positions or start color and end color");
|
||||
}
|
||||
copyLocalMatrix(copy);
|
||||
return copy;
|
||||
}
|
||||
|
||||
private static native long nativeCreate1(float x, float y, int colors[], float positions[]);
|
||||
private static native long nativeCreate2(float x, float y, int color0, int color1);
|
||||
}
|
299
src/api-impl/android/util/MathUtils.java
Normal file
299
src/api-impl/android/util/MathUtils.java
Normal file
|
@ -0,0 +1,299 @@
|
|||
/*
|
||||
* Copyright (C) 2009 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 android.util;
|
||||
|
||||
//import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.graphics.Rect;
|
||||
|
||||
/**
|
||||
* A class that contains utility methods related to numbers.
|
||||
*
|
||||
* @hide Pending API council approval
|
||||
*/
|
||||
public final class MathUtils {
|
||||
private static final float DEG_TO_RAD = 3.1415926f / 180.0f;
|
||||
private static final float RAD_TO_DEG = 180.0f / 3.1415926f;
|
||||
|
||||
private MathUtils() {
|
||||
}
|
||||
|
||||
//@UnsupportedAppUsage
|
||||
public static float abs(float v) {
|
||||
return v > 0 ? v : -v;
|
||||
}
|
||||
|
||||
//@UnsupportedAppUsage
|
||||
public static int constrain(int amount, int low, int high) {
|
||||
return amount < low ? low : (amount > high ? high : amount);
|
||||
}
|
||||
|
||||
public static long constrain(long amount, long low, long high) {
|
||||
return amount < low ? low : (amount > high ? high : amount);
|
||||
}
|
||||
|
||||
//@UnsupportedAppUsage
|
||||
public static float constrain(float amount, float low, float high) {
|
||||
return amount < low ? low : (amount > high ? high : amount);
|
||||
}
|
||||
|
||||
public static float log(float a) {
|
||||
return (float)Math.log(a);
|
||||
}
|
||||
|
||||
public static float exp(float a) {
|
||||
return (float)Math.exp(a);
|
||||
}
|
||||
|
||||
public static float pow(float a, float b) {
|
||||
return (float)Math.pow(a, b);
|
||||
}
|
||||
|
||||
public static float sqrt(float a) {
|
||||
return (float)Math.sqrt(a);
|
||||
}
|
||||
|
||||
public static float max(float a, float b) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
//@UnsupportedAppUsage
|
||||
public static float max(int a, int b) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
public static float max(float a, float b, float c) {
|
||||
return a > b ? (a > c ? a : c) : (b > c ? b : c);
|
||||
}
|
||||
|
||||
public static float max(int a, int b, int c) {
|
||||
return a > b ? (a > c ? a : c) : (b > c ? b : c);
|
||||
}
|
||||
|
||||
public static float min(float a, float b) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
public static float min(int a, int b) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
public static float min(float a, float b, float c) {
|
||||
return a < b ? (a < c ? a : c) : (b < c ? b : c);
|
||||
}
|
||||
|
||||
public static float min(int a, int b, int c) {
|
||||
return a < b ? (a < c ? a : c) : (b < c ? b : c);
|
||||
}
|
||||
|
||||
public static float dist(float x1, float y1, float x2, float y2) {
|
||||
final float x = (x2 - x1);
|
||||
final float y = (y2 - y1);
|
||||
return (float)Math.hypot(x, y);
|
||||
}
|
||||
|
||||
public static float dist(float x1, float y1, float z1, float x2, float y2, float z2) {
|
||||
final float x = (x2 - x1);
|
||||
final float y = (y2 - y1);
|
||||
final float z = (z2 - z1);
|
||||
return (float)Math.sqrt(x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
public static float mag(float a, float b) {
|
||||
return (float)Math.hypot(a, b);
|
||||
}
|
||||
|
||||
public static float mag(float a, float b, float c) {
|
||||
return (float)Math.sqrt(a * a + b * b + c * c);
|
||||
}
|
||||
|
||||
public static float sq(float v) {
|
||||
return v * v;
|
||||
}
|
||||
|
||||
public static float dot(float v1x, float v1y, float v2x, float v2y) {
|
||||
return v1x * v2x + v1y * v2y;
|
||||
}
|
||||
|
||||
public static float cross(float v1x, float v1y, float v2x, float v2y) {
|
||||
return v1x * v2y - v1y * v2x;
|
||||
}
|
||||
|
||||
public static float radians(float degrees) {
|
||||
return degrees * DEG_TO_RAD;
|
||||
}
|
||||
|
||||
public static float degrees(float radians) {
|
||||
return radians * RAD_TO_DEG;
|
||||
}
|
||||
|
||||
public static float acos(float value) {
|
||||
return (float)Math.acos(value);
|
||||
}
|
||||
|
||||
public static float asin(float value) {
|
||||
return (float)Math.asin(value);
|
||||
}
|
||||
|
||||
public static float atan(float value) {
|
||||
return (float)Math.atan(value);
|
||||
}
|
||||
|
||||
public static float atan2(float a, float b) {
|
||||
return (float)Math.atan2(a, b);
|
||||
}
|
||||
|
||||
public static float tan(float angle) {
|
||||
return (float)Math.tan(angle);
|
||||
}
|
||||
|
||||
//@UnsupportedAppUsage
|
||||
public static float lerp(float start, float stop, float amount) {
|
||||
return start + (stop - start) * amount;
|
||||
}
|
||||
|
||||
public static float lerp(int start, int stop, float amount) {
|
||||
return lerp((float)start, (float)stop, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the interpolation scalar (s) that satisfies the equation: {@code value = }{@link
|
||||
* #lerp}{@code (a, b, s)}
|
||||
*
|
||||
* <p>If {@code a == b}, then this function will return 0.
|
||||
*/
|
||||
public static float lerpInv(float a, float b, float value) {
|
||||
return a != b ? ((value - a) / (b - a)) : 0.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the single argument constrained between [0.0, 1.0].
|
||||
*/
|
||||
public static float saturate(float value) {
|
||||
return constrain(value, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the saturated (constrained between [0, 1]) result of {@link #lerpInv}.
|
||||
*/
|
||||
public static float lerpInvSat(float a, float b, float value) {
|
||||
return saturate(lerpInv(a, b, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an interpolated angle in degrees between a set of start and end
|
||||
* angles.
|
||||
* <p>
|
||||
* Unlike {@link #lerp(float, float, float)}, the direction and distance of
|
||||
* travel is determined by the shortest angle between the start and end
|
||||
* angles. For example, if the starting angle is 0 and the ending angle is
|
||||
* 350, then the interpolated angle will be in the range [0,-10] rather
|
||||
* than [0,350].
|
||||
*
|
||||
* @param start the starting angle in degrees
|
||||
* @param end the ending angle in degrees
|
||||
* @param amount the position between start and end in the range [0,1]
|
||||
* where 0 is the starting angle and 1 is the ending angle
|
||||
* @return the interpolated angle in degrees
|
||||
*/
|
||||
public static float lerpDeg(float start, float end, float amount) {
|
||||
final float minAngle = (((end - start) + 180) % 360) - 180;
|
||||
return minAngle * amount + start;
|
||||
}
|
||||
|
||||
public static float norm(float start, float stop, float value) {
|
||||
return (value - start) / (stop - start);
|
||||
}
|
||||
|
||||
public static float map(float minStart, float minStop, float maxStart, float maxStop, float value) {
|
||||
return maxStart + (maxStop - maxStart) * ((value - minStart) / (minStop - minStart));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a value in [rangeMin, rangeMax] that maps value in [valueMin, valueMax] to
|
||||
* returnVal in [rangeMin, rangeMax].
|
||||
* <p>
|
||||
* Always returns a constrained value in the range [rangeMin, rangeMax], even if value is
|
||||
* outside [valueMin, valueMax].
|
||||
* <p>
|
||||
* Eg:
|
||||
* constrainedMap(0f, 100f, 0f, 1f, 0.5f) = 50f
|
||||
* constrainedMap(20f, 200f, 10f, 20f, 20f) = 200f
|
||||
* constrainedMap(20f, 200f, 10f, 20f, 50f) = 200f
|
||||
* constrainedMap(10f, 50f, 10f, 20f, 5f) = 10f
|
||||
*
|
||||
* @param rangeMin minimum of the range that should be returned.
|
||||
* @param rangeMax maximum of the range that should be returned.
|
||||
* @param valueMin minimum of range to map {@code value} to.
|
||||
* @param valueMax maximum of range to map {@code value} to.
|
||||
* @param value to map to the range [{@code valueMin}, {@code valueMax}]. Note, can be outside
|
||||
* this range, resulting in a clamped value.
|
||||
* @return the mapped value, constrained to [{@code rangeMin}, {@code rangeMax}.
|
||||
*/
|
||||
public static float constrainedMap(
|
||||
float rangeMin, float rangeMax, float valueMin, float valueMax, float value) {
|
||||
return lerp(rangeMin, rangeMax, lerpInvSat(valueMin, valueMax, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform Hermite interpolation between two values.
|
||||
* Eg:
|
||||
* smoothStep(0, 0.5f, 0.5f) = 1f
|
||||
* smoothStep(0, 0.5f, 0.25f) = 0.5f
|
||||
*
|
||||
* @param start Left edge.
|
||||
* @param end Right edge.
|
||||
* @param x A value between {@code start} and {@code end}.
|
||||
* @return A number between 0 and 1 representing where {@code x} is in the interpolation.
|
||||
*/
|
||||
public static float smoothStep(float start, float end, float x) {
|
||||
return constrain((x - start) / (end - start), 0f, 1f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sum of the two parameters, or throws an exception if the resulting sum would
|
||||
* cause an overflow or underflow.
|
||||
* @throws IllegalArgumentException when overflow or underflow would occur.
|
||||
*/
|
||||
public static int addOrThrow(int a, int b) throws IllegalArgumentException {
|
||||
if (b == 0) {
|
||||
return a;
|
||||
}
|
||||
|
||||
if (b > 0 && a <= (Integer.MAX_VALUE - b)) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
if (b < 0 && a >= (Integer.MIN_VALUE - b)) {
|
||||
return a + b;
|
||||
}
|
||||
throw new IllegalArgumentException("Addition overflow: " + a + " + " + b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize a {@link Rect} so one size would be {@param largestSide}.
|
||||
*
|
||||
* @param outToResize Rectangle that will be resized.
|
||||
* @param largestSide Size of the largest side.
|
||||
*/
|
||||
public static void fitRect(Rect outToResize, int largestSide) {
|
||||
if (outToResize.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
float maxSize = Math.max(outToResize.width(), outToResize.height());
|
||||
outToResize.scale(largestSide / maxSize);
|
||||
}
|
||||
}
|
165
src/api-impl/android/util/Pools.java
Normal file
165
src/api-impl/android/util/Pools.java
Normal file
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* Copyright (C) 2009 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 android.util;
|
||||
|
||||
/**
|
||||
* Helper class for crating pools of objects. An example use looks like this:
|
||||
* <pre>
|
||||
* public class MyPooledClass {
|
||||
*
|
||||
* private static final SynchronizedPool<MyPooledClass> sPool =
|
||||
* new SynchronizedPool<MyPooledClass>(10);
|
||||
*
|
||||
* public static MyPooledClass obtain() {
|
||||
* MyPooledClass instance = sPool.acquire();
|
||||
* return (instance != null) ? instance : new MyPooledClass();
|
||||
* }
|
||||
*
|
||||
* public void recycle() {
|
||||
* // Clear state if needed.
|
||||
* sPool.release(this);
|
||||
* }
|
||||
*
|
||||
* . . .
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final class Pools {
|
||||
|
||||
/**
|
||||
* Interface for managing a pool of objects.
|
||||
*
|
||||
* @param <T> The pooled type.
|
||||
*/
|
||||
public static interface Pool<T> {
|
||||
|
||||
/**
|
||||
* @return An instance from the pool if such, null otherwise.
|
||||
*/
|
||||
public T acquire();
|
||||
|
||||
/**
|
||||
* Release an instance to the pool.
|
||||
*
|
||||
* @param instance The instance to release.
|
||||
* @return Whether the instance was put in the pool.
|
||||
*
|
||||
* @throws IllegalStateException If the instance is already in the pool.
|
||||
*/
|
||||
public boolean release(T instance);
|
||||
}
|
||||
|
||||
private Pools() {
|
||||
/* do nothing - hiding constructor */
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple (non-synchronized) pool of objects.
|
||||
*
|
||||
* @param <T> The pooled type.
|
||||
*/
|
||||
public static class SimplePool<T> implements Pool<T> {
|
||||
private final Object[] mPool;
|
||||
|
||||
private int mPoolSize;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param maxPoolSize The max pool size.
|
||||
*
|
||||
* @throws IllegalArgumentException If the max pool size is less than zero.
|
||||
*/
|
||||
public SimplePool(int maxPoolSize) {
|
||||
if (maxPoolSize <= 0) {
|
||||
throw new IllegalArgumentException("The max pool size must be > 0");
|
||||
}
|
||||
mPool = new Object[maxPoolSize];
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public T acquire() {
|
||||
if (mPoolSize > 0) {
|
||||
final int lastPooledIndex = mPoolSize - 1;
|
||||
T instance = (T)mPool[lastPooledIndex];
|
||||
mPool[lastPooledIndex] = null;
|
||||
mPoolSize--;
|
||||
return instance;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean release(T instance) {
|
||||
if (isInPool(instance)) {
|
||||
throw new IllegalStateException("Already in the pool!");
|
||||
}
|
||||
if (mPoolSize < mPool.length) {
|
||||
mPool[mPoolSize] = instance;
|
||||
mPoolSize++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isInPool(T instance) {
|
||||
for (int i = 0; i < mPoolSize; i++) {
|
||||
if (mPool[i] == instance) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronized) pool of objects.
|
||||
*
|
||||
* @param <T> The pooled type.
|
||||
*/
|
||||
public static class SynchronizedPool<T> extends SimplePool<T> {
|
||||
private final Object mLock = new Object();
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param maxPoolSize The max pool size.
|
||||
*
|
||||
* @throws IllegalArgumentException If the max pool size is less than zero.
|
||||
*/
|
||||
public SynchronizedPool(int maxPoolSize) {
|
||||
super(maxPoolSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T acquire() {
|
||||
synchronized (mLock) {
|
||||
return super.acquire();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean release(T element) {
|
||||
synchronized (mLock) {
|
||||
return super.release(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -210,6 +210,17 @@ public class TypedValue {
|
|||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* {@link #TYPE_NULL} data indicating the value was not specified.
|
||||
*/
|
||||
public static final int DATA_NULL_UNDEFINED = 0;
|
||||
/**
|
||||
* {@link #TYPE_NULL} data indicating the value was explicitly set to null.
|
||||
*/
|
||||
public static final int DATA_NULL_EMPTY = 1;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* If {@link #density} is equal to this value, then the density should be
|
||||
* treated as the system's default density value: {@link DisplayMetrics#DENSITY_DEFAULT}.
|
||||
|
|
|
@ -27,6 +27,7 @@ package com.android.internal.util;
|
|||
* @hide
|
||||
*/
|
||||
public final class GrowingArrayUtils {
|
||||
|
||||
/**
|
||||
* Appends an element to the end of the array, growing the array if there is no more room.
|
||||
* @param array The array to which to append the element. This must NOT be null.
|
||||
|
@ -38,10 +39,11 @@ public final class GrowingArrayUtils {
|
|||
*/
|
||||
public static <T> T[] append(T[] array, int currentSize, T element) {
|
||||
assert currentSize <= array.length;
|
||||
|
||||
if (currentSize + 1 > array.length) {
|
||||
@SuppressWarnings("unchecked")
|
||||
T[] newArray = ArrayUtils.newUnpaddedArray(
|
||||
(Class<T>) array.getClass().getComponentType(), growSize(currentSize));
|
||||
(Class<T>)array.getClass().getComponentType(), growSize(currentSize));
|
||||
System.arraycopy(array, 0, newArray, 0, currentSize);
|
||||
array = newArray;
|
||||
}
|
||||
|
@ -54,6 +56,7 @@ public final class GrowingArrayUtils {
|
|||
*/
|
||||
public static int[] append(int[] array, int currentSize, int element) {
|
||||
assert currentSize <= array.length;
|
||||
|
||||
if (currentSize + 1 > array.length) {
|
||||
int[] newArray = ArrayUtils.newUnpaddedIntArray(growSize(currentSize));
|
||||
System.arraycopy(array, 0, newArray, 0, currentSize);
|
||||
|
@ -68,6 +71,7 @@ public final class GrowingArrayUtils {
|
|||
*/
|
||||
public static long[] append(long[] array, int currentSize, long element) {
|
||||
assert currentSize <= array.length;
|
||||
|
||||
if (currentSize + 1 > array.length) {
|
||||
long[] newArray = ArrayUtils.newUnpaddedLongArray(growSize(currentSize));
|
||||
System.arraycopy(array, 0, newArray, 0, currentSize);
|
||||
|
@ -82,6 +86,7 @@ public final class GrowingArrayUtils {
|
|||
*/
|
||||
public static boolean[] append(boolean[] array, int currentSize, boolean element) {
|
||||
assert currentSize <= array.length;
|
||||
|
||||
if (currentSize + 1 > array.length) {
|
||||
boolean[] newArray = ArrayUtils.newUnpaddedBooleanArray(growSize(currentSize));
|
||||
System.arraycopy(array, 0, newArray, 0, currentSize);
|
||||
|
@ -91,6 +96,21 @@ public final class GrowingArrayUtils {
|
|||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Primitive float version of {@link #append(Object[], int, Object)}.
|
||||
*/
|
||||
public static float[] append(float[] array, int currentSize, float element) {
|
||||
assert currentSize <= array.length;
|
||||
|
||||
if (currentSize + 1 > array.length) {
|
||||
float[] newArray = ArrayUtils.newUnpaddedFloatArray(growSize(currentSize));
|
||||
System.arraycopy(array, 0, newArray, 0, currentSize);
|
||||
array = newArray;
|
||||
}
|
||||
array[currentSize] = element;
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts an element into the array at the specified index, growing the array if there is no
|
||||
* more room.
|
||||
|
@ -104,14 +124,16 @@ public final class GrowingArrayUtils {
|
|||
*/
|
||||
public static <T> T[] insert(T[] array, int currentSize, int index, T element) {
|
||||
assert currentSize <= array.length;
|
||||
|
||||
if (currentSize + 1 <= array.length) {
|
||||
System.arraycopy(array, index, array, index + 1, currentSize - index);
|
||||
array[index] = element;
|
||||
return array;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
T[] newArray = ArrayUtils.newUnpaddedArray((Class<T>)array.getClass().getComponentType(),
|
||||
growSize(currentSize));
|
||||
growSize(currentSize));
|
||||
System.arraycopy(array, 0, newArray, 0, index);
|
||||
newArray[index] = element;
|
||||
System.arraycopy(array, index, newArray, index + 1, array.length - index);
|
||||
|
@ -123,11 +145,13 @@ public final class GrowingArrayUtils {
|
|||
*/
|
||||
public static int[] insert(int[] array, int currentSize, int index, int element) {
|
||||
assert currentSize <= array.length;
|
||||
|
||||
if (currentSize + 1 <= array.length) {
|
||||
System.arraycopy(array, index, array, index + 1, currentSize - index);
|
||||
array[index] = element;
|
||||
return array;
|
||||
}
|
||||
|
||||
int[] newArray = ArrayUtils.newUnpaddedIntArray(growSize(currentSize));
|
||||
System.arraycopy(array, 0, newArray, 0, index);
|
||||
newArray[index] = element;
|
||||
|
@ -140,11 +164,13 @@ public final class GrowingArrayUtils {
|
|||
*/
|
||||
public static long[] insert(long[] array, int currentSize, int index, long element) {
|
||||
assert currentSize <= array.length;
|
||||
|
||||
if (currentSize + 1 <= array.length) {
|
||||
System.arraycopy(array, index, array, index + 1, currentSize - index);
|
||||
array[index] = element;
|
||||
return array;
|
||||
}
|
||||
|
||||
long[] newArray = ArrayUtils.newUnpaddedLongArray(growSize(currentSize));
|
||||
System.arraycopy(array, 0, newArray, 0, index);
|
||||
newArray[index] = element;
|
||||
|
@ -157,11 +183,13 @@ public final class GrowingArrayUtils {
|
|||
*/
|
||||
public static boolean[] insert(boolean[] array, int currentSize, int index, boolean element) {
|
||||
assert currentSize <= array.length;
|
||||
|
||||
if (currentSize + 1 <= array.length) {
|
||||
System.arraycopy(array, index, array, index + 1, currentSize - index);
|
||||
array[index] = element;
|
||||
return array;
|
||||
}
|
||||
|
||||
boolean[] newArray = ArrayUtils.newUnpaddedBooleanArray(growSize(currentSize));
|
||||
System.arraycopy(array, 0, newArray, 0, index);
|
||||
newArray[index] = element;
|
||||
|
@ -177,6 +205,7 @@ public final class GrowingArrayUtils {
|
|||
public static int growSize(int currentSize) {
|
||||
return currentSize <= 4 ? 8 : currentSize * 2;
|
||||
}
|
||||
|
||||
// Uninstantiable
|
||||
private GrowingArrayUtils() {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -118,9 +118,13 @@ srcs = [
|
|||
'android/content/res/AssetManager.java',
|
||||
'android/content/res/ColorStateList.java',
|
||||
'android/content/res/CompatibilityInfo.java',
|
||||
'android/content/res/ComplexColor.java',
|
||||
'android/content/res/Configuration.java',
|
||||
'android/content/res/ConfigurationBoundResourceCache.java',
|
||||
'android/content/res/GradientColor.java',
|
||||
'android/content/res/Resources.java',
|
||||
'android/content/res/StringBlock.java',
|
||||
'android/content/res/ThemedResourceCache.java',
|
||||
'android/content/res/TypedArray.java',
|
||||
'android/content/res/XmlBlock.java',
|
||||
'android/content/res/XmlResourceParser.java',
|
||||
|
@ -210,6 +214,7 @@ srcs = [
|
|||
'android/graphics/RectF.java',
|
||||
'android/graphics/Region.java',
|
||||
'android/graphics/Shader.java',
|
||||
'android/graphics/SweepGradient.java',
|
||||
'android/graphics/Typeface.java',
|
||||
'android/graphics/Xfermode.java',
|
||||
'android/graphics/drawable/Animatable.java',
|
||||
|
@ -426,6 +431,7 @@ srcs = [
|
|||
'android/util/JsonScope.java',
|
||||
'android/util/JsonToken.java',
|
||||
'android/util/JsonWriter.java',
|
||||
'android/util/MathUtils.java',
|
||||
'android/util/LayoutDirection.java',
|
||||
'android/util/Log.java',
|
||||
'android/util/LongSparseArray.java',
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue