[go: nahoru, domu]

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