Home | History | Annotate | Download | only in res
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.content.res;
     18 
     19 import android.annotation.Nullable;
     20 import android.graphics.drawable.Drawable;
     21 import android.util.AttributeSet;
     22 import android.util.DisplayMetrics;
     23 import android.util.Log;
     24 import android.util.TypedValue;
     25 
     26 import com.android.internal.util.XmlUtils;
     27 
     28 import java.util.Arrays;
     29 
     30 /**
     31  * Container for an array of values that were retrieved with
     32  * {@link Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)}
     33  * or {@link Resources#obtainAttributes}.  Be
     34  * sure to call {@link #recycle} when done with them.
     35  *
     36  * The indices used to retrieve values from this structure correspond to
     37  * the positions of the attributes given to obtainStyledAttributes.
     38  */
     39 public class TypedArray {
     40 
     41     static TypedArray obtain(Resources res, int len) {
     42         final TypedArray attrs = res.mTypedArrayPool.acquire();
     43         if (attrs != null) {
     44             attrs.mLength = len;
     45             attrs.mRecycled = false;
     46 
     47             final int fullLen = len * AssetManager.STYLE_NUM_ENTRIES;
     48             if (attrs.mData.length >= fullLen) {
     49                 return attrs;
     50             }
     51 
     52             attrs.mData = new int[fullLen];
     53             attrs.mIndices = new int[1 + len];
     54             return attrs;
     55         }
     56 
     57         return new TypedArray(res,
     58                 new int[len*AssetManager.STYLE_NUM_ENTRIES],
     59                 new int[1+len], len);
     60     }
     61 
     62     private final Resources mResources;
     63     private final DisplayMetrics mMetrics;
     64     private final AssetManager mAssets;
     65 
     66     private boolean mRecycled;
     67 
     68     /*package*/ XmlBlock.Parser mXml;
     69     /*package*/ Resources.Theme mTheme;
     70     /*package*/ int[] mData;
     71     /*package*/ int[] mIndices;
     72     /*package*/ int mLength;
     73     /*package*/ TypedValue mValue = new TypedValue();
     74 
     75     /**
     76      * Return the number of values in this array.
     77      */
     78     public int length() {
     79         if (mRecycled) {
     80             throw new RuntimeException("Cannot make calls to a recycled instance!");
     81         }
     82 
     83         return mLength;
     84     }
     85 
     86     /**
     87      * Return the number of indices in the array that actually have data.
     88      */
     89     public int getIndexCount() {
     90         if (mRecycled) {
     91             throw new RuntimeException("Cannot make calls to a recycled instance!");
     92         }
     93 
     94         return mIndices[0];
     95     }
     96 
     97     /**
     98      * Return an index in the array that has data.
     99      *
    100      * @param at The index you would like to returned, ranging from 0 to
    101      * {@link #getIndexCount()}.
    102      *
    103      * @return The index at the given offset, which can be used with
    104      * {@link #getValue} and related APIs.
    105      */
    106     public int getIndex(int at) {
    107         if (mRecycled) {
    108             throw new RuntimeException("Cannot make calls to a recycled instance!");
    109         }
    110 
    111         return mIndices[1+at];
    112     }
    113 
    114     /**
    115      * Return the Resources object this array was loaded from.
    116      */
    117     public Resources getResources() {
    118         if (mRecycled) {
    119             throw new RuntimeException("Cannot make calls to a recycled instance!");
    120         }
    121 
    122         return mResources;
    123     }
    124 
    125     /**
    126      * Retrieve the styled string value for the attribute at <var>index</var>.
    127      *
    128      * @param index Index of attribute to retrieve.
    129      *
    130      * @return CharSequence holding string data.  May be styled.  Returns
    131      *         null if the attribute is not defined.
    132      */
    133     public CharSequence getText(int index) {
    134         if (mRecycled) {
    135             throw new RuntimeException("Cannot make calls to a recycled instance!");
    136         }
    137 
    138         index *= AssetManager.STYLE_NUM_ENTRIES;
    139         final int[] data = mData;
    140         final int type = data[index+AssetManager.STYLE_TYPE];
    141         if (type == TypedValue.TYPE_NULL) {
    142             return null;
    143         } else if (type == TypedValue.TYPE_STRING) {
    144             return loadStringValueAt(index);
    145         }
    146 
    147         TypedValue v = mValue;
    148         if (getValueAt(index, v)) {
    149             Log.w(Resources.TAG, "Converting to string: " + v);
    150             return v.coerceToString();
    151         }
    152         Log.w(Resources.TAG, "getString of bad type: 0x"
    153               + Integer.toHexString(type));
    154         return null;
    155     }
    156 
    157     /**
    158      * Retrieve the string value for the attribute at <var>index</var>.
    159      *
    160      * @param index Index of attribute to retrieve.
    161      *
    162      * @return String holding string data.  Any styling information is
    163      * removed.  Returns null if the attribute is not defined.
    164      */
    165     public String getString(int index) {
    166         if (mRecycled) {
    167             throw new RuntimeException("Cannot make calls to a recycled instance!");
    168         }
    169 
    170         index *= AssetManager.STYLE_NUM_ENTRIES;
    171         final int[] data = mData;
    172         final int type = data[index+AssetManager.STYLE_TYPE];
    173         if (type == TypedValue.TYPE_NULL) {
    174             return null;
    175         } else if (type == TypedValue.TYPE_STRING) {
    176             return loadStringValueAt(index).toString();
    177         }
    178 
    179         TypedValue v = mValue;
    180         if (getValueAt(index, v)) {
    181             Log.w(Resources.TAG, "Converting to string: " + v);
    182             CharSequence cs = v.coerceToString();
    183             return cs != null ? cs.toString() : null;
    184         }
    185         Log.w(Resources.TAG, "getString of bad type: 0x"
    186               + Integer.toHexString(type));
    187         return null;
    188     }
    189 
    190     /**
    191      * Retrieve the string value for the attribute at <var>index</var>, but
    192      * only if that string comes from an immediate value in an XML file.  That
    193      * is, this does not allow references to string resources, string
    194      * attributes, or conversions from other types.  As such, this method
    195      * will only return strings for TypedArray objects that come from
    196      * attributes in an XML file.
    197      *
    198      * @param index Index of attribute to retrieve.
    199      *
    200      * @return String holding string data.  Any styling information is
    201      * removed.  Returns null if the attribute is not defined or is not
    202      * an immediate string value.
    203      */
    204     public String getNonResourceString(int index) {
    205         if (mRecycled) {
    206             throw new RuntimeException("Cannot make calls to a recycled instance!");
    207         }
    208 
    209         index *= AssetManager.STYLE_NUM_ENTRIES;
    210         final int[] data = mData;
    211         final int type = data[index+AssetManager.STYLE_TYPE];
    212         if (type == TypedValue.TYPE_STRING) {
    213             final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
    214             if (cookie < 0) {
    215                 return mXml.getPooledString(
    216                     data[index+AssetManager.STYLE_DATA]).toString();
    217             }
    218         }
    219         return null;
    220     }
    221 
    222     /**
    223      * @hide
    224      * Retrieve the string value for the attribute at <var>index</var> that is
    225      * not allowed to change with the given configurations.
    226      *
    227      * @param index Index of attribute to retrieve.
    228      * @param allowedChangingConfigs Bit mask of configurations from
    229      * {@link Configuration}.NATIVE_CONFIG_* that are allowed to change.
    230      *
    231      * @return String holding string data.  Any styling information is
    232      * removed.  Returns null if the attribute is not defined.
    233      */
    234     public String getNonConfigurationString(int index, int allowedChangingConfigs) {
    235         if (mRecycled) {
    236             throw new RuntimeException("Cannot make calls to a recycled instance!");
    237         }
    238 
    239         index *= AssetManager.STYLE_NUM_ENTRIES;
    240         final int[] data = mData;
    241         final int type = data[index+AssetManager.STYLE_TYPE];
    242         if ((data[index+AssetManager.STYLE_CHANGING_CONFIGURATIONS]&~allowedChangingConfigs) != 0) {
    243             return null;
    244         }
    245         if (type == TypedValue.TYPE_NULL) {
    246             return null;
    247         } else if (type == TypedValue.TYPE_STRING) {
    248             return loadStringValueAt(index).toString();
    249         }
    250 
    251         TypedValue v = mValue;
    252         if (getValueAt(index, v)) {
    253             Log.w(Resources.TAG, "Converting to string: " + v);
    254             CharSequence cs = v.coerceToString();
    255             return cs != null ? cs.toString() : null;
    256         }
    257         Log.w(Resources.TAG, "getString of bad type: 0x"
    258               + Integer.toHexString(type));
    259         return null;
    260     }
    261 
    262     /**
    263      * Retrieve the boolean value for the attribute at <var>index</var>.
    264      *
    265      * @param index Index of attribute to retrieve.
    266      * @param defValue Value to return if the attribute is not defined.
    267      *
    268      * @return Attribute boolean value, or defValue if not defined.
    269      */
    270     public boolean getBoolean(int index, boolean defValue) {
    271         if (mRecycled) {
    272             throw new RuntimeException("Cannot make calls to a recycled instance!");
    273         }
    274 
    275         index *= AssetManager.STYLE_NUM_ENTRIES;
    276         final int[] data = mData;
    277         final int type = data[index+AssetManager.STYLE_TYPE];
    278         if (type == TypedValue.TYPE_NULL) {
    279             return defValue;
    280         } else if (type >= TypedValue.TYPE_FIRST_INT
    281             && type <= TypedValue.TYPE_LAST_INT) {
    282             return data[index+AssetManager.STYLE_DATA] != 0;
    283         }
    284 
    285         TypedValue v = mValue;
    286         if (getValueAt(index, v)) {
    287             Log.w(Resources.TAG, "Converting to boolean: " + v);
    288             return XmlUtils.convertValueToBoolean(
    289                 v.coerceToString(), defValue);
    290         }
    291         Log.w(Resources.TAG, "getBoolean of bad type: 0x"
    292               + Integer.toHexString(type));
    293         return defValue;
    294     }
    295 
    296     /**
    297      * Retrieve the integer value for the attribute at <var>index</var>.
    298      *
    299      * @param index Index of attribute to retrieve.
    300      * @param defValue Value to return if the attribute is not defined.
    301      *
    302      * @return Attribute int value, or defValue if not defined.
    303      */
    304     public int getInt(int index, int defValue) {
    305         if (mRecycled) {
    306             throw new RuntimeException("Cannot make calls to a recycled instance!");
    307         }
    308 
    309         index *= AssetManager.STYLE_NUM_ENTRIES;
    310         final int[] data = mData;
    311         final int type = data[index+AssetManager.STYLE_TYPE];
    312         if (type == TypedValue.TYPE_NULL) {
    313             return defValue;
    314         } else if (type >= TypedValue.TYPE_FIRST_INT
    315             && type <= TypedValue.TYPE_LAST_INT) {
    316             return data[index+AssetManager.STYLE_DATA];
    317         }
    318 
    319         TypedValue v = mValue;
    320         if (getValueAt(index, v)) {
    321             Log.w(Resources.TAG, "Converting to int: " + v);
    322             return XmlUtils.convertValueToInt(
    323                 v.coerceToString(), defValue);
    324         }
    325         Log.w(Resources.TAG, "getInt of bad type: 0x"
    326               + Integer.toHexString(type));
    327         return defValue;
    328     }
    329 
    330     /**
    331      * Retrieve the float value for the attribute at <var>index</var>.
    332      *
    333      * @param index Index of attribute to retrieve.
    334      *
    335      * @return Attribute float value, or defValue if not defined..
    336      */
    337     public float getFloat(int index, float defValue) {
    338         if (mRecycled) {
    339             throw new RuntimeException("Cannot make calls to a recycled instance!");
    340         }
    341 
    342         index *= AssetManager.STYLE_NUM_ENTRIES;
    343         final int[] data = mData;
    344         final int type = data[index+AssetManager.STYLE_TYPE];
    345         if (type == TypedValue.TYPE_NULL) {
    346             return defValue;
    347         } else if (type == TypedValue.TYPE_FLOAT) {
    348             return Float.intBitsToFloat(data[index+AssetManager.STYLE_DATA]);
    349         } else if (type >= TypedValue.TYPE_FIRST_INT
    350             && type <= TypedValue.TYPE_LAST_INT) {
    351             return data[index+AssetManager.STYLE_DATA];
    352         }
    353 
    354         TypedValue v = mValue;
    355         if (getValueAt(index, v)) {
    356             Log.w(Resources.TAG, "Converting to float: " + v);
    357             CharSequence str = v.coerceToString();
    358             if (str != null) {
    359                 return Float.parseFloat(str.toString());
    360             }
    361         }
    362         Log.w(Resources.TAG, "getFloat of bad type: 0x"
    363               + Integer.toHexString(type));
    364         return defValue;
    365     }
    366 
    367     /**
    368      * Retrieve the color value for the attribute at <var>index</var>.  If
    369      * the attribute references a color resource holding a complex
    370      * {@link android.content.res.ColorStateList}, then the default color from
    371      * the set is returned.
    372      *
    373      * @param index Index of attribute to retrieve.
    374      * @param defValue Value to return if the attribute is not defined or
    375      *                 not a resource.
    376      *
    377      * @return Attribute color value, or defValue if not defined.
    378      */
    379     public int getColor(int index, int defValue) {
    380         if (mRecycled) {
    381             throw new RuntimeException("Cannot make calls to a recycled instance!");
    382         }
    383 
    384         index *= AssetManager.STYLE_NUM_ENTRIES;
    385         final int[] data = mData;
    386         final int type = data[index+AssetManager.STYLE_TYPE];
    387         if (type == TypedValue.TYPE_NULL) {
    388             return defValue;
    389         } else if (type >= TypedValue.TYPE_FIRST_INT
    390             && type <= TypedValue.TYPE_LAST_INT) {
    391             return data[index+AssetManager.STYLE_DATA];
    392         } else if (type == TypedValue.TYPE_STRING) {
    393             final TypedValue value = mValue;
    394             if (getValueAt(index, value)) {
    395                 ColorStateList csl = mResources.loadColorStateList(
    396                         value, value.resourceId);
    397                 return csl.getDefaultColor();
    398             }
    399             return defValue;
    400         } else if (type == TypedValue.TYPE_ATTRIBUTE) {
    401             throw new RuntimeException("Failed to resolve attribute at index " + index);
    402         }
    403 
    404         throw new UnsupportedOperationException("Can't convert to color: type=0x"
    405                 + Integer.toHexString(type));
    406     }
    407 
    408     /**
    409      * Retrieve the ColorStateList for the attribute at <var>index</var>.
    410      * The value may be either a single solid color or a reference to
    411      * a color or complex {@link android.content.res.ColorStateList} description.
    412      *
    413      * @param index Index of attribute to retrieve.
    414      *
    415      * @return ColorStateList for the attribute, or null if not defined.
    416      */
    417     public ColorStateList getColorStateList(int index) {
    418         if (mRecycled) {
    419             throw new RuntimeException("Cannot make calls to a recycled instance!");
    420         }
    421 
    422         final TypedValue value = mValue;
    423         if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
    424             if (value.type == TypedValue.TYPE_ATTRIBUTE) {
    425                 throw new RuntimeException("Failed to resolve attribute at index " + index);
    426             }
    427             return mResources.loadColorStateList(value, value.resourceId);
    428         }
    429         return null;
    430     }
    431 
    432     /**
    433      * Retrieve the integer value for the attribute at <var>index</var>.
    434      *
    435      * @param index Index of attribute to retrieve.
    436      * @param defValue Value to return if the attribute is not defined or
    437      *                 not a resource.
    438      *
    439      * @return Attribute integer value, or defValue if not defined.
    440      */
    441     public int getInteger(int index, int defValue) {
    442         if (mRecycled) {
    443             throw new RuntimeException("Cannot make calls to a recycled instance!");
    444         }
    445 
    446         index *= AssetManager.STYLE_NUM_ENTRIES;
    447         final int[] data = mData;
    448         final int type = data[index+AssetManager.STYLE_TYPE];
    449         if (type == TypedValue.TYPE_NULL) {
    450             return defValue;
    451         } else if (type >= TypedValue.TYPE_FIRST_INT
    452             && type <= TypedValue.TYPE_LAST_INT) {
    453             return data[index+AssetManager.STYLE_DATA];
    454         } else if (type == TypedValue.TYPE_ATTRIBUTE) {
    455             throw new RuntimeException("Failed to resolve attribute at index " + index);
    456         }
    457 
    458         throw new UnsupportedOperationException("Can't convert to integer: type=0x"
    459                 + Integer.toHexString(type));
    460     }
    461 
    462     /**
    463      * Retrieve a dimensional unit attribute at <var>index</var>.  Unit
    464      * conversions are based on the current {@link DisplayMetrics}
    465      * associated with the resources this {@link TypedArray} object
    466      * came from.
    467      *
    468      * @param index Index of attribute to retrieve.
    469      * @param defValue Value to return if the attribute is not defined or
    470      *                 not a resource.
    471      *
    472      * @return Attribute dimension value multiplied by the appropriate
    473      * metric, or defValue if not defined.
    474      *
    475      * @see #getDimensionPixelOffset
    476      * @see #getDimensionPixelSize
    477      */
    478     public float getDimension(int index, float defValue) {
    479         if (mRecycled) {
    480             throw new RuntimeException("Cannot make calls to a recycled instance!");
    481         }
    482 
    483         index *= AssetManager.STYLE_NUM_ENTRIES;
    484         final int[] data = mData;
    485         final int type = data[index+AssetManager.STYLE_TYPE];
    486         if (type == TypedValue.TYPE_NULL) {
    487             return defValue;
    488         } else if (type == TypedValue.TYPE_DIMENSION) {
    489             return TypedValue.complexToDimension(
    490                 data[index+AssetManager.STYLE_DATA], mMetrics);
    491         } else if (type == TypedValue.TYPE_ATTRIBUTE) {
    492             throw new RuntimeException("Failed to resolve attribute at index " + index);
    493         }
    494 
    495         throw new UnsupportedOperationException("Can't convert to dimension: type=0x"
    496                 + Integer.toHexString(type));
    497     }
    498 
    499     /**
    500      * Retrieve a dimensional unit attribute at <var>index</var> for use
    501      * as an offset in raw pixels.  This is the same as
    502      * {@link #getDimension}, except the returned value is converted to
    503      * integer pixels for you.  An offset conversion involves simply
    504      * truncating the base value to an integer.
    505      *
    506      * @param index Index of attribute to retrieve.
    507      * @param defValue Value to return if the attribute is not defined or
    508      *                 not a resource.
    509      *
    510      * @return Attribute dimension value multiplied by the appropriate
    511      * metric and truncated to integer pixels, or defValue if not defined.
    512      *
    513      * @see #getDimension
    514      * @see #getDimensionPixelSize
    515      */
    516     public int getDimensionPixelOffset(int index, int defValue) {
    517         if (mRecycled) {
    518             throw new RuntimeException("Cannot make calls to a recycled instance!");
    519         }
    520 
    521         index *= AssetManager.STYLE_NUM_ENTRIES;
    522         final int[] data = mData;
    523         final int type = data[index+AssetManager.STYLE_TYPE];
    524         if (type == TypedValue.TYPE_NULL) {
    525             return defValue;
    526         } else if (type == TypedValue.TYPE_DIMENSION) {
    527             return TypedValue.complexToDimensionPixelOffset(
    528                 data[index+AssetManager.STYLE_DATA], mMetrics);
    529         } else if (type == TypedValue.TYPE_ATTRIBUTE) {
    530             throw new RuntimeException("Failed to resolve attribute at index " + index);
    531         }
    532 
    533         throw new UnsupportedOperationException("Can't convert to dimension: type=0x"
    534                 + Integer.toHexString(type));
    535     }
    536 
    537     /**
    538      * Retrieve a dimensional unit attribute at <var>index</var> for use
    539      * as a size in raw pixels.  This is the same as
    540      * {@link #getDimension}, except the returned value is converted to
    541      * integer pixels for use as a size.  A size conversion involves
    542      * rounding the base value, and ensuring that a non-zero base value
    543      * is at least one pixel in size.
    544      *
    545      * @param index Index of attribute to retrieve.
    546      * @param defValue Value to return if the attribute is not defined or
    547      *                 not a resource.
    548      *
    549      * @return Attribute dimension value multiplied by the appropriate
    550      * metric and truncated to integer pixels, or defValue if not defined.
    551      *
    552      * @see #getDimension
    553      * @see #getDimensionPixelOffset
    554      */
    555     public int getDimensionPixelSize(int index, int defValue) {
    556         if (mRecycled) {
    557             throw new RuntimeException("Cannot make calls to a recycled instance!");
    558         }
    559 
    560         index *= AssetManager.STYLE_NUM_ENTRIES;
    561         final int[] data = mData;
    562         final int type = data[index+AssetManager.STYLE_TYPE];
    563         if (type == TypedValue.TYPE_NULL) {
    564             return defValue;
    565         } else if (type == TypedValue.TYPE_DIMENSION) {
    566             return TypedValue.complexToDimensionPixelSize(
    567                 data[index+AssetManager.STYLE_DATA], mMetrics);
    568         } else if (type == TypedValue.TYPE_ATTRIBUTE) {
    569             throw new RuntimeException("Failed to resolve attribute at index " + index);
    570         }
    571 
    572         throw new UnsupportedOperationException("Can't convert to dimension: type=0x"
    573                 + Integer.toHexString(type));
    574     }
    575 
    576     /**
    577      * Special version of {@link #getDimensionPixelSize} for retrieving
    578      * {@link android.view.ViewGroup}'s layout_width and layout_height
    579      * attributes.  This is only here for performance reasons; applications
    580      * should use {@link #getDimensionPixelSize}.
    581      *
    582      * @param index Index of the attribute to retrieve.
    583      * @param name Textual name of attribute for error reporting.
    584      *
    585      * @return Attribute dimension value multiplied by the appropriate
    586      * metric and truncated to integer pixels.
    587      */
    588     public int getLayoutDimension(int index, String name) {
    589         if (mRecycled) {
    590             throw new RuntimeException("Cannot make calls to a recycled instance!");
    591         }
    592 
    593         index *= AssetManager.STYLE_NUM_ENTRIES;
    594         final int[] data = mData;
    595         final int type = data[index+AssetManager.STYLE_TYPE];
    596         if (type >= TypedValue.TYPE_FIRST_INT
    597                 && type <= TypedValue.TYPE_LAST_INT) {
    598             return data[index+AssetManager.STYLE_DATA];
    599         } else if (type == TypedValue.TYPE_DIMENSION) {
    600             return TypedValue.complexToDimensionPixelSize(
    601                 data[index+AssetManager.STYLE_DATA], mMetrics);
    602         } else if (type == TypedValue.TYPE_ATTRIBUTE) {
    603             throw new RuntimeException("Failed to resolve attribute at index " + index);
    604         }
    605 
    606         throw new RuntimeException(getPositionDescription()
    607                 + ": You must supply a " + name + " attribute.");
    608     }
    609 
    610     /**
    611      * Special version of {@link #getDimensionPixelSize} for retrieving
    612      * {@link android.view.ViewGroup}'s layout_width and layout_height
    613      * attributes.  This is only here for performance reasons; applications
    614      * should use {@link #getDimensionPixelSize}.
    615      *
    616      * @param index Index of the attribute to retrieve.
    617      * @param defValue The default value to return if this attribute is not
    618      * default or contains the wrong type of data.
    619      *
    620      * @return Attribute dimension value multiplied by the appropriate
    621      * metric and truncated to integer pixels.
    622      */
    623     public int getLayoutDimension(int index, int defValue) {
    624         if (mRecycled) {
    625             throw new RuntimeException("Cannot make calls to a recycled instance!");
    626         }
    627 
    628         index *= AssetManager.STYLE_NUM_ENTRIES;
    629         final int[] data = mData;
    630         final int type = data[index+AssetManager.STYLE_TYPE];
    631         if (type >= TypedValue.TYPE_FIRST_INT
    632                 && type <= TypedValue.TYPE_LAST_INT) {
    633             return data[index+AssetManager.STYLE_DATA];
    634         } else if (type == TypedValue.TYPE_DIMENSION) {
    635             return TypedValue.complexToDimensionPixelSize(
    636                 data[index+AssetManager.STYLE_DATA], mMetrics);
    637         }
    638 
    639         return defValue;
    640     }
    641 
    642     /**
    643      * Retrieve a fractional unit attribute at <var>index</var>.
    644      *
    645      * @param index Index of attribute to retrieve.
    646      * @param base The base value of this fraction.  In other words, a
    647      *             standard fraction is multiplied by this value.
    648      * @param pbase The parent base value of this fraction.  In other
    649      *             words, a parent fraction (nn%p) is multiplied by this
    650      *             value.
    651      * @param defValue Value to return if the attribute is not defined or
    652      *                 not a resource.
    653      *
    654      * @return Attribute fractional value multiplied by the appropriate
    655      * base value, or defValue if not defined.
    656      */
    657     public float getFraction(int index, int base, int pbase, float defValue) {
    658         if (mRecycled) {
    659             throw new RuntimeException("Cannot make calls to a recycled instance!");
    660         }
    661 
    662         index *= AssetManager.STYLE_NUM_ENTRIES;
    663         final int[] data = mData;
    664         final int type = data[index+AssetManager.STYLE_TYPE];
    665         if (type == TypedValue.TYPE_NULL) {
    666             return defValue;
    667         } else if (type == TypedValue.TYPE_FRACTION) {
    668             return TypedValue.complexToFraction(
    669                 data[index+AssetManager.STYLE_DATA], base, pbase);
    670         } else if (type == TypedValue.TYPE_ATTRIBUTE) {
    671             throw new RuntimeException("Failed to resolve attribute at index " + index);
    672         }
    673 
    674         throw new UnsupportedOperationException("Can't convert to fraction: type=0x"
    675                 + Integer.toHexString(type));
    676     }
    677 
    678     /**
    679      * Retrieve the resource identifier for the attribute at
    680      * <var>index</var>.  Note that attribute resource as resolved when
    681      * the overall {@link TypedArray} object is retrieved.  As a
    682      * result, this function will return the resource identifier of the
    683      * final resource value that was found, <em>not</em> necessarily the
    684      * original resource that was specified by the attribute.
    685      *
    686      * @param index Index of attribute to retrieve.
    687      * @param defValue Value to return if the attribute is not defined or
    688      *                 not a resource.
    689      *
    690      * @return Attribute resource identifier, or defValue if not defined.
    691      */
    692     public int getResourceId(int index, int defValue) {
    693         if (mRecycled) {
    694             throw new RuntimeException("Cannot make calls to a recycled instance!");
    695         }
    696 
    697         index *= AssetManager.STYLE_NUM_ENTRIES;
    698         final int[] data = mData;
    699         if (data[index+AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) {
    700             final int resid = data[index+AssetManager.STYLE_RESOURCE_ID];
    701             if (resid != 0) {
    702                 return resid;
    703             }
    704         }
    705         return defValue;
    706     }
    707 
    708     /**
    709      * Retrieve the theme attribute resource identifier for the attribute at
    710      * <var>index</var>.
    711      *
    712      * @param index Index of attribute to retrieve.
    713      * @param defValue Value to return if the attribute is not defined or not a
    714      *            resource.
    715      * @return Theme attribute resource identifier, or defValue if not defined.
    716      * @hide
    717      */
    718     public int getThemeAttributeId(int index, int defValue) {
    719         if (mRecycled) {
    720             throw new RuntimeException("Cannot make calls to a recycled instance!");
    721         }
    722 
    723         index *= AssetManager.STYLE_NUM_ENTRIES;
    724         final int[] data = mData;
    725         if (data[index + AssetManager.STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) {
    726             return data[index + AssetManager.STYLE_DATA];
    727         }
    728         return defValue;
    729     }
    730 
    731     /**
    732      * Retrieve the Drawable for the attribute at <var>index</var>.
    733      *
    734      * @param index Index of attribute to retrieve.
    735      *
    736      * @return Drawable for the attribute, or null if not defined.
    737      */
    738     @Nullable
    739     public Drawable getDrawable(int index) {
    740         if (mRecycled) {
    741             throw new RuntimeException("Cannot make calls to a recycled instance!");
    742         }
    743 
    744         final TypedValue value = mValue;
    745         if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
    746             if (value.type == TypedValue.TYPE_ATTRIBUTE) {
    747                 throw new RuntimeException("Failed to resolve attribute at index " + index);
    748             }
    749             return mResources.loadDrawable(value, value.resourceId, mTheme);
    750         }
    751         return null;
    752     }
    753 
    754     /**
    755      * Retrieve the CharSequence[] for the attribute at <var>index</var>.
    756      * This gets the resource ID of the selected attribute, and uses
    757      * {@link Resources#getTextArray Resources.getTextArray} of the owning
    758      * Resources object to retrieve its String[].
    759      *
    760      * @param index Index of attribute to retrieve.
    761      *
    762      * @return CharSequence[] for the attribute, or null if not defined.
    763      */
    764     public CharSequence[] getTextArray(int index) {
    765         if (mRecycled) {
    766             throw new RuntimeException("Cannot make calls to a recycled instance!");
    767         }
    768 
    769         final TypedValue value = mValue;
    770         if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
    771             return mResources.getTextArray(value.resourceId);
    772         }
    773         return null;
    774     }
    775 
    776     /**
    777      * Retrieve the raw TypedValue for the attribute at <var>index</var>.
    778      *
    779      * @param index Index of attribute to retrieve.
    780      * @param outValue TypedValue object in which to place the attribute's
    781      *                 data.
    782      *
    783      * @return Returns true if the value was retrieved, else false.
    784      */
    785     public boolean getValue(int index, TypedValue outValue) {
    786         if (mRecycled) {
    787             throw new RuntimeException("Cannot make calls to a recycled instance!");
    788         }
    789 
    790         return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue);
    791     }
    792 
    793     /**
    794      * Returns the type of attribute at the specified index.
    795      *
    796      * @param index Index of attribute whose type to retrieve.
    797      * @return Attribute type.
    798      */
    799     public int getType(int index) {
    800         if (mRecycled) {
    801             throw new RuntimeException("Cannot make calls to a recycled instance!");
    802         }
    803 
    804         index *= AssetManager.STYLE_NUM_ENTRIES;
    805         return mData[index + AssetManager.STYLE_TYPE];
    806     }
    807 
    808     /**
    809      * Determines whether there is an attribute at <var>index</var>.
    810      * <p>
    811      * <strong>Note:</strong> If the attribute was set to {@code @empty} or
    812      * {@code @undefined}, this method returns {@code false}.
    813      *
    814      * @param index Index of attribute to retrieve.
    815      *
    816      * @return True if the attribute has a value, false otherwise.
    817      */
    818     public boolean hasValue(int index) {
    819         if (mRecycled) {
    820             throw new RuntimeException("Cannot make calls to a recycled instance!");
    821         }
    822 
    823         index *= AssetManager.STYLE_NUM_ENTRIES;
    824         final int[] data = mData;
    825         final int type = data[index+AssetManager.STYLE_TYPE];
    826         return type != TypedValue.TYPE_NULL;
    827     }
    828 
    829     /**
    830      * Determines whether there is an attribute at <var>index</var>, returning
    831      * {@code true} if the attribute was explicitly set to {@code @empty} and
    832      * {@code false} only if the attribute was undefined.
    833      *
    834      * @param index Index of attribute to retrieve.
    835      *
    836      * @return True if the attribute has a value or is empty, false otherwise.
    837      */
    838     public boolean hasValueOrEmpty(int index) {
    839         if (mRecycled) {
    840             throw new RuntimeException("Cannot make calls to a recycled instance!");
    841         }
    842 
    843         index *= AssetManager.STYLE_NUM_ENTRIES;
    844         final int[] data = mData;
    845         final int type = data[index+AssetManager.STYLE_TYPE];
    846         return type != TypedValue.TYPE_NULL
    847                 || data[index+AssetManager.STYLE_DATA] == TypedValue.DATA_NULL_EMPTY;
    848     }
    849 
    850     /**
    851      * Retrieve the raw TypedValue for the attribute at <var>index</var>
    852      * and return a temporary object holding its data.  This object is only
    853      * valid until the next call on to {@link TypedArray}.
    854      *
    855      * @param index Index of attribute to retrieve.
    856      *
    857      * @return Returns a TypedValue object if the attribute is defined,
    858      *         containing its data; otherwise returns null.  (You will not
    859      *         receive a TypedValue whose type is TYPE_NULL.)
    860      */
    861     public TypedValue peekValue(int index) {
    862         if (mRecycled) {
    863             throw new RuntimeException("Cannot make calls to a recycled instance!");
    864         }
    865 
    866         final TypedValue value = mValue;
    867         if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
    868             return value;
    869         }
    870         return null;
    871     }
    872 
    873     /**
    874      * Returns a message about the parser state suitable for printing error messages.
    875      */
    876     public String getPositionDescription() {
    877         if (mRecycled) {
    878             throw new RuntimeException("Cannot make calls to a recycled instance!");
    879         }
    880 
    881         return mXml != null ? mXml.getPositionDescription() : "<internal>";
    882     }
    883 
    884     /**
    885      * Recycle the TypedArray, to be re-used by a later caller. After calling
    886      * this function you must not ever touch the typed array again.
    887      */
    888     public void recycle() {
    889         if (mRecycled) {
    890             throw new RuntimeException(toString() + " recycled twice!");
    891         }
    892 
    893         mRecycled = true;
    894 
    895         // These may have been set by the client.
    896         mXml = null;
    897         mTheme = null;
    898 
    899         mResources.mTypedArrayPool.release(this);
    900     }
    901 
    902     /**
    903      * Extracts theme attributes from a typed array for later resolution using
    904      * {@link android.content.res.Resources.Theme#resolveAttributes(int[], int[])}.
    905      * Removes the entries from the typed array so that subsequent calls to typed
    906      * getters will return the default value without crashing.
    907      *
    908      * @return an array of length {@link #getIndexCount()} populated with theme
    909      *         attributes, or null if there are no theme attributes in the typed
    910      *         array
    911      * @hide
    912      */
    913     public int[] extractThemeAttrs() {
    914         if (mRecycled) {
    915             throw new RuntimeException("Cannot make calls to a recycled instance!");
    916         }
    917 
    918         int[] attrs = null;
    919 
    920         final int[] data = mData;
    921         final int N = length();
    922         for (int i = 0; i < N; i++) {
    923             final int index = i * AssetManager.STYLE_NUM_ENTRIES;
    924             if (data[index + AssetManager.STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) {
    925                 continue;
    926             }
    927 
    928             // Null the entry so that we can safely call getZzz().
    929             data[index + AssetManager.STYLE_TYPE] = TypedValue.TYPE_NULL;
    930 
    931             final int attr = data[index + AssetManager.STYLE_DATA];
    932             if (attr == 0) {
    933                 // This attribute is useless!
    934                 continue;
    935             }
    936 
    937             if (attrs == null) {
    938                 attrs = new int[N];
    939             }
    940             attrs[i] = attr;
    941         }
    942 
    943         return attrs;
    944     }
    945 
    946     /**
    947      * Return a mask of the configuration parameters for which the values in
    948      * this typed array may change.
    949      *
    950      * @return Returns a mask of the changing configuration parameters, as
    951      *         defined by {@link android.content.pm.ActivityInfo}.
    952      * @see android.content.pm.ActivityInfo
    953      */
    954     public int getChangingConfigurations() {
    955         int changingConfig = 0;
    956 
    957         final int[] data = mData;
    958         final int N = length();
    959         for (int i = 0; i < N; i++) {
    960             final int index = i * AssetManager.STYLE_NUM_ENTRIES;
    961             final int type = data[index + AssetManager.STYLE_TYPE];
    962             if (type == TypedValue.TYPE_NULL) {
    963                 continue;
    964             }
    965             changingConfig |= data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS];
    966         }
    967         return changingConfig;
    968     }
    969 
    970     private boolean getValueAt(int index, TypedValue outValue) {
    971         final int[] data = mData;
    972         final int type = data[index+AssetManager.STYLE_TYPE];
    973         if (type == TypedValue.TYPE_NULL) {
    974             return false;
    975         }
    976         outValue.type = type;
    977         outValue.data = data[index+AssetManager.STYLE_DATA];
    978         outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
    979         outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID];
    980         outValue.changingConfigurations = data[index+AssetManager.STYLE_CHANGING_CONFIGURATIONS];
    981         outValue.density = data[index+AssetManager.STYLE_DENSITY];
    982         outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null;
    983         return true;
    984     }
    985 
    986     private CharSequence loadStringValueAt(int index) {
    987         final int[] data = mData;
    988         final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
    989         if (cookie < 0) {
    990             if (mXml != null) {
    991                 return mXml.getPooledString(
    992                     data[index+AssetManager.STYLE_DATA]);
    993             }
    994             return null;
    995         }
    996         return mAssets.getPooledStringForCookie(cookie, data[index+AssetManager.STYLE_DATA]);
    997     }
    998 
    999     /*package*/ TypedArray(Resources resources, int[] data, int[] indices, int len) {
   1000         mResources = resources;
   1001         mMetrics = mResources.mMetrics;
   1002         mAssets = mResources.mAssets;
   1003         mData = data;
   1004         mIndices = indices;
   1005         mLength = len;
   1006     }
   1007 
   1008     @Override
   1009     public String toString() {
   1010         return Arrays.toString(mData);
   1011     }
   1012 }
   1013