Home | History | Annotate | Download | only in res
      1 /*
      2  * Copyright (C) 2015 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 package androidx.core.content.res;
     17 
     18 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
     19 
     20 import android.content.Context;
     21 import android.content.res.Resources;
     22 import android.content.res.TypedArray;
     23 import android.graphics.drawable.Drawable;
     24 import android.util.AttributeSet;
     25 import android.util.TypedValue;
     26 
     27 import androidx.annotation.AnyRes;
     28 import androidx.annotation.ColorInt;
     29 import androidx.annotation.NonNull;
     30 import androidx.annotation.Nullable;
     31 import androidx.annotation.RestrictTo;
     32 import androidx.annotation.StyleableRes;
     33 
     34 import org.xmlpull.v1.XmlPullParser;
     35 
     36 /**
     37  * Compat methods for accessing TypedArray values.
     38  *
     39  * All the getNamed*() functions added the attribute name match, to take care of potential ID
     40  * collision between the private attributes in older OS version (OEM) and the attributes existed in
     41  * the newer OS version.
     42  * For example, if an private attribute named "abcdefg" in Kitkat has the
     43  * same id value as "android:pathData" in Lollipop, we need to match the attribute's namefirst.
     44  *
     45  * @hide
     46  */
     47 @RestrictTo(LIBRARY_GROUP)
     48 public class TypedArrayUtils {
     49 
     50     private static final String NAMESPACE = "http://schemas.android.com/apk/res/android";
     51 
     52     /**
     53      * @return Whether the current node ofthe  {@link XmlPullParser} has an attribute with the
     54      * specified {@code attrName}.
     55      */
     56     public static boolean hasAttribute(@NonNull XmlPullParser parser, @NonNull String attrName) {
     57         return parser.getAttributeValue(NAMESPACE, attrName) != null;
     58     }
     59 
     60     /**
     61      * Retrieves a float attribute value. In addition to the styleable resource ID, we also make
     62      * sure that the attribute name matches.
     63      *
     64      * @return a float value in the {@link TypedArray} with the specified {@code resId}, or
     65      * {@code defaultValue} if it does not exist.
     66      */
     67     public static float getNamedFloat(@NonNull TypedArray a, @NonNull XmlPullParser parser,
     68             @NonNull String attrName, @StyleableRes int resId, float defaultValue) {
     69         final boolean hasAttr = hasAttribute(parser, attrName);
     70         if (!hasAttr) {
     71             return defaultValue;
     72         } else {
     73             return a.getFloat(resId, defaultValue);
     74         }
     75     }
     76 
     77     /**
     78      * Retrieves a boolean attribute value. In addition to the styleable resource ID, we also make
     79      * sure that the attribute name matches.
     80      *
     81      * @return a boolean value in the {@link TypedArray} with the specified {@code resId}, or
     82      * {@code defaultValue} if it does not exist.
     83      */
     84     public static boolean getNamedBoolean(@NonNull TypedArray a, @NonNull XmlPullParser parser,
     85             @NonNull String attrName, @StyleableRes int resId, boolean defaultValue) {
     86         final boolean hasAttr = hasAttribute(parser, attrName);
     87         if (!hasAttr) {
     88             return defaultValue;
     89         } else {
     90             return a.getBoolean(resId, defaultValue);
     91         }
     92     }
     93 
     94     /**
     95      * Retrieves an int attribute value. In addition to the styleable resource ID, we also make
     96      * sure that the attribute name matches.
     97      *
     98      * @return an int value in the {@link TypedArray} with the specified {@code resId}, or
     99      * {@code defaultValue} if it does not exist.
    100      */
    101     public static int getNamedInt(@NonNull TypedArray a, @NonNull XmlPullParser parser,
    102             @NonNull String attrName, @StyleableRes int resId, int defaultValue) {
    103         final boolean hasAttr = hasAttribute(parser, attrName);
    104         if (!hasAttr) {
    105             return defaultValue;
    106         } else {
    107             return a.getInt(resId, defaultValue);
    108         }
    109     }
    110 
    111     /**
    112      * Retrieves a color attribute value. In addition to the styleable resource ID, we also make
    113      * sure that the attribute name matches.
    114      *
    115      * @return a color value in the {@link TypedArray} with the specified {@code resId}, or
    116      * {@code defaultValue} if it does not exist.
    117      */
    118     @ColorInt
    119     public static int getNamedColor(@NonNull TypedArray a, @NonNull XmlPullParser parser,
    120             @NonNull String attrName, @StyleableRes int resId, @ColorInt int defaultValue) {
    121         final boolean hasAttr = hasAttribute(parser, attrName);
    122         if (!hasAttr) {
    123             return defaultValue;
    124         } else {
    125             return a.getColor(resId, defaultValue);
    126         }
    127     }
    128 
    129     /**
    130      * Retrieves a resource ID attribute value. In addition to the styleable resource ID, we also
    131      * make sure that the attribute name matches.
    132      *
    133      * @return a resource ID value in the {@link TypedArray} with the specified {@code resId}, or
    134      * {@code defaultValue} if it does not exist.
    135      */
    136     @AnyRes
    137     public static int getNamedResourceId(@NonNull TypedArray a, @NonNull XmlPullParser parser,
    138             @NonNull String attrName, @StyleableRes int resId, @AnyRes int defaultValue) {
    139         final boolean hasAttr = hasAttribute(parser, attrName);
    140         if (!hasAttr) {
    141             return defaultValue;
    142         } else {
    143             return a.getResourceId(resId, defaultValue);
    144         }
    145     }
    146 
    147     /**
    148      * Retrieves a string attribute value. In addition to the styleable resource ID, we also
    149      * make sure that the attribute name matches.
    150      *
    151      * @return a string value in the {@link TypedArray} with the specified {@code resId}, or
    152      * null if it does not exist.
    153      */
    154     @Nullable
    155     public static String getNamedString(@NonNull TypedArray a, @NonNull XmlPullParser parser,
    156             @NonNull String attrName, @StyleableRes int resId) {
    157         final boolean hasAttr = hasAttribute(parser, attrName);
    158         if (!hasAttr) {
    159             return null;
    160         } else {
    161             return a.getString(resId);
    162         }
    163     }
    164 
    165     /**
    166      * Retrieve the raw TypedValue for the attribute at <var>index</var>
    167      * and return a temporary object holding its data.  This object is only
    168      * valid until the next call on to {@link TypedArray}.
    169      */
    170     @Nullable
    171     public static TypedValue peekNamedValue(@NonNull TypedArray a, @NonNull XmlPullParser parser,
    172             @NonNull String attrName, int resId) {
    173         final boolean hasAttr = hasAttribute(parser, attrName);
    174         if (!hasAttr) {
    175             return null;
    176         } else {
    177             return a.peekValue(resId);
    178         }
    179     }
    180 
    181     /**
    182      * Obtains styled attributes from the theme, if available, or unstyled
    183      * resources if the theme is null.
    184      */
    185     @NonNull
    186     public static TypedArray obtainAttributes(@NonNull Resources res,
    187             @Nullable Resources.Theme theme, @NonNull AttributeSet set, @NonNull int[] attrs) {
    188         if (theme == null) {
    189             return res.obtainAttributes(set, attrs);
    190         }
    191         return theme.obtainStyledAttributes(set, attrs, 0, 0);
    192     }
    193 
    194     /**
    195      * @return a boolean value of {@code index}. If it does not exist, a boolean value of
    196      * {@code fallbackIndex}. If it still does not exist, {@code defaultValue}.
    197      */
    198     public static boolean getBoolean(@NonNull TypedArray a, @StyleableRes int index,
    199             @StyleableRes int fallbackIndex, boolean defaultValue) {
    200         boolean val = a.getBoolean(fallbackIndex, defaultValue);
    201         return a.getBoolean(index, val);
    202     }
    203 
    204     /**
    205      * @return a drawable value of {@code index}. If it does not exist, a drawable value of
    206      * {@code fallbackIndex}. If it still does not exist, {@code null}.
    207      */
    208     @Nullable
    209     public static Drawable getDrawable(@NonNull TypedArray a, @StyleableRes int index,
    210             @StyleableRes int fallbackIndex) {
    211         Drawable val = a.getDrawable(index);
    212         if (val == null) {
    213             val = a.getDrawable(fallbackIndex);
    214         }
    215         return val;
    216     }
    217 
    218     /**
    219      * @return an int value of {@code index}. If it does not exist, an int value of
    220      * {@code fallbackIndex}. If it still does not exist, {@code defaultValue}.
    221      */
    222     public static int getInt(@NonNull TypedArray a, @StyleableRes int index,
    223             @StyleableRes int fallbackIndex, int defaultValue) {
    224         int val = a.getInt(fallbackIndex, defaultValue);
    225         return a.getInt(index, val);
    226     }
    227 
    228     /**
    229      * @return a resource ID value of {@code index}. If it does not exist, a resource ID value of
    230      * {@code fallbackIndex}. If it still does not exist, {@code defaultValue}.
    231      */
    232     @AnyRes
    233     public static int getResourceId(@NonNull TypedArray a, @StyleableRes int index,
    234             @StyleableRes int fallbackIndex, @AnyRes int defaultValue) {
    235         int val = a.getResourceId(fallbackIndex, defaultValue);
    236         return a.getResourceId(index, val);
    237     }
    238 
    239     /**
    240      * @return a string value of {@code index}. If it does not exist, a string value of
    241      * {@code fallbackIndex}. If it still does not exist, {@code null}.
    242      */
    243     @Nullable
    244     public static String getString(@NonNull TypedArray a, @StyleableRes int index,
    245             @StyleableRes int fallbackIndex) {
    246         String val = a.getString(index);
    247         if (val == null) {
    248             val = a.getString(fallbackIndex);
    249         }
    250         return val;
    251     }
    252 
    253     /**
    254      * Retrieves a text attribute value with the specified fallback ID.
    255      *
    256      * @return a text value of {@code index}. If it does not exist, a text value of
    257      * {@code fallbackIndex}. If it still does not exist, {@code null}.
    258      */
    259     @Nullable
    260     public static CharSequence getText(@NonNull TypedArray a, @StyleableRes int index,
    261             @StyleableRes int fallbackIndex) {
    262         CharSequence val = a.getText(index);
    263         if (val == null) {
    264             val = a.getText(fallbackIndex);
    265         }
    266         return val;
    267     }
    268 
    269     /**
    270      * Retrieves a string array attribute value with the specified fallback ID.
    271      *
    272      * @return a string array value of {@code index}. If it does not exist, a string array value
    273      * of {@code fallbackIndex}. If it still does not exist, {@code null}.
    274      */
    275     @Nullable
    276     public static CharSequence[] getTextArray(@NonNull TypedArray a, @StyleableRes int index,
    277             @StyleableRes int fallbackIndex) {
    278         CharSequence[] val = a.getTextArray(index);
    279         if (val == null) {
    280             val = a.getTextArray(fallbackIndex);
    281         }
    282         return val;
    283     }
    284 
    285     /**
    286      * @return The resource ID value in the {@code context} specified by {@code attr}. If it does
    287      * not exist, {@code fallbackAttr}.
    288      */
    289     public static int getAttr(@NonNull Context context, int attr, int fallbackAttr) {
    290         TypedValue value = new TypedValue();
    291         context.getTheme().resolveAttribute(attr, value, true);
    292         if (value.resourceId != 0) {
    293             return attr;
    294         }
    295         return fallbackAttr;
    296     }
    297 
    298     private TypedArrayUtils() {
    299     }
    300 }
    301