blob: 0bb2498db63c903f6adb57e23f6208df0919e145 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.core.content.res;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.TypedValue;
import androidx.annotation.AnyRes;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.StyleableRes;
import org.xmlpull.v1.XmlPullParser;
/**
* Compat methods for accessing TypedArray values.
*
* All the getNamed*() functions added the attribute name match, to take care of potential ID
* collision between the private attributes in older OS version (OEM) and the attributes existed in
* the newer OS version.
* For example, if an private attribute named "abcdefg" in Kitkat has the
* same id value as "android:pathData" in Lollipop, we need to match the attribute's namefirst.
*
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
public class TypedArrayUtils {
private static final String NAMESPACE = "http://schemas.android.com/apk/res/android";
/**
* @return Whether the current node ofthe {@link XmlPullParser} has an attribute with the
* specified {@code attrName}.
*/
public static boolean hasAttribute(@NonNull XmlPullParser parser, @NonNull String attrName) {
return parser.getAttributeValue(NAMESPACE, attrName) != null;
}
/**
* Retrieves a float attribute value. In addition to the styleable resource ID, we also make
* sure that the attribute name matches.
*
* @return a float value in the {@link TypedArray} with the specified {@code resId}, or
* {@code defaultValue} if it does not exist.
*/
public static float getNamedFloat(@NonNull TypedArray a, @NonNull XmlPullParser parser,
@NonNull String attrName, @StyleableRes int resId, float defaultValue) {
final boolean hasAttr = hasAttribute(parser, attrName);
if (!hasAttr) {
return defaultValue;
} else {
return a.getFloat(resId, defaultValue);
}
}
/**
* Retrieves a boolean attribute value. In addition to the styleable resource ID, we also make
* sure that the attribute name matches.
*
* @return a boolean value in the {@link TypedArray} with the specified {@code resId}, or
* {@code defaultValue} if it does not exist.
*/
public static boolean getNamedBoolean(@NonNull TypedArray a, @NonNull XmlPullParser parser,
@NonNull String attrName, @StyleableRes int resId, boolean defaultValue) {
final boolean hasAttr = hasAttribute(parser, attrName);
if (!hasAttr) {
return defaultValue;
} else {
return a.getBoolean(resId, defaultValue);
}
}
/**
* Retrieves an int attribute value. In addition to the styleable resource ID, we also make
* sure that the attribute name matches.
*
* @return an int value in the {@link TypedArray} with the specified {@code resId}, or
* {@code defaultValue} if it does not exist.
*/
public static int getNamedInt(@NonNull TypedArray a, @NonNull XmlPullParser parser,
@NonNull String attrName, @StyleableRes int resId, int defaultValue) {
final boolean hasAttr = hasAttribute(parser, attrName);
if (!hasAttr) {
return defaultValue;
} else {
return a.getInt(resId, defaultValue);
}
}
/**
* Retrieves a color attribute value. In addition to the styleable resource ID, we also make
* sure that the attribute name matches.
*
* @return a color value in the {@link TypedArray} with the specified {@code resId}, or
* {@code defaultValue} if it does not exist.
*/
@ColorInt
public static int getNamedColor(@NonNull TypedArray a, @NonNull XmlPullParser parser,
@NonNull String attrName, @StyleableRes int resId, @ColorInt int defaultValue) {
final boolean hasAttr = hasAttribute(parser, attrName);
if (!hasAttr) {
return defaultValue;
} else {
return a.getColor(resId, defaultValue);
}
}
/**
* Retrieves a resource ID attribute value. In addition to the styleable resource ID, we also
* make sure that the attribute name matches.
*
* @return a resource ID value in the {@link TypedArray} with the specified {@code resId}, or
* {@code defaultValue} if it does not exist.
*/
@AnyRes
public static int getNamedResourceId(@NonNull TypedArray a, @NonNull XmlPullParser parser,
@NonNull String attrName, @StyleableRes int resId, @AnyRes int defaultValue) {
final boolean hasAttr = hasAttribute(parser, attrName);
if (!hasAttr) {
return defaultValue;
} else {
return a.getResourceId(resId, defaultValue);
}
}
/**
* Retrieves a string attribute value. In addition to the styleable resource ID, we also
* make sure that the attribute name matches.
*
* @return a string value in the {@link TypedArray} with the specified {@code resId}, or
* null if it does not exist.
*/
@Nullable
public static String getNamedString(@NonNull TypedArray a, @NonNull XmlPullParser parser,
@NonNull String attrName, @StyleableRes int resId) {
final boolean hasAttr = hasAttribute(parser, attrName);
if (!hasAttr) {
return null;
} else {
return a.getString(resId);
}
}
/**
* Retrieve the raw TypedValue for the attribute at <var>index</var>
* and return a temporary object holding its data. This object is only
* valid until the next call on to {@link TypedArray}.
*/
@Nullable
public static TypedValue peekNamedValue(@NonNull TypedArray a, @NonNull XmlPullParser parser,
@NonNull String attrName, int resId) {
final boolean hasAttr = hasAttribute(parser, attrName);
if (!hasAttr) {
return null;
} else {
return a.peekValue(resId);
}
}
/**
* Obtains styled attributes from the theme, if available, or unstyled
* resources if the theme is null.
*/
@NonNull
public static TypedArray obtainAttributes(@NonNull Resources res,
@Nullable Resources.Theme theme, @NonNull AttributeSet set, @NonNull int[] attrs) {
if (theme == null) {
return res.obtainAttributes(set, attrs);
}
return theme.obtainStyledAttributes(set, attrs, 0, 0);
}
/**
* @return a boolean value of {@code index}. If it does not exist, a boolean value of
* {@code fallbackIndex}. If it still does not exist, {@code defaultValue}.
*/
public static boolean getBoolean(@NonNull TypedArray a, @StyleableRes int index,
@StyleableRes int fallbackIndex, boolean defaultValue) {
boolean val = a.getBoolean(fallbackIndex, defaultValue);
return a.getBoolean(index, val);
}
/**
* @return a drawable value of {@code index}. If it does not exist, a drawable value of
* {@code fallbackIndex}. If it still does not exist, {@code null}.
*/
@Nullable
public static Drawable getDrawable(@NonNull TypedArray a, @StyleableRes int index,
@StyleableRes int fallbackIndex) {
Drawable val = a.getDrawable(index);
if (val == null) {
val = a.getDrawable(fallbackIndex);
}
return val;
}
/**
* @return an int value of {@code index}. If it does not exist, an int value of
* {@code fallbackIndex}. If it still does not exist, {@code defaultValue}.
*/
public static int getInt(@NonNull TypedArray a, @StyleableRes int index,
@StyleableRes int fallbackIndex, int defaultValue) {
int val = a.getInt(fallbackIndex, defaultValue);
return a.getInt(index, val);
}
/**
* @return a resource ID value of {@code index}. If it does not exist, a resource ID value of
* {@code fallbackIndex}. If it still does not exist, {@code defaultValue}.
*/
@AnyRes
public static int getResourceId(@NonNull TypedArray a, @StyleableRes int index,
@StyleableRes int fallbackIndex, @AnyRes int defaultValue) {
int val = a.getResourceId(fallbackIndex, defaultValue);
return a.getResourceId(index, val);
}
/**
* @return a string value of {@code index}. If it does not exist, a string value of
* {@code fallbackIndex}. If it still does not exist, {@code null}.
*/
@Nullable
public static String getString(@NonNull TypedArray a, @StyleableRes int index,
@StyleableRes int fallbackIndex) {
String val = a.getString(index);
if (val == null) {
val = a.getString(fallbackIndex);
}
return val;
}
/**
* Retrieves a text attribute value with the specified fallback ID.
*
* @return a text value of {@code index}. If it does not exist, a text value of
* {@code fallbackIndex}. If it still does not exist, {@code null}.
*/
@Nullable
public static CharSequence getText(@NonNull TypedArray a, @StyleableRes int index,
@StyleableRes int fallbackIndex) {
CharSequence val = a.getText(index);
if (val == null) {
val = a.getText(fallbackIndex);
}
return val;
}
/**
* Retrieves a string array attribute value with the specified fallback ID.
*
* @return a string array value of {@code index}. If it does not exist, a string array value
* of {@code fallbackIndex}. If it still does not exist, {@code null}.
*/
@Nullable
public static CharSequence[] getTextArray(@NonNull TypedArray a, @StyleableRes int index,
@StyleableRes int fallbackIndex) {
CharSequence[] val = a.getTextArray(index);
if (val == null) {
val = a.getTextArray(fallbackIndex);
}
return val;
}
/**
* @return The resource ID value in the {@code context} specified by {@code attr}. If it does
* not exist, {@code fallbackAttr}.
*/
public static int getAttr(@NonNull Context context, int attr, int fallbackAttr) {
TypedValue value = new TypedValue();
context.getTheme().resolveAttribute(attr, value, true);
if (value.resourceId != 0) {
return attr;
}
return fallbackAttr;
}
private TypedArrayUtils() {
}
}