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