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