1a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki/*
2a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki * Copyright (C) 2015 The Android Open Source Project
3a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki *
4a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki * Licensed under the Apache License, Version 2.0 (the "License");
5a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki * you may not use this file except in compliance with the License.
6a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki * You may obtain a copy of the License at
7a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki *
8a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki *      http://www.apache.org/licenses/LICENSE-2.0
9a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki *
10a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki * Unless required by applicable law or agreed to in writing, software
11a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki * distributed under the License is distributed on an "AS IS" BASIS,
12a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki * See the License for the specific language governing permissions and
14a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki * limitations under the License.
15a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki */
16a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Arakipackage android.support.v4.content.res;
17a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki
18a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Arakiimport static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
19a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki
20a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Arakiimport android.content.Context;
21b6086751979cb14740815502597e9fcfddb7054aztenghuiimport android.content.res.Resources;
22a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Arakiimport android.content.res.TypedArray;
23a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Arakiimport android.graphics.drawable.Drawable;
24a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Arakiimport android.support.annotation.AnyRes;
25a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Arakiimport android.support.annotation.ColorInt;
26a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Arakiimport android.support.annotation.NonNull;
27a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Arakiimport android.support.annotation.RestrictTo;
28a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Arakiimport android.support.annotation.StyleableRes;
29b6086751979cb14740815502597e9fcfddb7054aztenghuiimport android.util.AttributeSet;
30a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Arakiimport android.util.TypedValue;
31a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki
32a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Arakiimport org.xmlpull.v1.XmlPullParser;
33a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki
34a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki/**
35a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki * Compat methods for accessing TypedArray values.
36a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki *
37b6086751979cb14740815502597e9fcfddb7054aztenghui * All the getNamed*() functions added the attribute name match, to take care of potential ID
38b6086751979cb14740815502597e9fcfddb7054aztenghui * collision between the private attributes in older OS version (OEM) and the attributes existed in
39b6086751979cb14740815502597e9fcfddb7054aztenghui * the newer OS version.
40b6086751979cb14740815502597e9fcfddb7054aztenghui * For example, if an private attribute named "abcdefg" in Kitkat has the
41b6086751979cb14740815502597e9fcfddb7054aztenghui * same id value as "android:pathData" in Lollipop, we need to match the attribute's namefirst.
42b6086751979cb14740815502597e9fcfddb7054aztenghui *
43a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki * @hide
44a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki */
45a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki@RestrictTo(LIBRARY_GROUP)
46a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Arakipublic class TypedArrayUtils {
47a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki
48a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    private static final String NAMESPACE = "http://schemas.android.com/apk/res/android";
49a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki
50a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    /**
51a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * @return Whether the current node ofthe  {@link XmlPullParser} has an attribute with the
52a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * specified {@code attrName}.
53a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     */
54a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    public static boolean hasAttribute(@NonNull XmlPullParser parser, @NonNull String attrName) {
55a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        return parser.getAttributeValue(NAMESPACE, attrName) != null;
56a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    }
57a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki
58a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    /**
59a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * Retrieves a float attribute value. In addition to the styleable resource ID, we also make
60a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * sure that the attribute name matches.
61a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     *
62a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * @return a float value in the {@link TypedArray} with the specified {@code resId}, or
63a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * {@code defaultValue} if it does not exist.
64a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     */
65a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    public static float getNamedFloat(@NonNull TypedArray a, @NonNull XmlPullParser parser,
66a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki            @NonNull String attrName, @StyleableRes int resId, float defaultValue) {
67a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        final boolean hasAttr = hasAttribute(parser, attrName);
68a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        if (!hasAttr) {
69a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki            return defaultValue;
70a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        } else {
71a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki            return a.getFloat(resId, defaultValue);
72a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        }
73a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    }
74a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki
75a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    /**
76a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * Retrieves a boolean attribute value. In addition to the styleable resource ID, we also make
77a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * sure that the attribute name matches.
78a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     *
79a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * @return a boolean value in the {@link TypedArray} with the specified {@code resId}, or
80a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * {@code defaultValue} if it does not exist.
81a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     */
82a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    public static boolean getNamedBoolean(@NonNull TypedArray a, @NonNull XmlPullParser parser,
83a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki            String attrName, @StyleableRes int resId, boolean defaultValue) {
84a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        final boolean hasAttr = hasAttribute(parser, attrName);
85a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        if (!hasAttr) {
86a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki            return defaultValue;
87a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        } else {
88a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki            return a.getBoolean(resId, defaultValue);
89a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        }
90a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    }
91a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki
92a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    /**
93a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * Retrieves an int attribute value. In addition to the styleable resource ID, we also make
94a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * sure that the attribute name matches.
95a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     *
96a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * @return an int value in the {@link TypedArray} with the specified {@code resId}, or
97a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * {@code defaultValue} if it does not exist.
98a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     */
998158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    public static int getNamedInt(@NonNull TypedArray a, @NonNull XmlPullParser parser,
1008158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            String attrName, @StyleableRes int resId, int defaultValue) {
101a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        final boolean hasAttr = hasAttribute(parser, attrName);
102a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        if (!hasAttr) {
103a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki            return defaultValue;
104a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        } else {
105a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki            return a.getInt(resId, defaultValue);
106a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        }
107a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    }
108a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki
109a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    /**
110a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * Retrieves a color attribute value. In addition to the styleable resource ID, we also make
111a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * sure that the attribute name matches.
112a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     *
113a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * @return a color value in the {@link TypedArray} with the specified {@code resId}, or
114a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * {@code defaultValue} if it does not exist.
115a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     */
116a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    @ColorInt
117a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    public static int getNamedColor(@NonNull TypedArray a, @NonNull XmlPullParser parser,
118a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki            String attrName, @StyleableRes int resId, @ColorInt int defaultValue) {
119a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        final boolean hasAttr = hasAttribute(parser, attrName);
120a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        if (!hasAttr) {
121a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki            return defaultValue;
122a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        } else {
123a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki            return a.getColor(resId, defaultValue);
124a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        }
125a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    }
126a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki
127a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    /**
1288158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     * Retrieves a resource ID attribute value. In addition to the styleable resource ID, we also
1298158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     * make sure that the attribute name matches.
1308158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     *
1318158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     * @return a resource ID value in the {@link TypedArray} with the specified {@code resId}, or
1328158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     * {@code defaultValue} if it does not exist.
1338158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     */
1348158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    @AnyRes
1358158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    public static int getNamedResourceId(@NonNull TypedArray a, @NonNull XmlPullParser parser,
1368158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            String attrName, @StyleableRes int resId, @AnyRes int defaultValue) {
1378158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        final boolean hasAttr = hasAttribute(parser, attrName);
1388158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        if (!hasAttr) {
1398158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            return defaultValue;
1408158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        } else {
1418158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            return a.getResourceId(resId, defaultValue);
1428158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        }
1438158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    }
1448158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
1458158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    /**
1468158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     * Retrieves a string attribute value. In addition to the styleable resource ID, we also
1478158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     * make sure that the attribute name matches.
1488158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     *
1498158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     * @return a string value in the {@link TypedArray} with the specified {@code resId}, or
1508158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     * null if it does not exist.
1518158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     */
1528158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    public static String getNamedString(@NonNull TypedArray a, @NonNull XmlPullParser parser,
1538158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            String attrName, @StyleableRes int resId) {
1548158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        final boolean hasAttr = hasAttribute(parser, attrName);
1558158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        if (!hasAttr) {
1568158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            return null;
1578158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        } else {
1588158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            return a.getString(resId);
1598158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        }
1608158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    }
1618158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
1628158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    /**
163b6086751979cb14740815502597e9fcfddb7054aztenghui     * Retrieve the raw TypedValue for the attribute at <var>index</var>
164b6086751979cb14740815502597e9fcfddb7054aztenghui     * and return a temporary object holding its data.  This object is only
165b6086751979cb14740815502597e9fcfddb7054aztenghui     * valid until the next call on to {@link TypedArray}.
166b6086751979cb14740815502597e9fcfddb7054aztenghui     */
167b6086751979cb14740815502597e9fcfddb7054aztenghui    public static TypedValue peekNamedValue(TypedArray a, XmlPullParser parser, String attrName,
168b6086751979cb14740815502597e9fcfddb7054aztenghui            int resId) {
169b6086751979cb14740815502597e9fcfddb7054aztenghui        final boolean hasAttr = hasAttribute(parser, attrName);
170b6086751979cb14740815502597e9fcfddb7054aztenghui        if (!hasAttr) {
171b6086751979cb14740815502597e9fcfddb7054aztenghui            return null;
172b6086751979cb14740815502597e9fcfddb7054aztenghui        } else {
173b6086751979cb14740815502597e9fcfddb7054aztenghui            return a.peekValue(resId);
174b6086751979cb14740815502597e9fcfddb7054aztenghui        }
175b6086751979cb14740815502597e9fcfddb7054aztenghui    }
176b6086751979cb14740815502597e9fcfddb7054aztenghui
177b6086751979cb14740815502597e9fcfddb7054aztenghui    /**
178b6086751979cb14740815502597e9fcfddb7054aztenghui     * Obtains styled attributes from the theme, if available, or unstyled
179b6086751979cb14740815502597e9fcfddb7054aztenghui     * resources if the theme is null.
180b6086751979cb14740815502597e9fcfddb7054aztenghui     */
181b6086751979cb14740815502597e9fcfddb7054aztenghui    public static TypedArray obtainAttributes(
182b6086751979cb14740815502597e9fcfddb7054aztenghui            Resources res, Resources.Theme theme, AttributeSet set, int[] attrs) {
183b6086751979cb14740815502597e9fcfddb7054aztenghui        if (theme == null) {
184b6086751979cb14740815502597e9fcfddb7054aztenghui            return res.obtainAttributes(set, attrs);
185b6086751979cb14740815502597e9fcfddb7054aztenghui        }
186b6086751979cb14740815502597e9fcfddb7054aztenghui        return theme.obtainStyledAttributes(set, attrs, 0, 0);
187b6086751979cb14740815502597e9fcfddb7054aztenghui    }
188b6086751979cb14740815502597e9fcfddb7054aztenghui
189b6086751979cb14740815502597e9fcfddb7054aztenghui    /**
190a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * @return a boolean value of {@code index}. If it does not exist, a boolean value of
191a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * {@code fallbackIndex}. If it still does not exist, {@code defaultValue}.
192a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     */
193a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    public static boolean getBoolean(TypedArray a, @StyleableRes int index,
194a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki            @StyleableRes int fallbackIndex, boolean defaultValue) {
195a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        boolean val = a.getBoolean(fallbackIndex, defaultValue);
196a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        return a.getBoolean(index, val);
197a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    }
198a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki
199a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    /**
200a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * @return a drawable value of {@code index}. If it does not exist, a drawable value of
201a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * {@code fallbackIndex}. If it still does not exist, {@code null}.
202a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     */
203a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    public static Drawable getDrawable(TypedArray a, @StyleableRes int index,
204a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki            @StyleableRes int fallbackIndex) {
205a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        Drawable val = a.getDrawable(index);
206a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        if (val == null) {
207a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki            val = a.getDrawable(fallbackIndex);
208a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        }
209a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        return val;
210a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    }
211a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki
212a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    /**
213a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * @return an int value of {@code index}. If it does not exist, an int value of
214a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * {@code fallbackIndex}. If it still does not exist, {@code defaultValue}.
215a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     */
216a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    public static int getInt(TypedArray a, @StyleableRes int index,
217a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki            @StyleableRes int fallbackIndex, int defaultValue) {
218a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        int val = a.getInt(fallbackIndex, defaultValue);
219a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        return a.getInt(index, val);
220a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    }
221a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki
222a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    /**
223a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * @return a resource ID value of {@code index}. If it does not exist, a resource ID value of
224a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * {@code fallbackIndex}. If it still does not exist, {@code defaultValue}.
225a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     */
226a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    @AnyRes
227a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    public static int getResourceId(TypedArray a, @StyleableRes int index,
228a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki            @StyleableRes int fallbackIndex, @AnyRes int defaultValue) {
229a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        int val = a.getResourceId(fallbackIndex, defaultValue);
230a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        return a.getResourceId(index, val);
231a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    }
232a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki
233a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    /**
234a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * @return a string value of {@code index}. If it does not exist, a string value of
235a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * {@code fallbackIndex}. If it still does not exist, {@code null}.
236a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     */
237a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    public static String getString(TypedArray a, @StyleableRes int index,
238a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki            @StyleableRes int fallbackIndex) {
239a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        String val = a.getString(index);
240a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        if (val == null) {
241a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki            val = a.getString(fallbackIndex);
242a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        }
243a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        return val;
244a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    }
245a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki
246a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    /**
247a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * Retrieves a text attribute value with the specified fallback ID.
248a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     *
249a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * @return a text value of {@code index}. If it does not exist, a text value of
250a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * {@code fallbackIndex}. If it still does not exist, {@code null}.
251a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     */
252a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    public static CharSequence getText(TypedArray a, @StyleableRes int index,
253a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki            @StyleableRes int fallbackIndex) {
254a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        CharSequence val = a.getText(index);
255a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        if (val == null) {
256a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki            val = a.getText(fallbackIndex);
257a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        }
258a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        return val;
259a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    }
260a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki
261a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    /**
262a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * Retrieves a string array attribute value with the specified fallback ID.
263a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     *
264a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * @return a string array value of {@code index}. If it does not exist, a string array value
265a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * of {@code fallbackIndex}. If it still does not exist, {@code null}.
266a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     */
267a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    public static CharSequence[] getTextArray(TypedArray a, @StyleableRes int index,
268a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki            @StyleableRes int fallbackIndex) {
269a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        CharSequence[] val = a.getTextArray(index);
270a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        if (val == null) {
271a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki            val = a.getTextArray(fallbackIndex);
272a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        }
273a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        return val;
274a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    }
275a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki
276a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    /**
277a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * @return The resource ID value in the {@code context} specified by {@code attr}. If it does
278a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     * not exist, {@code fallbackAttr}.
279a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki     */
280a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    public static int getAttr(Context context, int attr, int fallbackAttr) {
281a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        TypedValue value = new TypedValue();
282a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        context.getTheme().resolveAttribute(attr, value, true);
283a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        if (value.resourceId != 0) {
284a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki            return attr;
285a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        }
286a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki        return fallbackAttr;
287a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki    }
288a80878dec1a3689e6e79566a9650de39c3291c01Yuichi Araki}
289