TypedArray.java revision f2969405020a72e282c348a6ea201d56e9f8d4ba
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.annotation.Nullable;
20import android.graphics.drawable.Drawable;
21import android.util.AttributeSet;
22import android.util.DisplayMetrics;
23import android.util.Log;
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
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