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