package carbon.drawable.ripple; import android.annotation.TargetApi; import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.Build; import android.util.TypedValue; import java.lang.reflect.Field; import java.util.Arrays; import carbon.Carbon; class TypedArrayCompat { private static final int[] TEMP_ARRAY = new int[1]; private final static ITypedArray IMPL; static { if (Carbon.IS_LOLLIPOP) { IMPL = new TypedArrayLollipop(); } else { IMPL = new BaseTypedArray(); } } /** * Retrieve the ColorStateList for the attribute at <var>index</var>. * The value may be either a single solid color or a reference to * a color or complex {@link ColorStateList} description. * * @param index Index of attribute to retrieve. * @return ColorStateList for the attribute, or null if not defined. */ public static ColorStateList getColorStateList(Resources.Theme theme, TypedArray a, TypedValue[] values, int index) { if (values != null && theme != null) { TypedValue v = values[index]; if (v.type == TypedValue.TYPE_ATTRIBUTE) { TEMP_ARRAY[0] = v.data; TypedArray tmp = theme.obtainStyledAttributes(null, TEMP_ARRAY, 0, 0); try { return tmp.getColorStateList(0); } finally { tmp.recycle(); } } } if (a != null) { return a.getColorStateList(index); } return null; } /** * Return a mask of the configuration parameters for which the values in * this typed array may change. * * @return Returns a mask of the changing configuration parameters, as defined by {@link * android.content.pm.ActivityInfo}. * @see android.content.pm.ActivityInfo */ public static int getChangingConfigurations(TypedArray array) { return IMPL.getChangingConfigurations(array); } /** * Retrieve the Drawable for the attribute at <var>index</var>. * * @param index Index of attribute to retrieve. * @return Drawable for the attribute, or null if not defined. */ public static Drawable getDrawable(Resources.Theme theme, TypedArray a, TypedValue[] values, int index) { if (values != null && theme != null) { TypedValue v = values[index]; if (v.type == TypedValue.TYPE_ATTRIBUTE) { TEMP_ARRAY[0] = v.data; TypedArray tmp = theme.obtainStyledAttributes(null, TEMP_ARRAY, 0, 0); try { return tmp.getDrawable(0); } finally { tmp.recycle(); } } } if (a != null) { return LollipopDrawablesCompat.getDrawable(a, index, theme); } return null; } /** * Retrieve the resource identifier for the attribute at * <var>index</var>. Note that attribute resource as resolved when * the overall {@link TypedArray} object is retrieved. As a * result, this function will return the resource identifier of the * final resource value that was found, <em>not</em> necessarily the * original resource that was specified by the attribute. * * @param index Index of attribute to retrieve. * @param def Value to return if the attribute is not defined or not a resource. * @return Attribute resource identifier, or defValue if not defined. */ public static int getResourceId(Resources.Theme theme, TypedArray a, TypedValue[] values, int index, int def) { if (values != null && theme != null) { TypedValue v = values[index]; if (v.type == TypedValue.TYPE_ATTRIBUTE) { TEMP_ARRAY[0] = v.data; TypedArray tmp = theme.obtainStyledAttributes(null, TEMP_ARRAY, 0, 0); try { return tmp.getResourceId(0, def); } finally { tmp.recycle(); } } } if (a != null) { return a.getResourceId(index, def); } return def; } /** * Retrieve a dimensional unit attribute at <var>index</var> for use * as an offset in raw pixels. This is the same as * {@link TypedArray#getDimension}, except the returned value is converted to * integer pixels for you. An offset conversion involves simply * truncating the base value to an integer. * <p/> * Retrieve from extracted first if no value than tries from {@link TypedArray} * * @param index Index of attribute to retrieve. * @param def Value to return if the attribute is not defined or not a resource. * @return Attribute dimension value multiplied by the appropriate metric and truncated to * integer pixels, or defValue if not defined. * @see TypedArray#getDimension * @see TypedArray#getDimensionPixelSize */ public static int getDimensionPixelOffset(Resources.Theme theme, TypedArray a, TypedValue[] values, int index, int def) { if (values != null && theme != null) { TypedValue v = values[index]; if (v.type == TypedValue.TYPE_ATTRIBUTE) { TEMP_ARRAY[0] = v.data; TypedArray tmp = theme.obtainStyledAttributes(null, TEMP_ARRAY, 0, 0); try { return tmp.getDimensionPixelOffset(0, def); } finally { tmp.recycle(); } } } if (a != null) { return a.getDimensionPixelOffset(index, def); } return def; } /*package*/ static final int STYLE_NUM_ENTRIES = 6; /*package*/ static final int STYLE_TYPE = 0; /*package*/ static final int STYLE_DATA = 1; /*package*/ static final int STYLE_ASSET_COOKIE = 2; /*package*/ static final int STYLE_RESOURCE_ID = 3; /*package*/ static final int STYLE_CHANGING_CONFIGURATIONS = 4; /*package*/ static final int STYLE_DENSITY = 5; /** * Extracts theme attributes from a typed array for later resolution using * {@link Resources.Theme#resolveAttributes(int[], int[])}. * Removes the entries from the typed array so that subsequent calls to typed * getters will return the default value without crashing. * * @return an array of length {@link TypedArray#getIndexCount()} populated with theme * attributes, or null if there are no theme attributes in the typed array */ public static int[] extractThemeAttrs(TypedArray array) { int[] scrap = null; /*if (mRecycled) { throw new RuntimeException("Cannot make calls to a recycled instance!"); }*/ int[] attrs = null; int[] mData = null; try { Field mDataField = array.getClass().getDeclaredField("mData"); mDataField.setAccessible(true); mData = (int[]) mDataField.get(array); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } final int[] data = mData; final int N = array.length(); for (int i = 0; i < N; i++) { final int index = i * STYLE_NUM_ENTRIES; if (data[index + STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { // Not an attribute, ignore. continue; } // Null the entry so that we can safely call getZzz(). data[index + STYLE_TYPE] = TypedValue.TYPE_NULL; final int attr = data[index + STYLE_DATA]; if (attr == 0) { // Useless data, ignore. continue; } // Ensure we have a usable attribute array. if (attrs == null) { if (scrap != null && scrap.length == N) { attrs = scrap; Arrays.fill(attrs, 0); } else { attrs = new int[N]; } } attrs[i] = attr; } return attrs; } interface ITypedArray { int getChangingConfigurations(TypedArray array); } static class BaseTypedArray implements ITypedArray { @Override public int getChangingConfigurations(TypedArray array) { return 0; } } @TargetApi(Build.VERSION_CODES.LOLLIPOP) static class TypedArrayLollipop extends BaseTypedArray { @Override public int getChangingConfigurations(TypedArray array) { return array.getChangingConfigurations(); } } }