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