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