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