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