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 */ hasAttribute(@onNull XmlPullParser parser, @NonNull String attrName)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 */ getNamedFloat(@onNull TypedArray a, @NonNull XmlPullParser parser, @NonNull String attrName, @StyleableRes int resId, float defaultValue)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 */ getNamedBoolean(@onNull TypedArray a, @NonNull XmlPullParser parser, @NonNull String attrName, @StyleableRes int resId, boolean defaultValue)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 */ getNamedInt(@onNull TypedArray a, @NonNull XmlPullParser parser, @NonNull String attrName, @StyleableRes int resId, int defaultValue)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 getNamedColor(@onNull TypedArray a, @NonNull XmlPullParser parser, @NonNull String attrName, @StyleableRes int resId, @ColorInt int defaultValue)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 getNamedResourceId(@onNull TypedArray a, @NonNull XmlPullParser parser, @NonNull String attrName, @StyleableRes int resId, @AnyRes int defaultValue)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 getNamedString(@onNull TypedArray a, @NonNull XmlPullParser parser, @NonNull String attrName, @StyleableRes int resId)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 peekNamedValue(@onNull TypedArray a, @NonNull XmlPullParser parser, @NonNull String attrName, int resId)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 obtainAttributes(@onNull Resources res, @Nullable Resources.Theme theme, @NonNull AttributeSet set, @NonNull int[] attrs)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 */ getBoolean(@onNull TypedArray a, @StyleableRes int index, @StyleableRes int fallbackIndex, boolean defaultValue)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 getDrawable(@onNull TypedArray a, @StyleableRes int index, @StyleableRes int fallbackIndex)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 */ getInt(@onNull TypedArray a, @StyleableRes int index, @StyleableRes int fallbackIndex, int defaultValue)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 getResourceId(@onNull TypedArray a, @StyleableRes int index, @StyleableRes int fallbackIndex, @AnyRes int defaultValue)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 getString(@onNull TypedArray a, @StyleableRes int index, @StyleableRes int fallbackIndex)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 getText(@onNull TypedArray a, @StyleableRes int index, @StyleableRes int fallbackIndex)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 getTextArray(@onNull TypedArray a, @StyleableRes int index, @StyleableRes int fallbackIndex)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 */ getAttr(@onNull Context context, int attr, int fallbackAttr)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 TypedArrayUtils()298 private TypedArrayUtils() { 299 } 300 } 301