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.Nullable;
20 import android.graphics.drawable.Drawable;
21 import android.util.AttributeSet;
22 import android.util.DisplayMetrics;
23 import android.util.Log;
24 import android.util.TypedValue;
25 
26 import com.android.internal.util.XmlUtils;
27 
28 import java.util.Arrays;
29 
30 /**
31  * Container for an array of values that were retrieved with
32  * {@link Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)}
33  * or {@link Resources#obtainAttributes}.  Be
34  * sure to call {@link #recycle} when done with them.
35  *
36  * The indices used to retrieve values from this structure correspond to
37  * the positions of the attributes given to obtainStyledAttributes.
38  */
39 public class TypedArray {
40 
obtain(Resources res, int len)41     static TypedArray obtain(Resources res, int len) {
42         final TypedArray attrs = res.mTypedArrayPool.acquire();
43         if (attrs != null) {
44             attrs.mLength = len;
45             attrs.mRecycled = false;
46 
47             final int fullLen = len * AssetManager.STYLE_NUM_ENTRIES;
48             if (attrs.mData.length >= fullLen) {
49                 return attrs;
50             }
51 
52             attrs.mData = new int[fullLen];
53             attrs.mIndices = new int[1 + len];
54             return attrs;
55         }
56 
57         return new TypedArray(res,
58                 new int[len*AssetManager.STYLE_NUM_ENTRIES],
59                 new int[1+len], len);
60     }
61 
62     private final Resources mResources;
63     private final DisplayMetrics mMetrics;
64     private final AssetManager mAssets;
65 
66     private boolean mRecycled;
67 
68     /*package*/ XmlBlock.Parser mXml;
69     /*package*/ Resources.Theme mTheme;
70     /*package*/ int[] mData;
71     /*package*/ int[] mIndices;
72     /*package*/ int mLength;
73     /*package*/ TypedValue mValue = new TypedValue();
74 
75     /**
76      * Return the number of values in this array.
77      */
length()78     public int length() {
79         if (mRecycled) {
80             throw new RuntimeException("Cannot make calls to a recycled instance!");
81         }
82 
83         return mLength;
84     }
85 
86     /**
87      * Return the number of indices in the array that actually have data.
88      */
getIndexCount()89     public int getIndexCount() {
90         if (mRecycled) {
91             throw new RuntimeException("Cannot make calls to a recycled instance!");
92         }
93 
94         return mIndices[0];
95     }
96 
97     /**
98      * Return an index in the array that has data.
99      *
100      * @param at The index you would like to returned, ranging from 0 to
101      * {@link #getIndexCount()}.
102      *
103      * @return The index at the given offset, which can be used with
104      * {@link #getValue} and related APIs.
105      */
getIndex(int at)106     public int getIndex(int at) {
107         if (mRecycled) {
108             throw new RuntimeException("Cannot make calls to a recycled instance!");
109         }
110 
111         return mIndices[1+at];
112     }
113 
114     /**
115      * Return the Resources object this array was loaded from.
116      */
getResources()117     public Resources getResources() {
118         if (mRecycled) {
119             throw new RuntimeException("Cannot make calls to a recycled instance!");
120         }
121 
122         return mResources;
123     }
124 
125     /**
126      * Retrieve the styled string value for the attribute at <var>index</var>.
127      *
128      * @param index Index of attribute to retrieve.
129      *
130      * @return CharSequence holding string data.  May be styled.  Returns
131      *         null if the attribute is not defined.
132      */
getText(int index)133     public CharSequence getText(int index) {
134         if (mRecycled) {
135             throw new RuntimeException("Cannot make calls to a recycled instance!");
136         }
137 
138         index *= AssetManager.STYLE_NUM_ENTRIES;
139         final int[] data = mData;
140         final int type = data[index+AssetManager.STYLE_TYPE];
141         if (type == TypedValue.TYPE_NULL) {
142             return null;
143         } else if (type == TypedValue.TYPE_STRING) {
144             return loadStringValueAt(index);
145         }
146 
147         TypedValue v = mValue;
148         if (getValueAt(index, v)) {
149             Log.w(Resources.TAG, "Converting to string: " + v);
150             return v.coerceToString();
151         }
152         Log.w(Resources.TAG, "getString of bad type: 0x"
153               + Integer.toHexString(type));
154         return null;
155     }
156 
157     /**
158      * Retrieve the string value for the attribute at <var>index</var>.
159      *
160      * @param index Index of attribute to retrieve.
161      *
162      * @return String holding string data.  Any styling information is
163      * removed.  Returns null if the attribute is not defined.
164      */
getString(int index)165     public String getString(int index) {
166         if (mRecycled) {
167             throw new RuntimeException("Cannot make calls to a recycled instance!");
168         }
169 
170         index *= AssetManager.STYLE_NUM_ENTRIES;
171         final int[] data = mData;
172         final int type = data[index+AssetManager.STYLE_TYPE];
173         if (type == TypedValue.TYPE_NULL) {
174             return null;
175         } else if (type == TypedValue.TYPE_STRING) {
176             return loadStringValueAt(index).toString();
177         }
178 
179         TypedValue v = mValue;
180         if (getValueAt(index, v)) {
181             Log.w(Resources.TAG, "Converting to string: " + v);
182             CharSequence cs = v.coerceToString();
183             return cs != null ? cs.toString() : null;
184         }
185         Log.w(Resources.TAG, "getString of bad type: 0x"
186               + Integer.toHexString(type));
187         return null;
188     }
189 
190     /**
191      * Retrieve the string value for the attribute at <var>index</var>, but
192      * only if that string comes from an immediate value in an XML file.  That
193      * is, this does not allow references to string resources, string
194      * attributes, or conversions from other types.  As such, this method
195      * will only return strings for TypedArray objects that come from
196      * attributes in an XML file.
197      *
198      * @param index Index of attribute to retrieve.
199      *
200      * @return String holding string data.  Any styling information is
201      * removed.  Returns null if the attribute is not defined or is not
202      * an immediate string value.
203      */
getNonResourceString(int index)204     public String getNonResourceString(int index) {
205         if (mRecycled) {
206             throw new RuntimeException("Cannot make calls to a recycled instance!");
207         }
208 
209         index *= AssetManager.STYLE_NUM_ENTRIES;
210         final int[] data = mData;
211         final int type = data[index+AssetManager.STYLE_TYPE];
212         if (type == TypedValue.TYPE_STRING) {
213             final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
214             if (cookie < 0) {
215                 return mXml.getPooledString(
216                     data[index+AssetManager.STYLE_DATA]).toString();
217             }
218         }
219         return null;
220     }
221 
222     /**
223      * @hide
224      * Retrieve the string value for the attribute at <var>index</var> that is
225      * not allowed to change with the given configurations.
226      *
227      * @param index Index of attribute to retrieve.
228      * @param allowedChangingConfigs Bit mask of configurations from
229      * {@link Configuration}.NATIVE_CONFIG_* that are allowed to change.
230      *
231      * @return String holding string data.  Any styling information is
232      * removed.  Returns null if the attribute is not defined.
233      */
getNonConfigurationString(int index, int allowedChangingConfigs)234     public String getNonConfigurationString(int index, int allowedChangingConfigs) {
235         if (mRecycled) {
236             throw new RuntimeException("Cannot make calls to a recycled instance!");
237         }
238 
239         index *= AssetManager.STYLE_NUM_ENTRIES;
240         final int[] data = mData;
241         final int type = data[index+AssetManager.STYLE_TYPE];
242         if ((data[index+AssetManager.STYLE_CHANGING_CONFIGURATIONS]&~allowedChangingConfigs) != 0) {
243             return null;
244         }
245         if (type == TypedValue.TYPE_NULL) {
246             return null;
247         } else if (type == TypedValue.TYPE_STRING) {
248             return loadStringValueAt(index).toString();
249         }
250 
251         TypedValue v = mValue;
252         if (getValueAt(index, v)) {
253             Log.w(Resources.TAG, "Converting to string: " + v);
254             CharSequence cs = v.coerceToString();
255             return cs != null ? cs.toString() : null;
256         }
257         Log.w(Resources.TAG, "getString of bad type: 0x"
258               + Integer.toHexString(type));
259         return null;
260     }
261 
262     /**
263      * Retrieve the boolean value for the attribute at <var>index</var>.
264      *
265      * @param index Index of attribute to retrieve.
266      * @param defValue Value to return if the attribute is not defined.
267      *
268      * @return Attribute boolean value, or defValue if not defined.
269      */
getBoolean(int index, boolean defValue)270     public boolean getBoolean(int index, boolean defValue) {
271         if (mRecycled) {
272             throw new RuntimeException("Cannot make calls to a recycled instance!");
273         }
274 
275         index *= AssetManager.STYLE_NUM_ENTRIES;
276         final int[] data = mData;
277         final int type = data[index+AssetManager.STYLE_TYPE];
278         if (type == TypedValue.TYPE_NULL) {
279             return defValue;
280         } else if (type >= TypedValue.TYPE_FIRST_INT
281             && type <= TypedValue.TYPE_LAST_INT) {
282             return data[index+AssetManager.STYLE_DATA] != 0;
283         }
284 
285         TypedValue v = mValue;
286         if (getValueAt(index, v)) {
287             Log.w(Resources.TAG, "Converting to boolean: " + v);
288             return XmlUtils.convertValueToBoolean(
289                 v.coerceToString(), defValue);
290         }
291         Log.w(Resources.TAG, "getBoolean of bad type: 0x"
292               + Integer.toHexString(type));
293         return defValue;
294     }
295 
296     /**
297      * Retrieve the integer value for the attribute at <var>index</var>.
298      *
299      * @param index Index of attribute to retrieve.
300      * @param defValue Value to return if the attribute is not defined.
301      *
302      * @return Attribute int value, or defValue if not defined.
303      */
getInt(int index, int defValue)304     public int getInt(int index, int defValue) {
305         if (mRecycled) {
306             throw new RuntimeException("Cannot make calls to a recycled instance!");
307         }
308 
309         index *= AssetManager.STYLE_NUM_ENTRIES;
310         final int[] data = mData;
311         final int type = data[index+AssetManager.STYLE_TYPE];
312         if (type == TypedValue.TYPE_NULL) {
313             return defValue;
314         } else if (type >= TypedValue.TYPE_FIRST_INT
315             && type <= TypedValue.TYPE_LAST_INT) {
316             return data[index+AssetManager.STYLE_DATA];
317         }
318 
319         TypedValue v = mValue;
320         if (getValueAt(index, v)) {
321             Log.w(Resources.TAG, "Converting to int: " + v);
322             return XmlUtils.convertValueToInt(
323                 v.coerceToString(), defValue);
324         }
325         Log.w(Resources.TAG, "getInt of bad type: 0x"
326               + Integer.toHexString(type));
327         return defValue;
328     }
329 
330     /**
331      * Retrieve the float value for the attribute at <var>index</var>.
332      *
333      * @param index Index of attribute to retrieve.
334      *
335      * @return Attribute float value, or defValue if not defined..
336      */
getFloat(int index, float defValue)337     public float getFloat(int index, float defValue) {
338         if (mRecycled) {
339             throw new RuntimeException("Cannot make calls to a recycled instance!");
340         }
341 
342         index *= AssetManager.STYLE_NUM_ENTRIES;
343         final int[] data = mData;
344         final int type = data[index+AssetManager.STYLE_TYPE];
345         if (type == TypedValue.TYPE_NULL) {
346             return defValue;
347         } else if (type == TypedValue.TYPE_FLOAT) {
348             return Float.intBitsToFloat(data[index+AssetManager.STYLE_DATA]);
349         } else if (type >= TypedValue.TYPE_FIRST_INT
350             && type <= TypedValue.TYPE_LAST_INT) {
351             return data[index+AssetManager.STYLE_DATA];
352         }
353 
354         TypedValue v = mValue;
355         if (getValueAt(index, v)) {
356             Log.w(Resources.TAG, "Converting to float: " + v);
357             CharSequence str = v.coerceToString();
358             if (str != null) {
359                 return Float.parseFloat(str.toString());
360             }
361         }
362         Log.w(Resources.TAG, "getFloat of bad type: 0x"
363               + Integer.toHexString(type));
364         return defValue;
365     }
366 
367     /**
368      * Retrieve the color value for the attribute at <var>index</var>.  If
369      * the attribute references a color resource holding a complex
370      * {@link android.content.res.ColorStateList}, then the default color from
371      * the set is returned.
372      *
373      * @param index Index of attribute to retrieve.
374      * @param defValue Value to return if the attribute is not defined or
375      *                 not a resource.
376      *
377      * @return Attribute color value, or defValue if not defined.
378      */
getColor(int index, int defValue)379     public int getColor(int index, int defValue) {
380         if (mRecycled) {
381             throw new RuntimeException("Cannot make calls to a recycled instance!");
382         }
383 
384         index *= AssetManager.STYLE_NUM_ENTRIES;
385         final int[] data = mData;
386         final int type = data[index+AssetManager.STYLE_TYPE];
387         if (type == TypedValue.TYPE_NULL) {
388             return defValue;
389         } else if (type >= TypedValue.TYPE_FIRST_INT
390             && type <= TypedValue.TYPE_LAST_INT) {
391             return data[index+AssetManager.STYLE_DATA];
392         } else if (type == TypedValue.TYPE_STRING) {
393             final TypedValue value = mValue;
394             if (getValueAt(index, value)) {
395                 ColorStateList csl = mResources.loadColorStateList(
396                         value, value.resourceId);
397                 return csl.getDefaultColor();
398             }
399             return defValue;
400         } else if (type == TypedValue.TYPE_ATTRIBUTE) {
401             throw new RuntimeException("Failed to resolve attribute at index " + index);
402         }
403 
404         throw new UnsupportedOperationException("Can't convert to color: type=0x"
405                 + Integer.toHexString(type));
406     }
407 
408     /**
409      * Retrieve the ColorStateList for the attribute at <var>index</var>.
410      * The value may be either a single solid color or a reference to
411      * a color or complex {@link android.content.res.ColorStateList} description.
412      *
413      * @param index Index of attribute to retrieve.
414      *
415      * @return ColorStateList for the attribute, or null if not defined.
416      */
getColorStateList(int index)417     public ColorStateList getColorStateList(int index) {
418         if (mRecycled) {
419             throw new RuntimeException("Cannot make calls to a recycled instance!");
420         }
421 
422         final TypedValue value = mValue;
423         if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
424             if (value.type == TypedValue.TYPE_ATTRIBUTE) {
425                 throw new RuntimeException("Failed to resolve attribute at index " + index);
426             }
427             return mResources.loadColorStateList(value, value.resourceId);
428         }
429         return null;
430     }
431 
432     /**
433      * Retrieve the integer value for the attribute at <var>index</var>.
434      *
435      * @param index Index of attribute to retrieve.
436      * @param defValue Value to return if the attribute is not defined or
437      *                 not a resource.
438      *
439      * @return Attribute integer value, or defValue if not defined.
440      */
getInteger(int index, int defValue)441     public int getInteger(int index, int defValue) {
442         if (mRecycled) {
443             throw new RuntimeException("Cannot make calls to a recycled instance!");
444         }
445 
446         index *= AssetManager.STYLE_NUM_ENTRIES;
447         final int[] data = mData;
448         final int type = data[index+AssetManager.STYLE_TYPE];
449         if (type == TypedValue.TYPE_NULL) {
450             return defValue;
451         } else if (type >= TypedValue.TYPE_FIRST_INT
452             && type <= TypedValue.TYPE_LAST_INT) {
453             return data[index+AssetManager.STYLE_DATA];
454         } else if (type == TypedValue.TYPE_ATTRIBUTE) {
455             throw new RuntimeException("Failed to resolve attribute at index " + index);
456         }
457 
458         throw new UnsupportedOperationException("Can't convert to integer: type=0x"
459                 + Integer.toHexString(type));
460     }
461 
462     /**
463      * Retrieve a dimensional unit attribute at <var>index</var>.  Unit
464      * conversions are based on the current {@link DisplayMetrics}
465      * associated with the resources this {@link TypedArray} object
466      * came from.
467      *
468      * @param index Index of attribute to retrieve.
469      * @param defValue Value to return if the attribute is not defined or
470      *                 not a resource.
471      *
472      * @return Attribute dimension value multiplied by the appropriate
473      * metric, or defValue if not defined.
474      *
475      * @see #getDimensionPixelOffset
476      * @see #getDimensionPixelSize
477      */
getDimension(int index, float defValue)478     public float getDimension(int index, float defValue) {
479         if (mRecycled) {
480             throw new RuntimeException("Cannot make calls to a recycled instance!");
481         }
482 
483         index *= AssetManager.STYLE_NUM_ENTRIES;
484         final int[] data = mData;
485         final int type = data[index+AssetManager.STYLE_TYPE];
486         if (type == TypedValue.TYPE_NULL) {
487             return defValue;
488         } else if (type == TypedValue.TYPE_DIMENSION) {
489             return TypedValue.complexToDimension(
490                 data[index+AssetManager.STYLE_DATA], mMetrics);
491         } else if (type == TypedValue.TYPE_ATTRIBUTE) {
492             throw new RuntimeException("Failed to resolve attribute at index " + index);
493         }
494 
495         throw new UnsupportedOperationException("Can't convert to dimension: type=0x"
496                 + Integer.toHexString(type));
497     }
498 
499     /**
500      * Retrieve a dimensional unit attribute at <var>index</var> for use
501      * as an offset in raw pixels.  This is the same as
502      * {@link #getDimension}, except the returned value is converted to
503      * integer pixels for you.  An offset conversion involves simply
504      * truncating the base value to an integer.
505      *
506      * @param index Index of attribute to retrieve.
507      * @param defValue Value to return if the attribute is not defined or
508      *                 not a resource.
509      *
510      * @return Attribute dimension value multiplied by the appropriate
511      * metric and truncated to integer pixels, or defValue if not defined.
512      *
513      * @see #getDimension
514      * @see #getDimensionPixelSize
515      */
getDimensionPixelOffset(int index, int defValue)516     public int getDimensionPixelOffset(int index, int defValue) {
517         if (mRecycled) {
518             throw new RuntimeException("Cannot make calls to a recycled instance!");
519         }
520 
521         index *= AssetManager.STYLE_NUM_ENTRIES;
522         final int[] data = mData;
523         final int type = data[index+AssetManager.STYLE_TYPE];
524         if (type == TypedValue.TYPE_NULL) {
525             return defValue;
526         } else if (type == TypedValue.TYPE_DIMENSION) {
527             return TypedValue.complexToDimensionPixelOffset(
528                 data[index+AssetManager.STYLE_DATA], mMetrics);
529         } else if (type == TypedValue.TYPE_ATTRIBUTE) {
530             throw new RuntimeException("Failed to resolve attribute at index " + index);
531         }
532 
533         throw new UnsupportedOperationException("Can't convert to dimension: type=0x"
534                 + Integer.toHexString(type));
535     }
536 
537     /**
538      * Retrieve a dimensional unit attribute at <var>index</var> for use
539      * as a size in raw pixels.  This is the same as
540      * {@link #getDimension}, except the returned value is converted to
541      * integer pixels for use as a size.  A size conversion involves
542      * rounding the base value, and ensuring that a non-zero base value
543      * is at least one pixel in size.
544      *
545      * @param index Index of attribute to retrieve.
546      * @param defValue Value to return if the attribute is not defined or
547      *                 not a resource.
548      *
549      * @return Attribute dimension value multiplied by the appropriate
550      * metric and truncated to integer pixels, or defValue if not defined.
551      *
552      * @see #getDimension
553      * @see #getDimensionPixelOffset
554      */
getDimensionPixelSize(int index, int defValue)555     public int getDimensionPixelSize(int index, int defValue) {
556         if (mRecycled) {
557             throw new RuntimeException("Cannot make calls to a recycled instance!");
558         }
559 
560         index *= AssetManager.STYLE_NUM_ENTRIES;
561         final int[] data = mData;
562         final int type = data[index+AssetManager.STYLE_TYPE];
563         if (type == TypedValue.TYPE_NULL) {
564             return defValue;
565         } else if (type == TypedValue.TYPE_DIMENSION) {
566             return TypedValue.complexToDimensionPixelSize(
567                 data[index+AssetManager.STYLE_DATA], mMetrics);
568         } else if (type == TypedValue.TYPE_ATTRIBUTE) {
569             throw new RuntimeException("Failed to resolve attribute at index " + index);
570         }
571 
572         throw new UnsupportedOperationException("Can't convert to dimension: type=0x"
573                 + Integer.toHexString(type));
574     }
575 
576     /**
577      * Special version of {@link #getDimensionPixelSize} for retrieving
578      * {@link android.view.ViewGroup}'s layout_width and layout_height
579      * attributes.  This is only here for performance reasons; applications
580      * should use {@link #getDimensionPixelSize}.
581      *
582      * @param index Index of the attribute to retrieve.
583      * @param name Textual name of attribute for error reporting.
584      *
585      * @return Attribute dimension value multiplied by the appropriate
586      * metric and truncated to integer pixels.
587      */
getLayoutDimension(int index, String name)588     public int getLayoutDimension(int index, String name) {
589         if (mRecycled) {
590             throw new RuntimeException("Cannot make calls to a recycled instance!");
591         }
592 
593         index *= AssetManager.STYLE_NUM_ENTRIES;
594         final int[] data = mData;
595         final int type = data[index+AssetManager.STYLE_TYPE];
596         if (type >= TypedValue.TYPE_FIRST_INT
597                 && type <= TypedValue.TYPE_LAST_INT) {
598             return data[index+AssetManager.STYLE_DATA];
599         } else if (type == TypedValue.TYPE_DIMENSION) {
600             return TypedValue.complexToDimensionPixelSize(
601                 data[index+AssetManager.STYLE_DATA], mMetrics);
602         } else if (type == TypedValue.TYPE_ATTRIBUTE) {
603             throw new RuntimeException("Failed to resolve attribute at index " + index);
604         }
605 
606         throw new RuntimeException(getPositionDescription()
607                 + ": You must supply a " + name + " attribute.");
608     }
609 
610     /**
611      * Special version of {@link #getDimensionPixelSize} for retrieving
612      * {@link android.view.ViewGroup}'s layout_width and layout_height
613      * attributes.  This is only here for performance reasons; applications
614      * should use {@link #getDimensionPixelSize}.
615      *
616      * @param index Index of the attribute to retrieve.
617      * @param defValue The default value to return if this attribute is not
618      * default or contains the wrong type of data.
619      *
620      * @return Attribute dimension value multiplied by the appropriate
621      * metric and truncated to integer pixels.
622      */
getLayoutDimension(int index, int defValue)623     public int getLayoutDimension(int index, int defValue) {
624         if (mRecycled) {
625             throw new RuntimeException("Cannot make calls to a recycled instance!");
626         }
627 
628         index *= AssetManager.STYLE_NUM_ENTRIES;
629         final int[] data = mData;
630         final int type = data[index+AssetManager.STYLE_TYPE];
631         if (type >= TypedValue.TYPE_FIRST_INT
632                 && type <= TypedValue.TYPE_LAST_INT) {
633             return data[index+AssetManager.STYLE_DATA];
634         } else if (type == TypedValue.TYPE_DIMENSION) {
635             return TypedValue.complexToDimensionPixelSize(
636                 data[index+AssetManager.STYLE_DATA], mMetrics);
637         }
638 
639         return defValue;
640     }
641 
642     /**
643      * Retrieve a fractional unit attribute at <var>index</var>.
644      *
645      * @param index Index of attribute to retrieve.
646      * @param base The base value of this fraction.  In other words, a
647      *             standard fraction is multiplied by this value.
648      * @param pbase The parent base value of this fraction.  In other
649      *             words, a parent fraction (nn%p) is multiplied by this
650      *             value.
651      * @param defValue Value to return if the attribute is not defined or
652      *                 not a resource.
653      *
654      * @return Attribute fractional value multiplied by the appropriate
655      * base value, or defValue if not defined.
656      */
getFraction(int index, int base, int pbase, float defValue)657     public float getFraction(int index, int base, int pbase, float defValue) {
658         if (mRecycled) {
659             throw new RuntimeException("Cannot make calls to a recycled instance!");
660         }
661 
662         index *= AssetManager.STYLE_NUM_ENTRIES;
663         final int[] data = mData;
664         final int type = data[index+AssetManager.STYLE_TYPE];
665         if (type == TypedValue.TYPE_NULL) {
666             return defValue;
667         } else if (type == TypedValue.TYPE_FRACTION) {
668             return TypedValue.complexToFraction(
669                 data[index+AssetManager.STYLE_DATA], base, pbase);
670         } else if (type == TypedValue.TYPE_ATTRIBUTE) {
671             throw new RuntimeException("Failed to resolve attribute at index " + index);
672         }
673 
674         throw new UnsupportedOperationException("Can't convert to fraction: type=0x"
675                 + Integer.toHexString(type));
676     }
677 
678     /**
679      * Retrieve the resource identifier for the attribute at
680      * <var>index</var>.  Note that attribute resource as resolved when
681      * the overall {@link TypedArray} object is retrieved.  As a
682      * result, this function will return the resource identifier of the
683      * final resource value that was found, <em>not</em> necessarily the
684      * original resource that was specified by the attribute.
685      *
686      * @param index Index of attribute to retrieve.
687      * @param defValue Value to return if the attribute is not defined or
688      *                 not a resource.
689      *
690      * @return Attribute resource identifier, or defValue if not defined.
691      */
getResourceId(int index, int defValue)692     public int getResourceId(int index, int defValue) {
693         if (mRecycled) {
694             throw new RuntimeException("Cannot make calls to a recycled instance!");
695         }
696 
697         index *= AssetManager.STYLE_NUM_ENTRIES;
698         final int[] data = mData;
699         if (data[index+AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) {
700             final int resid = data[index+AssetManager.STYLE_RESOURCE_ID];
701             if (resid != 0) {
702                 return resid;
703             }
704         }
705         return defValue;
706     }
707 
708     /**
709      * Retrieve the theme attribute resource identifier for the attribute at
710      * <var>index</var>.
711      *
712      * @param index Index of attribute to retrieve.
713      * @param defValue Value to return if the attribute is not defined or not a
714      *            resource.
715      * @return Theme attribute resource identifier, or defValue if not defined.
716      * @hide
717      */
getThemeAttributeId(int index, int defValue)718     public int getThemeAttributeId(int index, int defValue) {
719         if (mRecycled) {
720             throw new RuntimeException("Cannot make calls to a recycled instance!");
721         }
722 
723         index *= AssetManager.STYLE_NUM_ENTRIES;
724         final int[] data = mData;
725         if (data[index + AssetManager.STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) {
726             return data[index + AssetManager.STYLE_DATA];
727         }
728         return defValue;
729     }
730 
731     /**
732      * Retrieve the Drawable for the attribute at <var>index</var>.
733      *
734      * @param index Index of attribute to retrieve.
735      *
736      * @return Drawable for the attribute, or null if not defined.
737      */
738     @Nullable
getDrawable(int index)739     public Drawable getDrawable(int index) {
740         if (mRecycled) {
741             throw new RuntimeException("Cannot make calls to a recycled instance!");
742         }
743 
744         final TypedValue value = mValue;
745         if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
746             if (value.type == TypedValue.TYPE_ATTRIBUTE) {
747                 throw new RuntimeException("Failed to resolve attribute at index " + index);
748             }
749             return mResources.loadDrawable(value, value.resourceId, mTheme);
750         }
751         return null;
752     }
753 
754     /**
755      * Retrieve the CharSequence[] for the attribute at <var>index</var>.
756      * This gets the resource ID of the selected attribute, and uses
757      * {@link Resources#getTextArray Resources.getTextArray} of the owning
758      * Resources object to retrieve its String[].
759      *
760      * @param index Index of attribute to retrieve.
761      *
762      * @return CharSequence[] for the attribute, or null if not defined.
763      */
getTextArray(int index)764     public CharSequence[] getTextArray(int index) {
765         if (mRecycled) {
766             throw new RuntimeException("Cannot make calls to a recycled instance!");
767         }
768 
769         final TypedValue value = mValue;
770         if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
771             return mResources.getTextArray(value.resourceId);
772         }
773         return null;
774     }
775 
776     /**
777      * Retrieve the raw TypedValue for the attribute at <var>index</var>.
778      *
779      * @param index Index of attribute to retrieve.
780      * @param outValue TypedValue object in which to place the attribute's
781      *                 data.
782      *
783      * @return Returns true if the value was retrieved, else false.
784      */
getValue(int index, TypedValue outValue)785     public boolean getValue(int index, TypedValue outValue) {
786         if (mRecycled) {
787             throw new RuntimeException("Cannot make calls to a recycled instance!");
788         }
789 
790         return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue);
791     }
792 
793     /**
794      * Returns the type of attribute at the specified index.
795      *
796      * @param index Index of attribute whose type to retrieve.
797      * @return Attribute type.
798      */
getType(int index)799     public int getType(int index) {
800         if (mRecycled) {
801             throw new RuntimeException("Cannot make calls to a recycled instance!");
802         }
803 
804         index *= AssetManager.STYLE_NUM_ENTRIES;
805         return mData[index + AssetManager.STYLE_TYPE];
806     }
807 
808     /**
809      * Determines whether there is an attribute at <var>index</var>.
810      * <p>
811      * <strong>Note:</strong> If the attribute was set to {@code @empty} or
812      * {@code @undefined}, this method returns {@code false}.
813      *
814      * @param index Index of attribute to retrieve.
815      *
816      * @return True if the attribute has a value, false otherwise.
817      */
hasValue(int index)818     public boolean hasValue(int index) {
819         if (mRecycled) {
820             throw new RuntimeException("Cannot make calls to a recycled instance!");
821         }
822 
823         index *= AssetManager.STYLE_NUM_ENTRIES;
824         final int[] data = mData;
825         final int type = data[index+AssetManager.STYLE_TYPE];
826         return type != TypedValue.TYPE_NULL;
827     }
828 
829     /**
830      * Determines whether there is an attribute at <var>index</var>, returning
831      * {@code true} if the attribute was explicitly set to {@code @empty} and
832      * {@code false} only if the attribute was undefined.
833      *
834      * @param index Index of attribute to retrieve.
835      *
836      * @return True if the attribute has a value or is empty, false otherwise.
837      */
hasValueOrEmpty(int index)838     public boolean hasValueOrEmpty(int index) {
839         if (mRecycled) {
840             throw new RuntimeException("Cannot make calls to a recycled instance!");
841         }
842 
843         index *= AssetManager.STYLE_NUM_ENTRIES;
844         final int[] data = mData;
845         final int type = data[index+AssetManager.STYLE_TYPE];
846         return type != TypedValue.TYPE_NULL
847                 || data[index+AssetManager.STYLE_DATA] == TypedValue.DATA_NULL_EMPTY;
848     }
849 
850     /**
851      * Retrieve the raw TypedValue for the attribute at <var>index</var>
852      * and return a temporary object holding its data.  This object is only
853      * valid until the next call on to {@link TypedArray}.
854      *
855      * @param index Index of attribute to retrieve.
856      *
857      * @return Returns a TypedValue object if the attribute is defined,
858      *         containing its data; otherwise returns null.  (You will not
859      *         receive a TypedValue whose type is TYPE_NULL.)
860      */
peekValue(int index)861     public TypedValue peekValue(int index) {
862         if (mRecycled) {
863             throw new RuntimeException("Cannot make calls to a recycled instance!");
864         }
865 
866         final TypedValue value = mValue;
867         if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
868             return value;
869         }
870         return null;
871     }
872 
873     /**
874      * Returns a message about the parser state suitable for printing error messages.
875      */
getPositionDescription()876     public String getPositionDescription() {
877         if (mRecycled) {
878             throw new RuntimeException("Cannot make calls to a recycled instance!");
879         }
880 
881         return mXml != null ? mXml.getPositionDescription() : "<internal>";
882     }
883 
884     /**
885      * Recycle the TypedArray, to be re-used by a later caller. After calling
886      * this function you must not ever touch the typed array again.
887      */
recycle()888     public void recycle() {
889         if (mRecycled) {
890             throw new RuntimeException(toString() + " recycled twice!");
891         }
892 
893         mRecycled = true;
894 
895         // These may have been set by the client.
896         mXml = null;
897         mTheme = null;
898 
899         mResources.mTypedArrayPool.release(this);
900     }
901 
902     /**
903      * Extracts theme attributes from a typed array for later resolution using
904      * {@link android.content.res.Resources.Theme#resolveAttributes(int[], int[])}.
905      * Removes the entries from the typed array so that subsequent calls to typed
906      * getters will return the default value without crashing.
907      *
908      * @return an array of length {@link #getIndexCount()} populated with theme
909      *         attributes, or null if there are no theme attributes in the typed
910      *         array
911      * @hide
912      */
extractThemeAttrs()913     public int[] extractThemeAttrs() {
914         if (mRecycled) {
915             throw new RuntimeException("Cannot make calls to a recycled instance!");
916         }
917 
918         int[] attrs = null;
919 
920         final int[] data = mData;
921         final int N = length();
922         for (int i = 0; i < N; i++) {
923             final int index = i * AssetManager.STYLE_NUM_ENTRIES;
924             if (data[index + AssetManager.STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) {
925                 continue;
926             }
927 
928             // Null the entry so that we can safely call getZzz().
929             data[index + AssetManager.STYLE_TYPE] = TypedValue.TYPE_NULL;
930 
931             final int attr = data[index + AssetManager.STYLE_DATA];
932             if (attr == 0) {
933                 // This attribute is useless!
934                 continue;
935             }
936 
937             if (attrs == null) {
938                 attrs = new int[N];
939             }
940             attrs[i] = attr;
941         }
942 
943         return attrs;
944     }
945 
946     /**
947      * Return a mask of the configuration parameters for which the values in
948      * this typed array may change.
949      *
950      * @return Returns a mask of the changing configuration parameters, as
951      *         defined by {@link android.content.pm.ActivityInfo}.
952      * @see android.content.pm.ActivityInfo
953      */
getChangingConfigurations()954     public int getChangingConfigurations() {
955         int changingConfig = 0;
956 
957         final int[] data = mData;
958         final int N = length();
959         for (int i = 0; i < N; i++) {
960             final int index = i * AssetManager.STYLE_NUM_ENTRIES;
961             final int type = data[index + AssetManager.STYLE_TYPE];
962             if (type == TypedValue.TYPE_NULL) {
963                 continue;
964             }
965             changingConfig |= data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS];
966         }
967         return changingConfig;
968     }
969 
getValueAt(int index, TypedValue outValue)970     private boolean getValueAt(int index, TypedValue outValue) {
971         final int[] data = mData;
972         final int type = data[index+AssetManager.STYLE_TYPE];
973         if (type == TypedValue.TYPE_NULL) {
974             return false;
975         }
976         outValue.type = type;
977         outValue.data = data[index+AssetManager.STYLE_DATA];
978         outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
979         outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID];
980         outValue.changingConfigurations = data[index+AssetManager.STYLE_CHANGING_CONFIGURATIONS];
981         outValue.density = data[index+AssetManager.STYLE_DENSITY];
982         outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null;
983         return true;
984     }
985 
loadStringValueAt(int index)986     private CharSequence loadStringValueAt(int index) {
987         final int[] data = mData;
988         final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
989         if (cookie < 0) {
990             if (mXml != null) {
991                 return mXml.getPooledString(
992                     data[index+AssetManager.STYLE_DATA]);
993             }
994             return null;
995         }
996         return mAssets.getPooledStringForCookie(cookie, data[index+AssetManager.STYLE_DATA]);
997     }
998 
TypedArray(Resources resources, int[] data, int[] indices, int len)999     /*package*/ TypedArray(Resources resources, int[] data, int[] indices, int len) {
1000         mResources = resources;
1001         mMetrics = mResources.mMetrics;
1002         mAssets = mResources.mAssets;
1003         mData = data;
1004         mIndices = indices;
1005         mLength = len;
1006     }
1007 
1008     @Override
toString()1009     public String toString() {
1010         return Arrays.toString(mData);
1011     }
1012 }
1013