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                 String value = mXml.getPooledString(data[index + STYLE_DATA]).toString();
311                 if (value != null && mXml != null && mXml.mValidator != null) {
312                     mXml.mValidator.validateResStrAttr(mXml, index, value);
313                 }
314                 return value;
315             }
316         }
317         return null;
318     }
319 
320     /**
321      * Retrieves the string value for the attribute at <var>index</var> that is
322      * not allowed to change with the given configurations.
323      *
324      * @param index Index of attribute to retrieve.
325      * @param allowedChangingConfigs Bit mask of configurations from
326      *        {@link Configuration}.NATIVE_CONFIG_* that are allowed to change.
327      *
328      * @return String holding string data. Any styling information is removed.
329      *         Returns {@code null} if the attribute is not defined.
330      * @throws RuntimeException if the TypedArray has already been recycled.
331      * @hide
332      */
333     @UnsupportedAppUsage
getNonConfigurationString(@tyleableRes int index, @Config int allowedChangingConfigs)334     public String getNonConfigurationString(@StyleableRes int index,
335             @Config int allowedChangingConfigs) {
336         if (mRecycled) {
337             throw new RuntimeException("Cannot make calls to a recycled instance!");
338         }
339 
340         index *= STYLE_NUM_ENTRIES;
341         final int[] data = mData;
342         final int type = data[index + STYLE_TYPE];
343         final @Config int changingConfigs = ActivityInfo.activityInfoConfigNativeToJava(
344                 data[index + STYLE_CHANGING_CONFIGURATIONS]);
345         if ((changingConfigs & ~allowedChangingConfigs) != 0) {
346             return null;
347         }
348         if (type == TypedValue.TYPE_NULL) {
349             return null;
350         } else if (type == TypedValue.TYPE_STRING) {
351             return loadStringValueAt(index).toString();
352         }
353 
354         final TypedValue v = mValue;
355         if (getValueAt(index, v)) {
356             final CharSequence cs = v.coerceToString();
357             return cs != null ? cs.toString() : null;
358         }
359 
360         // We already checked for TYPE_NULL. This should never happen.
361         throw new RuntimeException("getNonConfigurationString of bad type: 0x"
362                 + Integer.toHexString(type));
363     }
364 
365     /**
366      * Retrieve the boolean value for the attribute at <var>index</var>.
367      * <p>
368      * If the attribute is an integer value, this method returns false if the
369      * attribute is equal to zero, and true otherwise.
370      * If the attribute is not a boolean or integer value,
371      * this method will attempt to coerce it to an integer using
372      * {@link Integer#decode(String)} and return whether it is equal to zero.
373      *
374      * @param index Index of attribute to retrieve.
375      * @param defValue Value to return if the attribute is not defined or
376      *                 cannot be coerced to an integer.
377      *
378      * @return Boolean value of the attribute, or defValue if the attribute was
379      *         not defined or could not be coerced to an integer.
380      * @throws RuntimeException if the TypedArray has already been recycled.
381      */
getBoolean(@tyleableRes int index, boolean defValue)382     public boolean getBoolean(@StyleableRes int index, boolean defValue) {
383         if (mRecycled) {
384             throw new RuntimeException("Cannot make calls to a recycled instance!");
385         }
386 
387         index *= STYLE_NUM_ENTRIES;
388         final int[] data = mData;
389         final int type = data[index + STYLE_TYPE];
390         if (type == TypedValue.TYPE_NULL) {
391             return defValue;
392         } else if (type >= TypedValue.TYPE_FIRST_INT
393                 && type <= TypedValue.TYPE_LAST_INT) {
394             return data[index + STYLE_DATA] != 0;
395         }
396 
397         final TypedValue v = mValue;
398         if (getValueAt(index, v)) {
399             StrictMode.noteResourceMismatch(v);
400             return XmlUtils.convertValueToBoolean(v.coerceToString(), defValue);
401         }
402 
403         // We already checked for TYPE_NULL. This should never happen.
404         throw new RuntimeException("getBoolean of bad type: 0x" + Integer.toHexString(type));
405     }
406 
407     /**
408      * Retrieve the integer value for the attribute at <var>index</var>.
409      * <p>
410      * If the attribute is not an integer, this method will attempt to coerce
411      * it to an integer using {@link Integer#decode(String)}.
412      *
413      * @param index Index of attribute to retrieve.
414      * @param defValue Value to return if the attribute is not defined or
415      *                 cannot be coerced to an integer.
416      *
417      * @return Integer value of the attribute, or defValue if the attribute was
418      *         not defined or could not be coerced to an integer.
419      * @throws RuntimeException if the TypedArray has already been recycled.
420      */
getInt(@tyleableRes int index, int defValue)421     public int getInt(@StyleableRes int index, int defValue) {
422         if (mRecycled) {
423             throw new RuntimeException("Cannot make calls to a recycled instance!");
424         }
425 
426         index *= STYLE_NUM_ENTRIES;
427         final int[] data = mData;
428         final int type = data[index + STYLE_TYPE];
429         if (type == TypedValue.TYPE_NULL) {
430             return defValue;
431         } else if (type >= TypedValue.TYPE_FIRST_INT
432                 && type <= TypedValue.TYPE_LAST_INT) {
433             return data[index + STYLE_DATA];
434         }
435 
436         final TypedValue v = mValue;
437         if (getValueAt(index, v)) {
438             StrictMode.noteResourceMismatch(v);
439             return XmlUtils.convertValueToInt(v.coerceToString(), defValue);
440         }
441 
442         // We already checked for TYPE_NULL. This should never happen.
443         throw new RuntimeException("getInt of bad type: 0x" + Integer.toHexString(type));
444     }
445 
446     /**
447      * Retrieve the float value for the attribute at <var>index</var>.
448      * <p>
449      * If the attribute is not a float or an integer, this method will attempt
450      * to coerce it to a float using {@link Float#parseFloat(String)}.
451      *
452      * @param index Index of attribute to retrieve.
453      *
454      * @return Attribute float value, or defValue if the attribute was
455      *         not defined or could not be coerced to a float.
456      * @throws RuntimeException if the TypedArray has already been recycled.
457      */
getFloat(@tyleableRes int index, float defValue)458     public float getFloat(@StyleableRes int index, float defValue) {
459         if (mRecycled) {
460             throw new RuntimeException("Cannot make calls to a recycled instance!");
461         }
462 
463         index *= STYLE_NUM_ENTRIES;
464         final int[] data = mData;
465         final int type = data[index + STYLE_TYPE];
466         if (type == TypedValue.TYPE_NULL) {
467             return defValue;
468         } else if (type == TypedValue.TYPE_FLOAT) {
469             return Float.intBitsToFloat(data[index + STYLE_DATA]);
470         } else if (type >= TypedValue.TYPE_FIRST_INT
471                 && type <= TypedValue.TYPE_LAST_INT) {
472             return data[index + STYLE_DATA];
473         }
474 
475         final TypedValue v = mValue;
476         if (getValueAt(index, v)) {
477             final CharSequence str = v.coerceToString();
478             if (str != null) {
479                 StrictMode.noteResourceMismatch(v);
480                 return Float.parseFloat(str.toString());
481             }
482         }
483 
484         // We already checked for TYPE_NULL. This should never happen.
485         throw new RuntimeException("getFloat of bad type: 0x" + Integer.toHexString(type));
486     }
487 
488     /**
489      * Retrieve the color value for the attribute at <var>index</var>.  If
490      * the attribute references a color resource holding a complex
491      * {@link android.content.res.ColorStateList}, then the default color from
492      * the set is returned.
493      * <p>
494      * This method will throw an exception if the attribute is defined but is
495      * not an integer color or color state list.
496      *
497      * @param index Index of attribute to retrieve.
498      * @param defValue Value to return if the attribute is not defined or
499      *                 not a resource.
500      *
501      * @return Attribute color value, or defValue if not defined.
502      * @throws RuntimeException if the TypedArray has already been recycled.
503      * @throws UnsupportedOperationException if the attribute is defined but is
504      *         not an integer color or color state list.
505      */
506     @ColorInt
getColor(@tyleableRes int index, @ColorInt int defValue)507     public int getColor(@StyleableRes int index, @ColorInt int defValue) {
508         if (mRecycled) {
509             throw new RuntimeException("Cannot make calls to a recycled instance!");
510         }
511 
512         final int attrIndex = index;
513         index *= STYLE_NUM_ENTRIES;
514 
515         final int[] data = mData;
516         final int type = data[index + STYLE_TYPE];
517         if (type == TypedValue.TYPE_NULL) {
518             return defValue;
519         } else if (type >= TypedValue.TYPE_FIRST_INT
520                 && type <= TypedValue.TYPE_LAST_INT) {
521             return data[index + STYLE_DATA];
522         } else if (type == TypedValue.TYPE_STRING) {
523             final TypedValue value = mValue;
524             if (getValueAt(index, value)) {
525                 final ColorStateList csl = mResources.loadColorStateList(
526                         value, value.resourceId, mTheme);
527                 return csl.getDefaultColor();
528             }
529             return defValue;
530         } else if (type == TypedValue.TYPE_ATTRIBUTE) {
531             final TypedValue value = mValue;
532             getValueAt(index, value);
533             throw new UnsupportedOperationException(
534                     "Failed to resolve attribute at index " + attrIndex + ": " + value
535                             + ", theme=" + mTheme);
536         }
537 
538         throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
539                 + " to color: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme);
540     }
541 
542     /**
543      * Retrieve the ComplexColor for the attribute at <var>index</var>.
544      * The value may be either a {@link android.content.res.ColorStateList} which can wrap a simple
545      * color value or a {@link android.content.res.GradientColor}
546      * <p>
547      * This method will return {@code null} if the attribute is not defined or
548      * is not an integer color, color state list or GradientColor.
549      *
550      * @param index Index of attribute to retrieve.
551      *
552      * @return ComplexColor for the attribute, or {@code null} if not defined.
553      * @throws RuntimeException if the attribute if the TypedArray has already
554      *         been recycled.
555      * @throws UnsupportedOperationException if the attribute is defined but is
556      *         not an integer color, color state list or GradientColor.
557      * @hide
558      */
559     @Nullable
getComplexColor(@tyleableRes int index)560     public ComplexColor getComplexColor(@StyleableRes int index) {
561         if (mRecycled) {
562             throw new RuntimeException("Cannot make calls to a recycled instance!");
563         }
564 
565         final TypedValue value = mValue;
566         if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
567             if (value.type == TypedValue.TYPE_ATTRIBUTE) {
568                 throw new UnsupportedOperationException(
569                         "Failed to resolve attribute at index " + index + ": " + value
570                                 + ", theme=" + mTheme);
571             }
572             return mResources.loadComplexColor(value, value.resourceId, mTheme);
573         }
574         return null;
575     }
576 
577     /**
578      * Retrieve the ColorStateList for the attribute at <var>index</var>.
579      * The value may be either a single solid color or a reference to
580      * a color or complex {@link android.content.res.ColorStateList}
581      * description.
582      * <p>
583      * This method will return {@code null} if the attribute is not defined or
584      * is not an integer color or color state list.
585      *
586      * @param index Index of attribute to retrieve.
587      *
588      * @return ColorStateList for the attribute, or {@code null} if not
589      *         defined.
590      * @throws RuntimeException if the attribute if the TypedArray has already
591      *         been recycled.
592      * @throws UnsupportedOperationException if the attribute is defined but is
593      *         not an integer color or color state list.
594      */
595     @Nullable
getColorStateList(@tyleableRes int index)596     public ColorStateList getColorStateList(@StyleableRes int index) {
597         if (mRecycled) {
598             throw new RuntimeException("Cannot make calls to a recycled instance!");
599         }
600 
601         final TypedValue value = mValue;
602         if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
603             if (value.type == TypedValue.TYPE_ATTRIBUTE) {
604                 throw new UnsupportedOperationException(
605                         "Failed to resolve attribute at index " + index + ": " + value
606                                 + ", theme=" + mTheme);
607             }
608             return mResources.loadColorStateList(value, value.resourceId, mTheme);
609         }
610         return null;
611     }
612 
613     /**
614      * Retrieve the integer value for the attribute at <var>index</var>.
615      * <p>
616      * Unlike {@link #getInt(int, int)}, this method will throw an exception if
617      * the attribute is defined but is not an integer.
618      *
619      * @param index Index of attribute to retrieve.
620      * @param defValue Value to return if the attribute is not defined or
621      *                 not a resource.
622      *
623      * @return Attribute integer value, or defValue if not defined.
624      * @throws RuntimeException if the TypedArray has already been recycled.
625      * @throws UnsupportedOperationException if the attribute is defined but is
626      *         not an integer.
627      */
getInteger(@tyleableRes int index, int defValue)628     public int getInteger(@StyleableRes int index, int defValue) {
629         if (mRecycled) {
630             throw new RuntimeException("Cannot make calls to a recycled instance!");
631         }
632 
633         final int attrIndex = index;
634         index *= STYLE_NUM_ENTRIES;
635 
636         final int[] data = mData;
637         final int type = data[index + STYLE_TYPE];
638         if (type == TypedValue.TYPE_NULL) {
639             return defValue;
640         } else if (type >= TypedValue.TYPE_FIRST_INT
641                 && type <= TypedValue.TYPE_LAST_INT) {
642             return data[index + STYLE_DATA];
643         } else if (type == TypedValue.TYPE_ATTRIBUTE) {
644             final TypedValue value = mValue;
645             getValueAt(index, value);
646             throw new UnsupportedOperationException(
647                     "Failed to resolve attribute at index " + attrIndex + ": " + value
648                             + ", theme=" + mTheme);
649         }
650 
651         throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
652                 + " to integer: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme);
653     }
654 
655     /**
656      * Retrieve a dimensional unit attribute at <var>index</var>. Unit
657      * conversions are based on the current {@link DisplayMetrics}
658      * associated with the resources this {@link TypedArray} object
659      * came from.
660      * <p>
661      * This method will throw an exception if the attribute is defined but is
662      * not a dimension.
663      *
664      * @param index Index of attribute to retrieve.
665      * @param defValue Value to return if the attribute is not defined or
666      *                 not a resource.
667      *
668      * @return Attribute dimension value multiplied by the appropriate
669      *         metric, or defValue if not defined.
670      * @throws RuntimeException if the TypedArray has already been recycled.
671      * @throws UnsupportedOperationException if the attribute is defined but is
672      *         not an integer.
673      *
674      * @see #getDimensionPixelOffset
675      * @see #getDimensionPixelSize
676      */
getDimension(@tyleableRes int index, float defValue)677     public float getDimension(@StyleableRes int index, float defValue) {
678         if (mRecycled) {
679             throw new RuntimeException("Cannot make calls to a recycled instance!");
680         }
681 
682         final int attrIndex = index;
683         index *= STYLE_NUM_ENTRIES;
684 
685         final int[] data = mData;
686         final int type = data[index + STYLE_TYPE];
687         if (type == TypedValue.TYPE_NULL) {
688             return defValue;
689         } else if (type == TypedValue.TYPE_DIMENSION) {
690             return TypedValue.complexToDimension(data[index + STYLE_DATA], mMetrics);
691         } else if (type == TypedValue.TYPE_ATTRIBUTE) {
692             final TypedValue value = mValue;
693             getValueAt(index, value);
694             throw new UnsupportedOperationException(
695                     "Failed to resolve attribute at index " + attrIndex + ": " + value
696                             + ", theme=" + mTheme);
697         }
698 
699         throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
700                 + " to dimension: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme);
701     }
702 
703     /**
704      * Retrieve a dimensional unit attribute at <var>index</var> for use
705      * as an offset in raw pixels.  This is the same as
706      * {@link #getDimension}, except the returned value is converted to
707      * integer pixels for you.  An offset conversion involves simply
708      * truncating the base value to an integer.
709      * <p>
710      * This method will throw an exception if the attribute is defined but is
711      * not a dimension.
712      *
713      * @param index Index of attribute to retrieve.
714      * @param defValue Value to return if the attribute is not defined or
715      *                 not a resource.
716      *
717      * @return Attribute dimension value multiplied by the appropriate
718      *         metric and truncated to integer pixels, or defValue if not defined.
719      * @throws RuntimeException if the TypedArray has already been recycled.
720      * @throws UnsupportedOperationException if the attribute is defined but is
721      *         not an integer.
722      *
723      * @see #getDimension
724      * @see #getDimensionPixelSize
725      */
getDimensionPixelOffset(@tyleableRes int index, int defValue)726     public int getDimensionPixelOffset(@StyleableRes int index, int defValue) {
727         if (mRecycled) {
728             throw new RuntimeException("Cannot make calls to a recycled instance!");
729         }
730 
731         final int attrIndex = index;
732         index *= STYLE_NUM_ENTRIES;
733 
734         final int[] data = mData;
735         final int type = data[index + STYLE_TYPE];
736         if (type == TypedValue.TYPE_NULL) {
737             return defValue;
738         } else if (type == TypedValue.TYPE_DIMENSION) {
739             return TypedValue.complexToDimensionPixelOffset(data[index + STYLE_DATA], mMetrics);
740         } else if (type == TypedValue.TYPE_ATTRIBUTE) {
741             final TypedValue value = mValue;
742             getValueAt(index, value);
743             throw new UnsupportedOperationException(
744                     "Failed to resolve attribute at index " + attrIndex + ": " + value
745                             + ", theme=" + mTheme);
746         }
747 
748         throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
749                 + " to dimension: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme);
750     }
751 
752     /**
753      * Retrieve a dimensional unit attribute at <var>index</var> for use
754      * as a size in raw pixels.  This is the same as
755      * {@link #getDimension}, except the returned value is converted to
756      * integer pixels for use as a size.  A size conversion involves
757      * rounding the base value, and ensuring that a non-zero base value
758      * is at least one pixel in size.
759      * <p>
760      * This method will throw an exception if the attribute is defined but is
761      * not a dimension.
762      *
763      * @param index Index of attribute to retrieve.
764      * @param defValue Value to return if the attribute is not defined or
765      *                 not a resource.
766      *
767      * @return Attribute dimension value multiplied by the appropriate
768      *         metric and truncated to integer pixels, or defValue if not defined.
769      * @throws RuntimeException if the TypedArray has already been recycled.
770      * @throws UnsupportedOperationException if the attribute is defined but is
771      *         not a dimension.
772      *
773      * @see #getDimension
774      * @see #getDimensionPixelOffset
775      */
getDimensionPixelSize(@tyleableRes int index, int defValue)776     public int getDimensionPixelSize(@StyleableRes int index, int defValue) {
777         if (mRecycled) {
778             throw new RuntimeException("Cannot make calls to a recycled instance!");
779         }
780 
781         final int attrIndex = index;
782         index *= STYLE_NUM_ENTRIES;
783 
784         final int[] data = mData;
785         final int type = data[index + STYLE_TYPE];
786         if (type == TypedValue.TYPE_NULL) {
787             return defValue;
788         } else if (type == TypedValue.TYPE_DIMENSION) {
789             return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics);
790         } else if (type == TypedValue.TYPE_ATTRIBUTE) {
791             final TypedValue value = mValue;
792             getValueAt(index, value);
793             throw new UnsupportedOperationException(
794                     "Failed to resolve attribute at index " + attrIndex + ": " + value
795                             + ", theme=" + mTheme);
796         }
797 
798         throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
799                 + " to dimension: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme);
800     }
801 
802     /**
803      * Special version of {@link #getDimensionPixelSize} for retrieving
804      * {@link android.view.ViewGroup}'s layout_width and layout_height
805      * attributes.  This is only here for performance reasons; applications
806      * should use {@link #getDimensionPixelSize}.
807      * <p>
808      * This method will throw an exception if the attribute is defined but is
809      * not a dimension or integer (enum).
810      *
811      * @param index Index of the attribute to retrieve.
812      * @param name Textual name of attribute for error reporting.
813      *
814      * @return Attribute dimension value multiplied by the appropriate
815      *         metric and truncated to integer pixels.
816      * @throws RuntimeException if the TypedArray has already been recycled.
817      * @throws UnsupportedOperationException if the attribute is defined but is
818      *         not a dimension or integer (enum).
819      */
getLayoutDimension(@tyleableRes int index, String name)820     public int getLayoutDimension(@StyleableRes int index, String name) {
821         if (mRecycled) {
822             throw new RuntimeException("Cannot make calls to a recycled instance!");
823         }
824 
825         final int attrIndex = index;
826         index *= STYLE_NUM_ENTRIES;
827 
828         final int[] data = mData;
829         final int type = data[index + STYLE_TYPE];
830         if (type >= TypedValue.TYPE_FIRST_INT
831                 && type <= TypedValue.TYPE_LAST_INT) {
832             return data[index + STYLE_DATA];
833         } else if (type == TypedValue.TYPE_DIMENSION) {
834             return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics);
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                             + ", theme=" + mTheme);
841         }
842 
843         throw new UnsupportedOperationException(getPositionDescription()
844                 + ": You must supply a " + name + " attribute." + ", theme=" + mTheme);
845     }
846 
847     /**
848      * Special version of {@link #getDimensionPixelSize} for retrieving
849      * {@link android.view.ViewGroup}'s layout_width and layout_height
850      * attributes.  This is only here for performance reasons; applications
851      * should use {@link #getDimensionPixelSize}.
852      *
853      * @param index Index of the attribute to retrieve.
854      * @param defValue The default value to return if this attribute is not
855      *                 default or contains the wrong type of data.
856      *
857      * @return Attribute dimension value multiplied by the appropriate
858      *         metric and truncated to integer pixels.
859      * @throws RuntimeException if the TypedArray has already been recycled.
860      */
getLayoutDimension(@tyleableRes int index, int defValue)861     public int getLayoutDimension(@StyleableRes int index, int defValue) {
862         if (mRecycled) {
863             throw new RuntimeException("Cannot make calls to a recycled instance!");
864         }
865 
866         index *= STYLE_NUM_ENTRIES;
867         final int[] data = mData;
868         final int type = data[index + STYLE_TYPE];
869         if (type >= TypedValue.TYPE_FIRST_INT
870                 && type <= TypedValue.TYPE_LAST_INT) {
871             return data[index + STYLE_DATA];
872         } else if (type == TypedValue.TYPE_DIMENSION) {
873             return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics);
874         }
875 
876         return defValue;
877     }
878 
879     /**
880      * Retrieves a fractional unit attribute at <var>index</var>.
881      *
882      * @param index Index of attribute to retrieve.
883      * @param base The base value of this fraction.  In other words, a
884      *             standard fraction is multiplied by this value.
885      * @param pbase The parent base value of this fraction.  In other
886      *             words, a parent fraction (nn%p) is multiplied by this
887      *             value.
888      * @param defValue Value to return if the attribute is not defined or
889      *                 not a resource.
890      *
891      * @return Attribute fractional value multiplied by the appropriate
892      *         base value, or defValue if not defined.
893      * @throws RuntimeException if the TypedArray has already been recycled.
894      * @throws UnsupportedOperationException if the attribute is defined but is
895      *         not a fraction.
896      */
getFraction(@tyleableRes int index, int base, int pbase, float defValue)897     public float getFraction(@StyleableRes int index, int base, int pbase, float defValue) {
898         if (mRecycled) {
899             throw new RuntimeException("Cannot make calls to a recycled instance!");
900         }
901 
902         final int attrIndex = index;
903         index *= STYLE_NUM_ENTRIES;
904 
905         final int[] data = mData;
906         final int type = data[index + STYLE_TYPE];
907         if (type == TypedValue.TYPE_NULL) {
908             return defValue;
909         } else if (type == TypedValue.TYPE_FRACTION) {
910             return TypedValue.complexToFraction(data[index + STYLE_DATA], base, pbase);
911         } else if (type == TypedValue.TYPE_ATTRIBUTE) {
912             final TypedValue value = mValue;
913             getValueAt(index, value);
914             throw new UnsupportedOperationException(
915                     "Failed to resolve attribute at index " + attrIndex + ": " + value
916                             + ", theme=" + mTheme);
917         }
918 
919         throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
920                 + " to fraction: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme);
921     }
922 
923     /**
924      * Retrieves the resource identifier for the attribute at
925      * <var>index</var>.  Note that attribute resource as resolved when
926      * the overall {@link TypedArray} object is retrieved.  As a
927      * result, this function will return the resource identifier of the
928      * final resource value that was found, <em>not</em> necessarily the
929      * original resource that was specified by the attribute.
930      *
931      * @param index Index of attribute to retrieve.
932      * @param defValue Value to return if the attribute is not defined or
933      *                 not a resource.
934      *
935      * @return Attribute resource identifier, or defValue if not defined.
936      * @throws RuntimeException if the TypedArray has already been recycled.
937      */
938     @AnyRes
getResourceId(@tyleableRes int index, int defValue)939     public int getResourceId(@StyleableRes int index, int defValue) {
940         if (mRecycled) {
941             throw new RuntimeException("Cannot make calls to a recycled instance!");
942         }
943 
944         index *= STYLE_NUM_ENTRIES;
945         final int[] data = mData;
946         if (data[index + STYLE_TYPE] != TypedValue.TYPE_NULL) {
947             final int resid = data[index + STYLE_RESOURCE_ID];
948             if (resid != 0) {
949                 return resid;
950             }
951         }
952         return defValue;
953     }
954 
955     /**
956      * Retrieves the theme attribute resource identifier for the attribute at
957      * <var>index</var>.
958      *
959      * @param index Index of attribute to retrieve.
960      * @param defValue Value to return if the attribute is not defined or not a
961      *                 resource.
962      *
963      * @return Theme attribute resource identifier, or defValue if not defined.
964      * @throws RuntimeException if the TypedArray has already been recycled.
965      * @hide
966      */
getThemeAttributeId(@tyleableRes int index, int defValue)967     public int getThemeAttributeId(@StyleableRes int index, int defValue) {
968         if (mRecycled) {
969             throw new RuntimeException("Cannot make calls to a recycled instance!");
970         }
971 
972         index *= STYLE_NUM_ENTRIES;
973         final int[] data = mData;
974         if (data[index + STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) {
975             return data[index + STYLE_DATA];
976         }
977         return defValue;
978     }
979 
980     /**
981      * Retrieve the Drawable for the attribute at <var>index</var>.
982      * <p>
983      * This method will throw an exception if the attribute is defined but is
984      * not a color or drawable resource.
985      *
986      * @param index Index of attribute to retrieve.
987      *
988      * @return Drawable for the attribute, or {@code null} if not defined.
989      * @throws RuntimeException if the TypedArray has already been recycled.
990      * @throws UnsupportedOperationException if the attribute is defined but is
991      *         not a color or drawable resource.
992      */
993     @Nullable
getDrawable(@tyleableRes int index)994     public Drawable getDrawable(@StyleableRes int index) {
995         return getDrawableForDensity(index, 0);
996     }
997 
998     /**
999      * Version of {@link #getDrawable(int)} that accepts an override density.
1000      * @hide
1001      */
1002     @Nullable
getDrawableForDensity(@tyleableRes int index, int density)1003     public Drawable getDrawableForDensity(@StyleableRes int index, int density) {
1004         if (mRecycled) {
1005             throw new RuntimeException("Cannot make calls to a recycled instance!");
1006         }
1007 
1008         final TypedValue value = mValue;
1009         if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
1010             if (value.type == TypedValue.TYPE_ATTRIBUTE) {
1011                 throw new UnsupportedOperationException(
1012                         "Failed to resolve attribute at index " + index + ": " + value
1013                                 + ", theme=" + mTheme);
1014             }
1015 
1016             if (density > 0) {
1017                 // If the density is overridden, the value in the TypedArray will not reflect this.
1018                 // Do a separate lookup of the resourceId with the density override.
1019                 mResources.getValueForDensity(value.resourceId, density, value, true);
1020             }
1021             return mResources.loadDrawable(value, value.resourceId, density, mTheme);
1022         }
1023         return null;
1024     }
1025 
1026     /**
1027      * Retrieve the Typeface for the attribute at <var>index</var>.
1028      * <p>
1029      * This method will throw an exception if the attribute is defined but is
1030      * not a font.
1031      *
1032      * @param index Index of attribute to retrieve.
1033      *
1034      * @return Typeface for the attribute, or {@code null} if not defined.
1035      * @throws RuntimeException if the TypedArray has already been recycled.
1036      * @throws UnsupportedOperationException if the attribute is defined but is
1037      *         not a font resource.
1038      */
1039     @Nullable
getFont(@tyleableRes int index)1040     public Typeface getFont(@StyleableRes int index) {
1041         if (mRecycled) {
1042             throw new RuntimeException("Cannot make calls to a recycled instance!");
1043         }
1044 
1045         final TypedValue value = mValue;
1046         if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
1047             if (value.type == TypedValue.TYPE_ATTRIBUTE) {
1048                 throw new UnsupportedOperationException(
1049                         "Failed to resolve attribute at index " + index + ": " + value
1050                                 + ", theme=" + mTheme);
1051             }
1052             return mResources.getFont(value, value.resourceId);
1053         }
1054         return null;
1055     }
1056 
1057     /**
1058      * Retrieve the CharSequence[] for the attribute at <var>index</var>.
1059      * This gets the resource ID of the selected attribute, and uses
1060      * {@link Resources#getTextArray Resources.getTextArray} of the owning
1061      * Resources object to retrieve its String[].
1062      * <p>
1063      * This method will throw an exception if the attribute is defined but is
1064      * not a text array resource.
1065      *
1066      * @param index Index of attribute to retrieve.
1067      *
1068      * @return CharSequence[] for the attribute, or {@code null} if not
1069      *         defined.
1070      * @throws RuntimeException if the TypedArray has already been recycled.
1071      */
getTextArray(@tyleableRes int index)1072     public CharSequence[] getTextArray(@StyleableRes int index) {
1073         if (mRecycled) {
1074             throw new RuntimeException("Cannot make calls to a recycled instance!");
1075         }
1076 
1077         final TypedValue value = mValue;
1078         if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
1079             return mResources.getTextArray(value.resourceId);
1080         }
1081         return null;
1082     }
1083 
1084     /**
1085      * Retrieve the raw TypedValue for the attribute at <var>index</var>.
1086      *
1087      * @param index Index of attribute to retrieve.
1088      * @param outValue TypedValue object in which to place the attribute's
1089      *                 data.
1090      *
1091      * @return {@code true} if the value was retrieved and not @empty, {@code false} otherwise.
1092      * @throws RuntimeException if the TypedArray has already been recycled.
1093      */
getValue(@tyleableRes int index, TypedValue outValue)1094     public boolean getValue(@StyleableRes int index, TypedValue outValue) {
1095         if (mRecycled) {
1096             throw new RuntimeException("Cannot make calls to a recycled instance!");
1097         }
1098 
1099         return getValueAt(index * STYLE_NUM_ENTRIES, outValue);
1100     }
1101 
1102     /**
1103      * Returns the type of attribute at the specified index.
1104      *
1105      * @param index Index of attribute whose type to retrieve.
1106      *
1107      * @return Attribute type.
1108      * @throws RuntimeException if the TypedArray has already been recycled.
1109      */
getType(@tyleableRes int index)1110     public int getType(@StyleableRes int index) {
1111         if (mRecycled) {
1112             throw new RuntimeException("Cannot make calls to a recycled instance!");
1113         }
1114 
1115         index *= STYLE_NUM_ENTRIES;
1116         return mData[index + STYLE_TYPE];
1117     }
1118 
1119     /**
1120      * Returns the resource ID of the style or layout against which the specified attribute was
1121      * resolved, otherwise returns defValue.
1122      *
1123      * For example, if you we resolving two attributes {@code android:attribute1} and
1124      * {@code android:attribute2} and you were inflating a {@link android.view.View} from
1125      * {@code layout/my_layout.xml}:
1126      * <pre>
1127      *     &lt;View
1128      *         style="@style/viewStyle"
1129      *         android:layout_width="wrap_content"
1130      *         android:layout_height="wrap_content"
1131      *         android:attribute1="foo"/&gt;
1132      * </pre>
1133      *
1134      * and {@code @style/viewStyle} is:
1135      * <pre>
1136      *     &lt;style android:name="viewStyle"&gt;
1137      *         &lt;item name="android:attribute2"&gt;bar&lt;item/&gt;
1138      *     &lt;style/&gt;
1139      * </pre>
1140      *
1141      * then resolved {@link TypedArray} will have values that return source resource ID of
1142      * {@code R.layout.my_layout} for {@code android:attribute1} and {@code R.style.viewStyle} for
1143      * {@code android:attribute2}.
1144      *
1145      * @param index Index of attribute whose source style to retrieve.
1146      * @param defaultValue Value to return if the attribute is not defined or
1147      *                     not a resource.
1148      *
1149      * @return Either a style resource ID, layout resource ID, or defaultValue if it was not
1150      * resolved in a style or layout.
1151      * @throws RuntimeException if the TypedArray has already been recycled.
1152      */
1153     @AnyRes
getSourceResourceId(@tyleableRes int index, @AnyRes int defaultValue)1154     public int getSourceResourceId(@StyleableRes int index, @AnyRes int defaultValue) {
1155         if (mRecycled) {
1156             throw new RuntimeException("Cannot make calls to a recycled instance!");
1157         }
1158 
1159         index *= STYLE_NUM_ENTRIES;
1160         final int resid = mData[index + STYLE_SOURCE_RESOURCE_ID];
1161         if (resid != 0) {
1162             return resid;
1163         }
1164         return defaultValue;
1165     }
1166 
1167     /**
1168      * Determines whether there is an attribute at <var>index</var>.
1169      * <p>
1170      * <strong>Note:</strong> If the attribute was set to {@code @empty} or
1171      * {@code @undefined}, this method returns {@code false}.
1172      *
1173      * @param index Index of attribute to retrieve.
1174      *
1175      * @return True if the attribute has a value, false otherwise.
1176      * @throws RuntimeException if the TypedArray has already been recycled.
1177      */
hasValue(@tyleableRes int index)1178     public boolean hasValue(@StyleableRes int index) {
1179         if (mRecycled) {
1180             throw new RuntimeException("Cannot make calls to a recycled instance!");
1181         }
1182 
1183         index *= STYLE_NUM_ENTRIES;
1184         final int[] data = mData;
1185         final int type = data[index + STYLE_TYPE];
1186         return type != TypedValue.TYPE_NULL;
1187     }
1188 
1189     /**
1190      * Determines whether there is an attribute at <var>index</var>, returning
1191      * {@code true} if the attribute was explicitly set to {@code @empty} and
1192      * {@code false} only if the attribute was undefined.
1193      *
1194      * @param index Index of attribute to retrieve.
1195      *
1196      * @return True if the attribute has a value or is empty, false otherwise.
1197      * @throws RuntimeException if the TypedArray has already been recycled.
1198      */
hasValueOrEmpty(@tyleableRes int index)1199     public boolean hasValueOrEmpty(@StyleableRes int index) {
1200         if (mRecycled) {
1201             throw new RuntimeException("Cannot make calls to a recycled instance!");
1202         }
1203 
1204         index *= STYLE_NUM_ENTRIES;
1205         final int[] data = mData;
1206         final int type = data[index + STYLE_TYPE];
1207         return type != TypedValue.TYPE_NULL
1208                 || data[index + STYLE_DATA] == TypedValue.DATA_NULL_EMPTY;
1209     }
1210 
1211     /**
1212      * Retrieve the raw TypedValue for the attribute at <var>index</var>
1213      * and return a temporary object holding its data.  This object is only
1214      * valid until the next call on to {@link TypedArray}.
1215      *
1216      * @param index Index of attribute to retrieve.
1217      *
1218      * @return Returns a TypedValue object if the attribute is defined,
1219      *         containing its data; otherwise returns null.  (You will not
1220      *         receive a TypedValue whose type is TYPE_NULL.)
1221      * @throws RuntimeException if the TypedArray has already been recycled.
1222      */
peekValue(@tyleableRes int index)1223     public TypedValue peekValue(@StyleableRes int index) {
1224         if (mRecycled) {
1225             throw new RuntimeException("Cannot make calls to a recycled instance!");
1226         }
1227 
1228         final TypedValue value = mValue;
1229         if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
1230             return value;
1231         }
1232         return null;
1233     }
1234 
1235     /**
1236      * Returns a message about the parser state suitable for printing error messages.
1237      *
1238      * @return Human-readable description of current parser state.
1239      * @throws RuntimeException if the TypedArray has already been recycled.
1240      */
getPositionDescription()1241     public String getPositionDescription() {
1242         if (mRecycled) {
1243             throw new RuntimeException("Cannot make calls to a recycled instance!");
1244         }
1245 
1246         return mXml != null ? mXml.getPositionDescription() : "<internal>";
1247     }
1248 
1249     /**
1250      * Recycles the TypedArray, to be re-used by a later caller. After calling
1251      * this function you must not ever touch the typed array again.
1252      *
1253      * @throws RuntimeException if the TypedArray has already been recycled.
1254      */
recycle()1255     public void recycle() {
1256         if (mRecycled) {
1257             throw new RuntimeException(toString() + " recycled twice!");
1258         }
1259 
1260         mRecycled = true;
1261 
1262         // These may have been set by the client.
1263         mXml = null;
1264         mTheme = null;
1265         mAssets = null;
1266 
1267         mResources.mTypedArrayPool.release(this);
1268     }
1269 
1270     /**
1271      * Recycles the TypedArray, to be re-used by a later caller. After calling
1272      * this function you must not ever touch the typed array again.
1273      *
1274      * @see #recycle()
1275      * @throws RuntimeException if the TypedArray has already been recycled.
1276      */
close()1277     public void close() {
1278         recycle();
1279     }
1280 
1281     /**
1282      * Extracts theme attributes from a typed array for later resolution using
1283      * {@link android.content.res.Resources.Theme#resolveAttributes(int[], int[])}.
1284      * Removes the entries from the typed array so that subsequent calls to typed
1285      * getters will return the default value without crashing.
1286      *
1287      * @return an array of length {@link #getIndexCount()} populated with theme
1288      *         attributes, or null if there are no theme attributes in the typed
1289      *         array
1290      * @throws RuntimeException if the TypedArray has already been recycled.
1291      * @hide
1292      */
1293     @Nullable
1294     @UnsupportedAppUsage
extractThemeAttrs()1295     public int[] extractThemeAttrs() {
1296         return extractThemeAttrs(null);
1297     }
1298 
1299     /**
1300      * @hide
1301      */
1302     @Nullable
1303     @UnsupportedAppUsage
extractThemeAttrs(@ullable int[] scrap)1304     public int[] extractThemeAttrs(@Nullable int[] scrap) {
1305         if (mRecycled) {
1306             throw new RuntimeException("Cannot make calls to a recycled instance!");
1307         }
1308 
1309         int[] attrs = null;
1310 
1311         final int[] data = mData;
1312         final int N = length();
1313         for (int i = 0; i < N; i++) {
1314             final int index = i * STYLE_NUM_ENTRIES;
1315             if (data[index + STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) {
1316                 // Not an attribute, ignore.
1317                 continue;
1318             }
1319 
1320             // Null the entry so that we can safely call getZzz().
1321             data[index + STYLE_TYPE] = TypedValue.TYPE_NULL;
1322 
1323             final int attr = data[index + STYLE_DATA];
1324             if (attr == 0) {
1325                 // Useless data, ignore.
1326                 continue;
1327             }
1328 
1329             // Ensure we have a usable attribute array.
1330             if (attrs == null) {
1331                 if (scrap != null && scrap.length == N) {
1332                     attrs = scrap;
1333                     Arrays.fill(attrs, 0);
1334                 } else {
1335                     attrs = new int[N];
1336                 }
1337             }
1338 
1339             attrs[i] = attr;
1340         }
1341 
1342         return attrs;
1343     }
1344 
1345     /**
1346      * Return a mask of the configuration parameters for which the values in
1347      * this typed array may change.
1348      *
1349      * @return Returns a mask of the changing configuration parameters, as
1350      *         defined by {@link android.content.pm.ActivityInfo}.
1351      * @throws RuntimeException if the TypedArray has already been recycled.
1352      * @see android.content.pm.ActivityInfo
1353      */
getChangingConfigurations()1354     public @Config int getChangingConfigurations() {
1355         if (mRecycled) {
1356             throw new RuntimeException("Cannot make calls to a recycled instance!");
1357         }
1358 
1359         @Config int changingConfig = 0;
1360 
1361         final int[] data = mData;
1362         final int N = length();
1363         for (int i = 0; i < N; i++) {
1364             final int index = i * STYLE_NUM_ENTRIES;
1365             final int type = data[index + STYLE_TYPE];
1366             if (type == TypedValue.TYPE_NULL) {
1367                 continue;
1368             }
1369             changingConfig |= ActivityInfo.activityInfoConfigNativeToJava(
1370                     data[index + STYLE_CHANGING_CONFIGURATIONS]);
1371         }
1372         return changingConfig;
1373     }
1374 
1375     @UnsupportedAppUsage
getValueAt(int index, TypedValue outValue)1376     private boolean getValueAt(int index, TypedValue outValue) {
1377         final int[] data = mData;
1378         final int type = data[index + STYLE_TYPE];
1379         if (type == TypedValue.TYPE_NULL) {
1380             return false;
1381         }
1382         outValue.type = type;
1383         outValue.data = data[index + STYLE_DATA];
1384         outValue.assetCookie = data[index + STYLE_ASSET_COOKIE];
1385         outValue.resourceId = data[index + STYLE_RESOURCE_ID];
1386         outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
1387                 data[index + STYLE_CHANGING_CONFIGURATIONS]);
1388         outValue.density = data[index + STYLE_DENSITY];
1389         outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null;
1390         outValue.sourceResourceId = data[index + STYLE_SOURCE_RESOURCE_ID];
1391         return true;
1392     }
1393 
1394     @Nullable
loadStringValueAt(int index)1395     private CharSequence loadStringValueAt(int index) {
1396         final int[] data = mData;
1397         final int cookie = data[index + STYLE_ASSET_COOKIE];
1398         CharSequence value = null;
1399         if (cookie < 0) {
1400             if (mXml != null) {
1401                 value = mXml.getPooledString(data[index + STYLE_DATA]);
1402             }
1403         } else {
1404             value = mAssets.getPooledStringForCookie(cookie, data[index + STYLE_DATA]);
1405         }
1406         if (value != null && mXml != null && mXml.mValidator != null) {
1407             mXml.mValidator.validateResStrAttr(mXml, index / STYLE_NUM_ENTRIES, value);
1408         }
1409         return value;
1410     }
1411 
1412     /** @hide */
TypedArray(Resources resources)1413     protected TypedArray(Resources resources) {
1414         mResources = resources;
1415         mMetrics = mResources.getDisplayMetrics();
1416         mAssets = mResources.getAssets();
1417     }
1418 
1419     @Override
toString()1420     public String toString() {
1421         return Arrays.toString(mData);
1422     }
1423 }
1424