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