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