TypedArray.java revision 8e5e11b99fac942122ee2d6cdd30af51564861ae
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        }
404
405        throw new UnsupportedOperationException("Can't convert to color: type=0x"
406                + Integer.toHexString(type));
407    }
408
409    /**
410     * Retrieve the ColorStateList for the attribute at <var>index</var>.
411     * The value may be either a single solid color or a reference to
412     * a color or complex {@link android.content.res.ColorStateList} description.
413     *
414     * @param index Index of attribute to retrieve.
415     *
416     * @return ColorStateList for the attribute, or null if not defined.
417     */
418    public ColorStateList getColorStateList(int index) {
419        if (mRecycled) {
420            throw new RuntimeException("Cannot make calls to a recycled instance!");
421        }
422
423        final TypedValue value = mValue;
424        if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
425            return mResources.loadColorStateList(value, value.resourceId);
426        }
427        return null;
428    }
429
430    /**
431     * Retrieve the integer value for the attribute at <var>index</var>.
432     *
433     * @param index Index of attribute to retrieve.
434     * @param defValue Value to return if the attribute is not defined or
435     *                 not a resource.
436     *
437     * @return Attribute integer value, or defValue if not defined.
438     */
439    public int getInteger(int index, int defValue) {
440        if (mRecycled) {
441            throw new RuntimeException("Cannot make calls to a recycled instance!");
442        }
443
444        index *= AssetManager.STYLE_NUM_ENTRIES;
445        final int[] data = mData;
446        final int type = data[index+AssetManager.STYLE_TYPE];
447        if (type == TypedValue.TYPE_NULL) {
448            return defValue;
449        } else if (type >= TypedValue.TYPE_FIRST_INT
450            && type <= TypedValue.TYPE_LAST_INT) {
451            return data[index+AssetManager.STYLE_DATA];
452        }
453
454        throw new UnsupportedOperationException("Can't convert to integer: type=0x"
455                + Integer.toHexString(type));
456    }
457
458    /**
459     * Retrieve a dimensional unit attribute at <var>index</var>.  Unit
460     * conversions are based on the current {@link DisplayMetrics}
461     * associated with the resources this {@link TypedArray} object
462     * came from.
463     *
464     * @param index Index of attribute to retrieve.
465     * @param defValue Value to return if the attribute is not defined or
466     *                 not a resource.
467     *
468     * @return Attribute dimension value multiplied by the appropriate
469     * metric, or defValue if not defined.
470     *
471     * @see #getDimensionPixelOffset
472     * @see #getDimensionPixelSize
473     */
474    public float getDimension(int index, float defValue) {
475        if (mRecycled) {
476            throw new RuntimeException("Cannot make calls to a recycled instance!");
477        }
478
479        index *= AssetManager.STYLE_NUM_ENTRIES;
480        final int[] data = mData;
481        final int type = data[index+AssetManager.STYLE_TYPE];
482        if (type == TypedValue.TYPE_NULL) {
483            return defValue;
484        } else if (type == TypedValue.TYPE_DIMENSION) {
485            return TypedValue.complexToDimension(
486                data[index+AssetManager.STYLE_DATA], mMetrics);
487        }
488
489        throw new UnsupportedOperationException("Can't convert to dimension: type=0x"
490                + Integer.toHexString(type));
491    }
492
493    /**
494     * Retrieve a dimensional unit attribute at <var>index</var> for use
495     * as an offset in raw pixels.  This is the same as
496     * {@link #getDimension}, except the returned value is converted to
497     * integer pixels for you.  An offset conversion involves simply
498     * truncating the base value to an integer.
499     *
500     * @param index Index of attribute to retrieve.
501     * @param defValue Value to return if the attribute is not defined or
502     *                 not a resource.
503     *
504     * @return Attribute dimension value multiplied by the appropriate
505     * metric and truncated to integer pixels, or defValue if not defined.
506     *
507     * @see #getDimension
508     * @see #getDimensionPixelSize
509     */
510    public int getDimensionPixelOffset(int index, int defValue) {
511        if (mRecycled) {
512            throw new RuntimeException("Cannot make calls to a recycled instance!");
513        }
514
515        index *= AssetManager.STYLE_NUM_ENTRIES;
516        final int[] data = mData;
517        final int type = data[index+AssetManager.STYLE_TYPE];
518        if (type == TypedValue.TYPE_NULL) {
519            return defValue;
520        } else if (type == TypedValue.TYPE_DIMENSION) {
521            return TypedValue.complexToDimensionPixelOffset(
522                data[index+AssetManager.STYLE_DATA], mMetrics);
523        }
524
525        throw new UnsupportedOperationException("Can't convert to dimension: type=0x"
526                + Integer.toHexString(type));
527    }
528
529    /**
530     * Retrieve a dimensional unit attribute at <var>index</var> for use
531     * as a size in raw pixels.  This is the same as
532     * {@link #getDimension}, except the returned value is converted to
533     * integer pixels for use as a size.  A size conversion involves
534     * rounding the base value, and ensuring that a non-zero base value
535     * is at least one pixel in size.
536     *
537     * @param index Index of attribute to retrieve.
538     * @param defValue Value to return if the attribute is not defined or
539     *                 not a resource.
540     *
541     * @return Attribute dimension value multiplied by the appropriate
542     * metric and truncated to integer pixels, or defValue if not defined.
543     *
544     * @see #getDimension
545     * @see #getDimensionPixelOffset
546     */
547    public int getDimensionPixelSize(int index, int defValue) {
548        if (mRecycled) {
549            throw new RuntimeException("Cannot make calls to a recycled instance!");
550        }
551
552        index *= AssetManager.STYLE_NUM_ENTRIES;
553        final int[] data = mData;
554        final int type = data[index+AssetManager.STYLE_TYPE];
555        if (type == TypedValue.TYPE_NULL) {
556            return defValue;
557        } else if (type == TypedValue.TYPE_DIMENSION) {
558            return TypedValue.complexToDimensionPixelSize(
559                data[index+AssetManager.STYLE_DATA], mMetrics);
560        }
561
562        throw new UnsupportedOperationException("Can't convert to dimension: type=0x"
563                + Integer.toHexString(type));
564    }
565
566    /**
567     * Special version of {@link #getDimensionPixelSize} for retrieving
568     * {@link android.view.ViewGroup}'s layout_width and layout_height
569     * attributes.  This is only here for performance reasons; applications
570     * should use {@link #getDimensionPixelSize}.
571     *
572     * @param index Index of the attribute to retrieve.
573     * @param name Textual name of attribute for error reporting.
574     *
575     * @return Attribute dimension value multiplied by the appropriate
576     * metric and truncated to integer pixels.
577     */
578    public int getLayoutDimension(int index, String name) {
579        if (mRecycled) {
580            throw new RuntimeException("Cannot make calls to a recycled instance!");
581        }
582
583        index *= AssetManager.STYLE_NUM_ENTRIES;
584        final int[] data = mData;
585        final int type = data[index+AssetManager.STYLE_TYPE];
586        if (type >= TypedValue.TYPE_FIRST_INT
587                && type <= TypedValue.TYPE_LAST_INT) {
588            return data[index+AssetManager.STYLE_DATA];
589        } else if (type == TypedValue.TYPE_DIMENSION) {
590            return TypedValue.complexToDimensionPixelSize(
591                data[index+AssetManager.STYLE_DATA], mMetrics);
592        }
593
594        throw new RuntimeException(getPositionDescription()
595                + ": You must supply a " + name + " attribute.");
596    }
597
598    /**
599     * Special version of {@link #getDimensionPixelSize} for retrieving
600     * {@link android.view.ViewGroup}'s layout_width and layout_height
601     * attributes.  This is only here for performance reasons; applications
602     * should use {@link #getDimensionPixelSize}.
603     *
604     * @param index Index of the attribute to retrieve.
605     * @param defValue The default value to return if this attribute is not
606     * default or contains the wrong type of data.
607     *
608     * @return Attribute dimension value multiplied by the appropriate
609     * metric and truncated to integer pixels.
610     */
611    public int getLayoutDimension(int index, int defValue) {
612        if (mRecycled) {
613            throw new RuntimeException("Cannot make calls to a recycled instance!");
614        }
615
616        index *= AssetManager.STYLE_NUM_ENTRIES;
617        final int[] data = mData;
618        final int type = data[index+AssetManager.STYLE_TYPE];
619        if (type >= TypedValue.TYPE_FIRST_INT
620                && type <= TypedValue.TYPE_LAST_INT) {
621            return data[index+AssetManager.STYLE_DATA];
622        } else if (type == TypedValue.TYPE_DIMENSION) {
623            return TypedValue.complexToDimensionPixelSize(
624                data[index+AssetManager.STYLE_DATA], mMetrics);
625        }
626
627        return defValue;
628    }
629
630    /**
631     * Retrieve a fractional unit attribute at <var>index</var>.
632     *
633     * @param index Index of attribute to retrieve.
634     * @param base The base value of this fraction.  In other words, a
635     *             standard fraction is multiplied by this value.
636     * @param pbase The parent base value of this fraction.  In other
637     *             words, a parent fraction (nn%p) is multiplied by this
638     *             value.
639     * @param defValue Value to return if the attribute is not defined or
640     *                 not a resource.
641     *
642     * @return Attribute fractional value multiplied by the appropriate
643     * base value, or defValue if not defined.
644     */
645    public float getFraction(int index, int base, int pbase, float defValue) {
646        if (mRecycled) {
647            throw new RuntimeException("Cannot make calls to a recycled instance!");
648        }
649
650        index *= AssetManager.STYLE_NUM_ENTRIES;
651        final int[] data = mData;
652        final int type = data[index+AssetManager.STYLE_TYPE];
653        if (type == TypedValue.TYPE_NULL) {
654            return defValue;
655        } else if (type == TypedValue.TYPE_FRACTION) {
656            return TypedValue.complexToFraction(
657                data[index+AssetManager.STYLE_DATA], base, pbase);
658        }
659
660        throw new UnsupportedOperationException("Can't convert to fraction: type=0x"
661                + Integer.toHexString(type));
662    }
663
664    /**
665     * Retrieve the resource identifier for the attribute at
666     * <var>index</var>.  Note that attribute resource as resolved when
667     * the overall {@link TypedArray} object is retrieved.  As a
668     * result, this function will return the resource identifier of the
669     * final resource value that was found, <em>not</em> necessarily the
670     * original resource that was specified by the attribute.
671     *
672     * @param index Index of attribute to retrieve.
673     * @param defValue Value to return if the attribute is not defined or
674     *                 not a resource.
675     *
676     * @return Attribute resource identifier, or defValue if not defined.
677     */
678    public int getResourceId(int index, int defValue) {
679        if (mRecycled) {
680            throw new RuntimeException("Cannot make calls to a recycled instance!");
681        }
682
683        index *= AssetManager.STYLE_NUM_ENTRIES;
684        final int[] data = mData;
685        if (data[index+AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) {
686            final int resid = data[index+AssetManager.STYLE_RESOURCE_ID];
687            if (resid != 0) {
688                return resid;
689            }
690        }
691        return defValue;
692    }
693
694    /**
695     * Retrieve the theme attribute resource identifier for the attribute at
696     * <var>index</var>.
697     *
698     * @param index Index of attribute to retrieve.
699     * @param defValue Value to return if the attribute is not defined or not a
700     *            resource.
701     * @return Theme attribute resource identifier, or defValue if not defined.
702     * @hide
703     */
704    public int getThemeAttributeId(int index, int defValue) {
705        if (mRecycled) {
706            throw new RuntimeException("Cannot make calls to a recycled instance!");
707        }
708
709        index *= AssetManager.STYLE_NUM_ENTRIES;
710        final int[] data = mData;
711        if (data[index + AssetManager.STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) {
712            return data[index + AssetManager.STYLE_DATA];
713        }
714        return defValue;
715    }
716
717    /**
718     * Retrieve the Drawable for the attribute at <var>index</var>.  This
719     * gets the resource ID of the selected attribute, and uses
720     * {@link Resources#getDrawable Resources.getDrawable} of the owning
721     * Resources object to retrieve its Drawable.
722     *
723     * @param index Index of attribute to retrieve.
724     *
725     * @return Drawable for the attribute, or null if not defined.
726     */
727    public Drawable getDrawable(int index) {
728        if (mRecycled) {
729            throw new RuntimeException("Cannot make calls to a recycled instance!");
730        }
731
732        final TypedValue value = mValue;
733        if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
734            if (false) {
735                System.out.println("******************************************************************");
736                System.out.println("Got drawable resource: type="
737                                   + value.type
738                                   + " str=" + value.string
739                                   + " int=0x" + Integer.toHexString(value.data)
740                                   + " cookie=" + value.assetCookie);
741                System.out.println("******************************************************************");
742            }
743            return mResources.loadDrawable(value, value.resourceId, mTheme);
744        }
745        return null;
746    }
747
748    /**
749     * Retrieve the CharSequence[] for the attribute at <var>index</var>.
750     * This gets the resource ID of the selected attribute, and uses
751     * {@link Resources#getTextArray Resources.getTextArray} of the owning
752     * Resources object to retrieve its String[].
753     *
754     * @param index Index of attribute to retrieve.
755     *
756     * @return CharSequence[] for the attribute, or null if not defined.
757     */
758    public CharSequence[] getTextArray(int index) {
759        if (mRecycled) {
760            throw new RuntimeException("Cannot make calls to a recycled instance!");
761        }
762
763        final TypedValue value = mValue;
764        if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
765            if (false) {
766                System.out.println("******************************************************************");
767                System.out.println("Got drawable resource: type="
768                                   + value.type
769                                   + " str=" + value.string
770                                   + " int=0x" + Integer.toHexString(value.data)
771                                   + " cookie=" + value.assetCookie);
772                System.out.println("******************************************************************");
773            }
774            return mResources.getTextArray(value.resourceId);
775        }
776        return null;
777    }
778
779    /**
780     * Retrieve the raw TypedValue for the attribute at <var>index</var>.
781     *
782     * @param index Index of attribute to retrieve.
783     * @param outValue TypedValue object in which to place the attribute's
784     *                 data.
785     *
786     * @return Returns true if the value was retrieved, else false.
787     */
788    public boolean getValue(int index, TypedValue outValue) {
789        if (mRecycled) {
790            throw new RuntimeException("Cannot make calls to a recycled instance!");
791        }
792
793        return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue);
794    }
795
796    /**
797     * Returns the type of attribute at the specified index.
798     *
799     * @param index Index of attribute whose type to retrieve.
800     * @return Attribute type.
801     */
802    public int getType(int index) {
803        if (mRecycled) {
804            throw new RuntimeException("Cannot make calls to a recycled instance!");
805        }
806
807        index *= AssetManager.STYLE_NUM_ENTRIES;
808        return mData[index + AssetManager.STYLE_TYPE];
809    }
810
811    /**
812     * Determines whether there is an attribute at <var>index</var>.
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     * Retrieve the raw TypedValue for the attribute at <var>index</var>
831     * and return a temporary object holding its data.  This object is only
832     * valid until the next call on to {@link TypedArray}.
833     *
834     * @param index Index of attribute to retrieve.
835     *
836     * @return Returns a TypedValue object if the attribute is defined,
837     *         containing its data; otherwise returns null.  (You will not
838     *         receive a TypedValue whose type is TYPE_NULL.)
839     */
840    public TypedValue peekValue(int index) {
841        if (mRecycled) {
842            throw new RuntimeException("Cannot make calls to a recycled instance!");
843        }
844
845        final TypedValue value = mValue;
846        if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
847            return value;
848        }
849        return null;
850    }
851
852    /**
853     * Returns a message about the parser state suitable for printing error messages.
854     */
855    public String getPositionDescription() {
856        if (mRecycled) {
857            throw new RuntimeException("Cannot make calls to a recycled instance!");
858        }
859
860        return mXml != null ? mXml.getPositionDescription() : "<internal>";
861    }
862
863    /**
864     * Recycle the TypedArray, to be re-used by a later caller. After calling
865     * this function you must not ever touch the typed array again.
866     */
867    public void recycle() {
868        if (mRecycled) {
869            throw new RuntimeException(toString() + " recycled twice!");
870        }
871
872        mRecycled = true;
873        mResources = null;
874        mMetrics = null;
875        mAssets = null;
876
877        // These may have been set by the client.
878        mXml = null;
879        mTheme = null;
880
881        synchronized (mPool) {
882            mPool.release(this);
883        }
884    }
885
886    /**
887     * Extracts theme attributes from a typed array for later resolution using
888     * {@link android.content.res.Resources.Theme#resolveAttributes(int[], int[])}.
889     * Removes the entries from the typed array so that subsequent calls to typed
890     * getters will return the default value without crashing.
891     *
892     * @return an array of length {@link #getIndexCount()} populated with theme
893     *         attributes, or null if there are no theme attributes in the typed
894     *         array
895     * @hide
896     */
897    public int[] extractThemeAttrs() {
898        if (mRecycled) {
899            throw new RuntimeException("Cannot make calls to a recycled instance!");
900        }
901
902        int[] attrs = null;
903
904        final int[] data = mData;
905        final int N = length();
906        for (int i = 0; i < N; i++) {
907            final int index = i * AssetManager.STYLE_NUM_ENTRIES;
908            if (data[index + AssetManager.STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) {
909                continue;
910            }
911
912            // Null the entry so that we can safely call getZzz().
913            data[index + AssetManager.STYLE_TYPE] = TypedValue.TYPE_NULL;
914
915            final int attr = data[index + AssetManager.STYLE_DATA];
916            if (attr == 0) {
917                // This attribute is useless!
918                continue;
919            }
920
921            if (attrs == null) {
922                attrs = new int[N];
923            }
924            attrs[i] = attr;
925        }
926
927        return attrs;
928    }
929
930    /**
931     * Return a mask of the configuration parameters for which the values in
932     * this typed array may change.
933     *
934     * @return Returns a mask of the changing configuration parameters, as
935     *         defined by {@link android.content.pm.ActivityInfo}.
936     * @see android.content.pm.ActivityInfo
937     */
938    public int getChangingConfigurations() {
939        int changingConfig = 0;
940
941        final int[] data = mData;
942        final int N = length();
943        for (int i = 0; i < N; i++) {
944            final int index = i * AssetManager.STYLE_NUM_ENTRIES;
945            final int type = data[index + AssetManager.STYLE_TYPE];
946            if (type == TypedValue.TYPE_NULL) {
947                continue;
948            }
949            changingConfig |= data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS];
950        }
951        return changingConfig;
952    }
953
954    private boolean getValueAt(int index, TypedValue outValue) {
955        final int[] data = mData;
956        final int type = data[index+AssetManager.STYLE_TYPE];
957        if (type == TypedValue.TYPE_NULL) {
958            return false;
959        }
960        outValue.type = type;
961        outValue.data = data[index+AssetManager.STYLE_DATA];
962        outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
963        outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID];
964        outValue.changingConfigurations = data[index+AssetManager.STYLE_CHANGING_CONFIGURATIONS];
965        outValue.density = data[index+AssetManager.STYLE_DENSITY];
966        outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null;
967        return true;
968    }
969
970    private CharSequence loadStringValueAt(int index) {
971        final int[] data = mData;
972        final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
973        if (cookie < 0) {
974            if (mXml != null) {
975                return mXml.getPooledString(
976                    data[index+AssetManager.STYLE_DATA]);
977            }
978            return null;
979        }
980        return mAssets.getPooledStringForCookie(cookie, data[index+AssetManager.STYLE_DATA]);
981    }
982
983    /*package*/ TypedArray(Resources resources, int[] data, int[] indices, int len) {
984        mResources = resources;
985        mMetrics = mResources.getDisplayMetrics();
986        mAssets = mResources.getAssets();
987        mData = data;
988        mIndices = indices;
989        mLength = len;
990    }
991
992    @Override
993    public String toString() {
994        return Arrays.toString(mData);
995    }
996}
997