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