/* * Copyright (C) 2008 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 java.util.Arrays; import java.util.HashMap; import com.intel.mpt.annotation.MayloonStubAnnotation; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.util.MathUtils; import android.util.TypedValue; /** * Container for an array of values that were retrieved with * {@link Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)} * or {@link Resources#obtainAttributes}. Be * sure to call {@link #recycle} when done with them. * * The indices used to retrieve values from this structure correspond to * the positions of the attributes given to obtainStyledAttributes. */ public class TypedArray { private final Resources mResources; /*package*/XmlBlock.Parser mXml; /*package*/int[] mRsrcs; /*package*/int[] mData; /*package*/int[] mIndices; /*package*/int mLength; private TypedValue mValue = new TypedValue(); public HashMap<Integer, String> mPooledStrings = new HashMap<Integer, String>(); /** * Return the number of values in this array. */ public int length() { return mLength; } /** * Return the number of indices in the array that actually have data. */ public int getIndexCount() { return mIndices[0]; } /** * Return an index in the array that has data. * * @param at The index you would like to returned, ranging from 0 to * {@link #getIndexCount()}. * * @return The index at the given offset, which can be used with * {@link #getValue} and related APIs. */ public int getIndex(int at) { return mIndices[1 + at]; } /** * 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 defValue Value to return if the attribute is not defined or * not a resource. * * @return Attribute resource identifier, or defValue if not defined. */ public int getResourceId(int index, int defValue) { index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; if (data[index + AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) { final int resid = data[index + AssetManager.STYLE_RESOURCE_ID]; if (resid != 0) { return resid; } } return defValue; } /** * Retrieve the styled string value for the attribute at <var>index</var>. * * @param index Index of attribute to retrieve. * * @return CharSequence holding string data. May be styled. Returns * null if the attribute is not defined. */ public CharSequence getText(int index) { // System.out.println("getText index " + index); index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index + AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return null; } else if (type == TypedValue.TYPE_STRING) { return loadStringValueAt(index); } TypedValue v = mValue; if (getValueAt(index, v)) { Log.w(Resources.TAG, "Converting to string: " + v); return v.coerceToString(); } System.out.println(Resources.TAG + "getString of bad type: 0x" + Integer.toHexString(type)); return null; } /** * Retrieve the string value for the attribute at <var>index</var>. * * @param index Index of attribute to retrieve. * * @return String holding string data. Any styling information is * removed. Returns null if the attribute is not defined. */ public String getString(int index) { index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index + AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return null; } else if (type == TypedValue.TYPE_STRING) { return loadStringValueAt(index).toString(); } TypedValue v = mValue; if (getValueAt(index, v)) { Log.w(Resources.TAG, "Converting to string: " + v); CharSequence cs = v.coerceToString(); return cs != null ? cs.toString() : null; } Log.w(Resources.TAG, "getString of bad type: 0x" + Integer.toHexString(type)); return null; } /** * Retrieve the boolean value for the attribute at <var>index</var>. * * @param index Index of attribute to retrieve. * @param defValue Value to return if the attribute is not defined. * * @return Attribute boolean value, or defValue if not defined. */ public boolean getBoolean(int index, boolean defValue) { index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index + AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { return data[index + AssetManager.STYLE_DATA] != 0; } TypedValue v = mValue; if (getValueAt(index, v)) { Log.w(Resources.TAG, "Converting to boolean: " + v); return convertValueToBoolean(v.coerceToString(), defValue); } Log.w(Resources.TAG, "getBoolean of bad type: 0x" + Integer.toHexString(type)); return defValue; } /** * Retrieve the integer value for the attribute at <var>index</var>. * * @param index Index of attribute to retrieve. * @param defValue Value to return if the attribute is not defined. * * @return Attribute int value, or defValue if not defined. */ public int getInt(int index, int defValue) { index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index + AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { // System.out.println("index: " + index + ", value: " // + data[index + AssetManager.STYLE_DATA]); return data[index + AssetManager.STYLE_DATA]; } TypedValue v = mValue; if (getValueAt(index, v)) { Log.w(Resources.TAG, "Converting to int: " + v); return convertValueToInt(v.coerceToString(), defValue); } Log.w(Resources.TAG, "getInt of bad type: 0x" + Integer.toHexString(type)); return defValue; } /** * Retrieve the float value for the attribute at <var>index</var>. * * @param index Index of attribute to retrieve. * * @return Attribute float value, or defValue if not defined.. */ public float getFloat(int index, float defValue) { index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index + AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_FLOAT) { return MathUtils.myIntBitsToFloat(data[index + AssetManager.STYLE_DATA]); } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { return data[index + AssetManager.STYLE_DATA]; } TypedValue v = mValue; if (getValueAt(index, v)) { Log.w(Resources.TAG, "Converting to float: " + v); CharSequence str = v.coerceToString(); if (str != null) { return Float.parseFloat(str.toString()); } } Log.w(Resources.TAG, "getFloat of bad type: 0x" + Integer.toHexString(type)); return defValue; } /** * Retrieve a dimensional unit attribute at <var>index</var>. Unit * conversions are based on the current {@link DisplayMetrics} * associated with the resources this {@link TypedArray} object * came from. * * @param index Index of attribute to retrieve. * @param defValue Value to return if the attribute is not defined or * not a resource. * * @return Attribute dimension value multiplied by the appropriate * metric, or defValue if not defined. * * @see #getDimensionPixelOffset * @see #getDimensionPixelSize */ public float getDimension(int index, float defValue) { index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index + AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { return TypedValue.complexToDimension(data[index + AssetManager.STYLE_DATA], mResources.mMetrics); } throw new UnsupportedOperationException( "Can't convert to dimension: type=0x" + Integer.toHexString(type)); } /** * Retrieve a dimensional unit attribute at <var>index</var> for use * as an offset in raw pixels. This is the same as * {@link #getDimension}, except the returned value is converted to * integer pixels for you. An offset conversion involves simply * truncating the base value to an integer. * * @param index Index of attribute to retrieve. * @param defValue 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 #getDimension * @see #getDimensionPixelSize */ public int getDimensionPixelOffset(int index, int defValue) { index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index + AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { return TypedValue.complexToDimensionPixelOffset(data[index + AssetManager.STYLE_DATA], mResources.mMetrics); } throw new UnsupportedOperationException( "Can't convert to dimension: type=0x" + Integer.toHexString(type)); } /** * Retrieve a dimensional unit attribute at <var>index</var> for use * as a size in raw pixels. This is the same as * {@link #getDimension}, except the returned value is converted to * integer pixels for use as a size. A size conversion involves * rounding the base value, and ensuring that a non-zero base value * is at least one pixel in size. * * @param index Index of attribute to retrieve. * @param defValue 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 #getDimension * @see #getDimensionPixelOffset */ public int getDimensionPixelSize(int index, int defValue) { index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index + AssetManager.STYLE_TYPE]; // System.out.println("type is " + type); if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { return TypedValue.complexToDimensionPixelSize(data[index + AssetManager.STYLE_DATA], mResources.mMetrics); } throw new UnsupportedOperationException( "Can't convert to dimension: type=0x" + Integer.toHexString(type)); } /** * Special version of {@link #getDimensionPixelSize} for retrieving * {@link android.view.ViewGroup}'s layout_width and layout_height * attributes. This is only here for performance reasons; applications * should use {@link #getDimensionPixelSize}. * * @param index Index of the attribute to retrieve. * @param name Textual name of attribute for error reporting. * * @return Attribute dimension value multiplied by the appropriate * metric and truncated to integer pixels. */ public int getLayoutDimension(int index, String name) { index *= AssetManager.STYLE_NUM_ENTRIES; if (index < 0 || index >= mData.length) { throw new ArrayIndexOutOfBoundsException( "TypedArray.getLayoutDimension param index is Out of Bounds"); } final int[] data = mData; final int type = data[index + AssetManager.STYLE_TYPE]; // System.out.println("attribute " + name + " type: "+ type + " value: " + data[index+AssetManager.STYLE_DATA]); if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { return data[index + AssetManager.STYLE_DATA]; } else if (type == TypedValue.TYPE_DIMENSION) { // return data[index + AssetManager.STYLE_DATA]; int rValue = TypedValue.complexToDimensionPixelSize(data[index + AssetManager.STYLE_DATA], mResources.mMetrics); // System.out.println("rValue: " + rValue); return rValue; } throw new RuntimeException(": You must supply a " + name + " attribute."); } /** * Special version of {@link #getDimensionPixelSize} for retrieving * {@link android.view.ViewGroup}'s layout_width and layout_height * attributes. This is only here for performance reasons; applications * should use {@link #getDimensionPixelSize}. * * @param index Index of the attribute to retrieve. * @param defValue The default value to return if this attribute is not * default or contains the wrong type of data. * * @return Attribute dimension value multiplied by the appropriate * metric and truncated to integer pixels. */ public int getLayoutDimension(int index, int defValue) { index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index+AssetManager.STYLE_TYPE]; if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { return data[index+AssetManager.STYLE_DATA]; } else if (type == TypedValue.TYPE_DIMENSION) { return TypedValue.complexToDimensionPixelSize( data[index+AssetManager.STYLE_DATA], mResources.mMetrics); } return defValue; } /** * Retrieve a fractional unit attribute at <var>index</var>. * * @param index Index of attribute to retrieve. * @param base The base value of this fraction. In other words, a * standard fraction is multiplied by this value. * @param pbase The parent base value of this fraction. In other * words, a parent fraction (nn%p) is multiplied by this * value. * @param defValue Value to return if the attribute is not defined or * not a resource. * * @return Attribute fractional value multiplied by the appropriate * base value, or defValue if not defined. */ public float getFraction(int index, int base, int pbase, float defValue) { index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_FRACTION) { return TypedValue.complexToFraction( data[index+AssetManager.STYLE_DATA], base, pbase); } throw new UnsupportedOperationException("Can't convert to fraction: type=0x" + Integer.toHexString(type)); } /** * Give back a previously retrieved StyledAttributes, for later re-use. */ public void recycle() { synchronized (mResources.mTmpValue) { TypedArray cached = mResources.mCachedStyledAttributes; if (cached == null || cached.mData.length < mData.length) { mXml = null; mResources.mCachedStyledAttributes = this; } } } private boolean getValueAt(int index, TypedValue outValue) { final int[] data = mData; final int type = data[index + AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return false; } outValue.type = type; outValue.data = data[index + AssetManager.STYLE_DATA]; outValue.assetCookie = data[index + AssetManager.STYLE_ASSET_COOKIE]; outValue.resourceId = data[index + AssetManager.STYLE_RESOURCE_ID]; outValue.changingConfigurations = data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]; outValue.density = data[index + AssetManager.STYLE_DENSITY]; outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null; return true; } private CharSequence loadStringValueAt(int index) { final int[] data = mData; final int cookie = data[index + AssetManager.STYLE_ASSET_COOKIE]; if (cookie < 0) { if (mXml != null) { return mXml.getPooledString(data[index + AssetManager.STYLE_DATA]); } return null; } //System.out.println("Getting pooled from: " + v); return mResources.mAssets.getPooledString(cookie, data[index + AssetManager.STYLE_DATA]); } TypedArray(Resources resources, int[] data, int[] indices, int len) { mResources = resources; mData = data; mIndices = indices; mLength = len; } public String toString() { return Arrays.toString(mData); } public TypedValue peekValue(int index) { final TypedValue value = mValue; if (getValueAt(index * AssetManager.STYLE_NUM_ENTRIES, value)) { return value; } return null; } /** * Retrieve the string value for the attribute at <var>index</var>, but only * if that string comes from an immediate value in an XML file. That is, * this does not allow references to string resources, string attributes, or * conversions from other types. As such, this method will only return * strings for TypedArray objects that come from attributes in an XML file. * * @param index Index of attribute to retrieve. * @return String holding string data. Any styling information is removed. * Returns null if the attribute is not defined or is not an * immediate string value. */ public String getNonResourceString(int index) { index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index + AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_STRING) { final int cookie = data[index + AssetManager.STYLE_ASSET_COOKIE]; if (cookie < 0) { return mXml.getPooledString( data[index + AssetManager.STYLE_DATA]).toString(); } } return null; } public String getNonConfigurationString(int index, int allowedChangingConfigs) { index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index + AssetManager.STYLE_TYPE]; if ((data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS] & ~allowedChangingConfigs) != 0) { // System.out.println("wrong pass1"); return null; } if (type == TypedValue.TYPE_NULL) { // System.out.println("wrong pass2"); return null; } else if (type == TypedValue.TYPE_STRING) { // System.out.println("right pass"); return loadStringValueAt(index).toString(); } TypedValue v = mValue; if (getValueAt(index, v)) { CharSequence cs = v.coerceToString(); return cs != null ? cs.toString() : null; } return null; } public static final int convertValueToInt(CharSequence charSeq, int defaultValue) { if (null == charSeq) return defaultValue; String nm = charSeq.toString(); // XXX This code is copied from Integer.decode() so we don't // have to instantiate an Integer! int sign = 1; int index = 0; int len = nm.length(); int base = 10; if ('-' == nm.charAt(0)) { sign = -1; index++; } if ('0' == nm.charAt(index)) { // Quick check for a zero by itself if (index == (len - 1)) return 0; char c = nm.charAt(index + 1); if ('x' == c || 'X' == c) { index += 2; base = 16; } else { index++; base = 8; } } else if ('#' == nm.charAt(index)) { index++; base = 16; } return Integer.parseInt(nm.substring(index), base) * sign; } public static final boolean convertValueToBoolean(CharSequence value, boolean defaultValue) { boolean result = false; if (null == value) return defaultValue; if (value.equals("1") || value.equals("true") || value.equals("TRUE")) result = true; return result; } /** * Retrieve the raw TypedValue for the attribute at <var>index</var>. * * @param index Index of attribute to retrieve. * @param outValue TypedValue object in which to place the attribute's * data. * * @return Returns true if the value was retrieved, else false. */ public boolean getValue(int index, TypedValue outValue) { return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue); } /** * Determines whether there is an attribute at <var>index</var>. * * @param index Index of attribute to retrieve. * * @return True if the attribute has a value, false otherwise. */ public boolean hasValue(int index) { index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index + AssetManager.STYLE_TYPE]; return type != TypedValue.TYPE_NULL; } /** * Retrieve the Drawable for the attribute at <var>index</var>. This * gets the resource ID of the selected attribute, and uses * {@link Resources#getDrawable Resources.getDrawable} of the owning * Resources object to retrieve its Drawable. * * @param index Index of attribute to retrieve. * * @return Drawable for the attribute, or null if not defined. */ public Drawable getDrawable(int index) { final TypedValue value = mValue; if (getValueAt(index * AssetManager.STYLE_NUM_ENTRIES, value)) { if (false) { System.out .println("******************************************************************"); System.out.println("Got drawable resource: type=" + value.type + " str=" + value.string + " int=0x" + Integer.toHexString(value.data) + " cookie=" + value.assetCookie); System.out .println("******************************************************************"); } return mResources.loadDrawable(value, value.resourceId); } return null; } public CharSequence[] getTextArray(int index) { final TypedValue value = mValue; if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { if (false) { System.out.println("******************************************************************"); System.out.println("Got drawable resource: type=" + value.type + " str=" + value.string + " int=0x" + Integer.toHexString(value.data) + " cookie=" + value.assetCookie); System.out.println("******************************************************************"); } return mResources.getTextArray(value.resourceId); } return null; } /** * 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 android.content.res.ColorStateList} description. * * @param index Index of attribute to retrieve. * * @return ColorStateList for the attribute, or null if not defined. */ public ColorStateList getColorStateList(int index) { final TypedValue value = mValue; if (getValueAt(index * AssetManager.STYLE_NUM_ENTRIES, value)) { return mResources.loadColorStateList(value, value.resourceId); } return null; } /** * Retrieve the integer value for the attribute at <var>index</var>. * * @param index Index of attribute to retrieve. * @param defValue Value to return if the attribute is not defined or * not a resource. * * @return Attribute integer value, or defValue if not defined. */ public int getInteger(int index, int defValue) { index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { return data[index+AssetManager.STYLE_DATA]; } throw new UnsupportedOperationException("Can't convert to integer: type=0x" + Integer.toHexString(type)); } /** * Retrieve the color value for the attribute at <var>index</var>. If * the attribute references a color resource holding a complex * {@link android.content.res.ColorStateList}, then the default color from * the set is returned. * * @param index Index of attribute to retrieve. * @param defValue Value to return if the attribute is not defined or * not a resource. * * @return Attribute color value, or defValue if not defined. */ public int getColor(int index, int defValue) { index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index + AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { return data[index + AssetManager.STYLE_DATA]; } else if (type == TypedValue.TYPE_STRING) { final TypedValue value = mValue; if (getValueAt(index, value)) { ColorStateList csl = mResources.loadColorStateList(value, value.resourceId); return csl.getDefaultColor(); } return defValue; } throw new UnsupportedOperationException( "Can't convert to color: type=0x" + Integer.toHexString(type)); } public String getPositionDescription() { return mXml != null ? mXml.getPositionDescription() : "<internal>"; } public Resources getResources() { return mResources; } }