blob: 4c32da6d21f035508a48931401f437132152aacb [file] [log] [blame]
package org.robolectric.shadows;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.os.ParcelFileDescriptor;
import android.util.AttributeSet;
import android.util.TypedValue;
import org.jetbrains.annotations.NotNull;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.HiddenApi;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
import org.robolectric.res.Attribute;
import org.robolectric.res.DrawableNode;
import org.robolectric.res.DrawableResourceLoader;
import org.robolectric.res.FsFile;
import org.robolectric.res.ResName;
import org.robolectric.res.ResType;
import org.robolectric.res.ResourceIndex;
import org.robolectric.res.ResourceLoader;
import org.robolectric.res.Style;
import org.robolectric.res.StyleData;
import org.robolectric.res.TypedResource;
import org.robolectric.res.builder.ResourceParser;
import org.robolectric.util.Strings;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import static org.robolectric.Shadows.shadowOf;
import static org.robolectric.shadows.ShadowApplication.getInstance;
/**
* Shadow for {@link android.content.res.AssetManager}.
*/
@Implements(AssetManager.class)
public final class ShadowAssetManager {
public static final int STYLE_NUM_ENTRIES = 6;
public static final int STYLE_TYPE = 0;
public static final int STYLE_DATA = 1;
public static final int STYLE_ASSET_COOKIE = 2;
public static final int STYLE_RESOURCE_ID = 3;
public static final int STYLE_CHANGING_CONFIGURATIONS = 4;
public static final int STYLE_DENSITY = 5;
private static ResourceLoader systemResources;
private Map<$ptrClassBoxed, Resources.Theme> themesById = new LinkedHashMap<>();
private Map<$ptrClassBoxed, List<OverlayedStyle>> appliedStyles = new HashMap<>();
private int nextInternalThemeId = 1000;
private ResourceLoader resourceLoader;
@RealObject
AssetManager realObject;
public static void setSystemResources(ResourceLoader systemResources) {
ShadowAssetManager.systemResources = systemResources;
}
@HiddenApi @Implementation
public CharSequence getResourceText(int ident) {
TypedResource value = getAndResolve(ident, getQualifiers(), true);
if (value == null) return null;
return (CharSequence) value.getData();
}
@HiddenApi @Implementation
public CharSequence getResourceBagText(int ident, int bagEntryId) {
throw new UnsupportedOperationException(); // todo
}
@HiddenApi @Implementation
public String[] getResourceStringArray(final int id) {
CharSequence[] resourceTextArray = getResourceTextArray(id);
if (resourceTextArray == null) return null;
String[] strings = new String[resourceTextArray.length];
for (int i = 0; i < strings.length; i++) {
strings[i] = resourceTextArray[i].toString();
}
return strings;
}
@HiddenApi @Implementation
public int getResourceIdentifier(String name, String defType, String defPackage) {
ResourceIndex resourceIndex = getResourceLoader().getResourceIndex();
ResName resName = ResName.qualifyResName(name, defPackage, defType);
Integer resourceId = resourceIndex.getResourceId(resName);
if (resourceId == null) return 0;
return resourceId;
}
@HiddenApi @Implementation
public boolean getResourceValue(int ident, int density, TypedValue outValue, boolean resolveRefs) {
TypedResource value = getAndResolve(ident, getQualifiers(), resolveRefs);
if (value == null) return false;
getConverter(value).fillTypedValue(value.getData(), outValue);
return true;
}
private Converter getConverter(TypedResource value) {
return Converter.getConverter(value.getResType());
}
@HiddenApi @Implementation
public CharSequence[] getResourceTextArray(final int id) {
ResName resName = getResourceLoader().getResourceIndex().getResName(id);
if (resName == null) throw new Resources.NotFoundException("unknown resource " + id);
TypedResource value = getAndResolve(resName, getQualifiers(), true);
if (value == null) return null;
TypedResource[] items = getConverter(value).getItems(value);
CharSequence[] charSequences = new CharSequence[items.length];
for (int i = 0; i < items.length; i++) {
TypedResource typedResource = resolve(items[i], getQualifiers(), resName);
charSequences[i] = getConverter(typedResource).asCharSequence(typedResource);
}
return charSequences;
}
@HiddenApi @Implementation
public boolean getThemeValue($ptrClass theme, int ident, TypedValue outValue, boolean resolveRefs) {
ResourceIndex resourceIndex = getResourceLoader().getResourceIndex();
ResName resName = resourceIndex.getResName(ident);
Resources.Theme theTheme = getThemeByInternalId(theme);
// Load the style for the theme we represent. E.g. "@style/Theme.Robolectric"
int styleResourceId = shadowOf(theTheme).getStyleResourceId();
ResName themeStyleName = resourceIndex.getResName(styleResourceId);
if (themeStyleName == null) return false; // is this right?
Style themeStyle = resolveStyle(resourceLoader, null, themeStyleName, getQualifiers());
//// Load the theme attribute for the default style attributes. E.g., attr/buttonStyle
//ResName defStyleName = getResourceLoader().getResourceIndex().getResName(ident);
//
//// Load the style for the default style attribute. E.g. "@style/Widget.Robolectric.Button";
//String defStyleNameValue = themeStyle.getAttrValue(defStyleName);
//ResName defStyleResName = new ResName(defStyleName.packageName, "style", defStyleName.name);
//Style style = resolveStyle(resourceLoader, defStyleResName, getQualifiers());
if (themeStyle != null) {
List<OverlayedStyle> overlayThemeStyles = getOverlayThemeStyles(styleResourceId);
Attribute attrValue = getOverlayedThemeValue(resName, themeStyle, overlayThemeStyles);
while(resolveRefs && attrValue != null && attrValue.isStyleReference()) {
ResName attrResName = new ResName(attrValue.contextPackageName, "attr", attrValue.value.substring(1));
attrValue = getOverlayedThemeValue(attrResName, themeStyle, overlayThemeStyles);
}
if (attrValue != null) {
Converter.convertAndFill(attrValue, outValue, resourceLoader, getQualifiers(), resolveRefs);
return true;
}
}
return false;
}
@HiddenApi @Implementation
public void ensureStringBlocks() {
}
@Implementation
public final InputStream open(String fileName) throws IOException {
return ShadowApplication.getInstance().getAppManifest().getAssetsDirectory().join(fileName).getInputStream();
}
@Implementation
public final InputStream open(String fileName, int accessMode) throws IOException {
return ShadowApplication.getInstance().getAppManifest().getAssetsDirectory().join(fileName).getInputStream();
}
@Implementation
public final AssetFileDescriptor openFd(String fileName) throws IOException {
File file = new File(ShadowApplication.getInstance().getAppManifest().getAssetsDirectory().join(fileName).getPath());
ParcelFileDescriptor parcelFileDescriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
return new AssetFileDescriptor(parcelFileDescriptor, 0, file.length());
}
@Implementation
public final String[] list(String path) throws IOException {
FsFile file = ShadowApplication.getInstance().getAppManifest().getAssetsDirectory().join(path);
if (file.isDirectory()) {
return file.listFileNames();
}
return new String[0];
}
@HiddenApi @Implementation
public final InputStream openNonAsset(int cookie, String fileName, int accessMode) throws IOException {
final ResName resName = qualifyFromNonAssetFileName(fileName);
final DrawableNode drawableNode = getResourceLoader().getDrawableNode(resName, getQualifiers());
if (drawableNode == null) {
throw new IOException("Unable to find resource for " + fileName);
}
if (accessMode == AssetManager.ACCESS_STREAMING) {
return drawableNode.getFsFile().getInputStream();
} else {
return new ByteArrayInputStream(drawableNode.getFsFile().getBytes());
}
}
private ResName qualifyFromNonAssetFileName(String fileName) {
if (fileName.startsWith("jar:")) {
// Must remove "jar:" prefix, or else qualifyFromFilePath fails on Windows
return ResName.qualifyFromFilePath("android", fileName.replaceFirst("jar:", ""));
} else {
return ResName.qualifyFromFilePath(ShadowApplication.getInstance().getAppManifest().getPackageName(), fileName);
}
}
@HiddenApi @Implementation
public final AssetFileDescriptor openNonAssetFd(int cookie, String fileName) throws IOException {
throw new UnsupportedOperationException();
}
@Implementation
public final XmlResourceParser openXmlResourceParser(int cookie, String fileName) throws IOException {
return ResourceParser.create(fileName, "fixme", "fixme", null);
}
@HiddenApi @Implementation
public int addAssetPath(String path) {
return 1;
}
@HiddenApi @Implementation
public boolean isUpToDate() {
return true;
}
@HiddenApi @Implementation
public void setLocale(String locale) {
}
@Implementation
public String[] getLocales() {
return new String[0]; // todo
}
@HiddenApi @Implementation
public void setConfiguration(int mcc, int mnc, String locale,
int orientation, int touchscreen, int density, int keyboard,
int keyboardHidden, int navigation, int screenWidth, int screenHeight,
int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp,
int screenLayout, int uiMode, int majorVersion) {
}
@HiddenApi @Implementation
public int[] getArrayIntResource(int arrayRes) {
ResName resName = getResourceLoader().getResourceIndex().getResName(arrayRes);
if (resName == null) throw new Resources.NotFoundException("unknown resource " + arrayRes);
TypedResource value = getAndResolve(resName, getQualifiers(), true);
if (value == null) return null;
TypedResource[] items = getConverter(value).getItems(value);
int[] ints = new int[items.length];
for (int i = 0; i < items.length; i++) {
TypedResource typedResource = resolve(items[i], getQualifiers(), resName);
ints[i] = getConverter(typedResource).asInt(typedResource);
}
return ints;
}
@HiddenApi @Implementation
synchronized public int createTheme() {
return nextInternalThemeId++;
}
@HiddenApi @Implementation
synchronized public void releaseTheme($ptrClass theme) {
themesById.remove(theme);
}
@HiddenApi @Implementation
public static void applyThemeStyle($ptrClass theme, int styleRes, boolean force) {
ResourceLoader resourceLoader = getInstance().getResourceLoader();
ShadowAssetManager assetManager = shadowOf(getInstance().getAssets());
final Map<$ptrClassBoxed, List<OverlayedStyle>> appliedStyles = assetManager.appliedStyles;
if (!appliedStyles.containsKey(theme)) {
appliedStyles.put(theme, new LinkedList<OverlayedStyle>());
}
ResourceIndex resourceIndex = resourceLoader.getResourceIndex();
ResName resName = resourceIndex.getResName(styleRes);
Style style = resolveStyle(resourceLoader, null, resName, assetManager.getQualifiers());
List<OverlayedStyle> overlayedStyleList = appliedStyles.get(theme);
OverlayedStyle styleToAdd = new OverlayedStyle(style, force);
for (int i = 0; i < overlayedStyleList.size(); ++i) {
if (styleToAdd.equals(overlayedStyleList.get(i))) {
overlayedStyleList.remove(i);
break;
}
}
overlayedStyleList.add(styleToAdd);
}
List<OverlayedStyle> getOverlayThemeStyles($ptrClass themeResourceId) {
return appliedStyles.get(themeResourceId);
}
static class OverlayedStyle {
Style style;
boolean force;
public OverlayedStyle(Style style, boolean force) {
this.style = style;
this.force = force;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof OverlayedStyle)) {
return false;
}
OverlayedStyle overlayedStyle = (OverlayedStyle) obj;
return style.equals(overlayedStyle.style);
}
@Override
public int hashCode() {
return style.hashCode();
}
}
@HiddenApi @Implementation
public static void copyTheme($ptrClass dest, long source) {
throw new UnsupportedOperationException();
}
/////////////////////////
synchronized public void setTheme($ptrClass internalThemeId, Resources.Theme theme) {
themesById.put(internalThemeId, theme);
}
synchronized private Resources.Theme getThemeByInternalId($ptrClass internalThemeId) {
return themesById.get(internalThemeId);
}
static Style resolveStyle(ResourceLoader resourceLoader, Style appTheme, @NotNull ResName themeStyleName, String qualifiers) {
TypedResource themeStyleResource = resourceLoader.getValue(themeStyleName, qualifiers);
if (themeStyleResource == null) return null;
StyleData themeStyleData = (StyleData) themeStyleResource.getData();
return new StyleResolver(resourceLoader, themeStyleData, appTheme, themeStyleName, qualifiers);
}
TypedResource getAndResolve(int resId, String qualifiers, boolean resolveRefs) {
ResName resName = getResourceLoader().getResourceIndex().getResName(resId);
if (resName == null) throw new Resources.NotFoundException("unknown resource " + resId);
return getAndResolve(resName, qualifiers, resolveRefs);
}
TypedResource getAndResolve(@NotNull ResName resName, String qualifiers, boolean resolveRefs) {
TypedResource value = getResourceLoader().getValue(resName, qualifiers);
if (resolveRefs) {
value = resolve(value, qualifiers, resName);
}
// todo: make the drawable loader put stuff into the normal spot...
if (value == null && DrawableResourceLoader.isStillHandledHere(resName)) {
DrawableNode drawableNode = getResourceLoader().getDrawableNode(resName, qualifiers);
return new TypedResource<FsFile>(drawableNode.getFsFile(), ResType.FILE);
}
// todo: gross. this is so resources.getString(R.layout.foo) works for ABS.
if (value == null && "layout".equals(resName.type)) {
throw new UnsupportedOperationException("ugh, this doesn't work still?");
}
return value;
}
TypedResource resolve(TypedResource value, String qualifiers, ResName contextResName) {
return resolveResource(value, qualifiers, contextResName).value;
}
ResName resolveResName(int resId, String qualifiers) {
ResName resName = getResourceLoader().getResourceIndex().getResName(resId);
if (resName == null) return null;
TypedResource value = getResourceLoader().getValue(resName, qualifiers);
return resolveResource(value, qualifiers, resName).resName;
}
private Resource resolveResource(TypedResource value, String qualifiers, ResName resName) {
while (isReference(value)) {
String s = value.asString();
if (s.equals("@null") || s.equals("@empty")) {
value = null;
} else {
String refStr = s.substring(1).replace("+", "");
resName = ResName.qualifyResName(refStr, resName);
value = getResourceLoader().getValue(resName, qualifiers);
}
}
return new Resource(value, resName);
}
ResourceLoader getResourceLoader() {
if (resourceLoader == null) {
resourceLoader = ShadowApplication.getInstance().getResourceLoader();
}
return resourceLoader;
}
private boolean isReference(TypedResource value) {
if (value != null) {
Object data = value.getData();
if (data instanceof String) {
String s = (String) data;
return !s.isEmpty() && s.charAt(0) == '@';
}
}
return false;
}
public FsFile getAssetsDirectory() {
return ShadowApplication.getInstance().getAppManifest().getAssetsDirectory();
}
public String getQualifiers() {
return RuntimeEnvironment.getQualifiers();
}
public void setQualifiers(String qualifiers) {
RuntimeEnvironment.setQualifiers(qualifiers);
}
private static class Resource {
public final ResName resName;
public final TypedResource<?> value;
public Resource(TypedResource<?> value, ResName resName) {
this.value = value;
this.resName = resName;
}
}
static class StyleResolver implements Style {
private final ResourceLoader resourceLoader;
private final List<StyleData> styles = new ArrayList<>();
private final Style theme;
private final ResName myResName;
private final String qualifiers;
public StyleResolver(ResourceLoader resourceLoader, StyleData styleData,
Style theme, ResName myResName, String qualifiers) {
this.resourceLoader = resourceLoader;
this.theme = theme;
this.myResName = myResName;
this.qualifiers = qualifiers;
styles.add(styleData);
}
@Override public Attribute getAttrValue(ResName resName) {
for (StyleData style : styles) {
Attribute value = style.getAttrValue(resName);
if (value != null) return value;
}
int initialSize = styles.size();
while (hasParent(styles.get(styles.size() - 1))) {
StyleData parent = getParent(styles.get(styles.size() - 1));
if (parent != null) {
styles.add(parent);
} else {
break;
}
}
for (int i = initialSize; i < styles.size(); i++) {
StyleData style = styles.get(i);
Attribute value = style.getAttrValue(resName);
if (value != null) return value;
}
if (theme != null) {
Attribute value = theme.getAttrValue(resName);
if (value != null) return value;
}
return null;
}
private static String getParentStyleName(StyleData style) {
if (style == null) {
return null;
}
String parent = style.getParent();
if (parent == null || parent.isEmpty()) {
parent = null;
String name = style.getName();
if (name.contains(".")) {
parent = name.substring(0, name.lastIndexOf('.'));
if (parent.isEmpty()) {
return null;
}
}
}
return parent;
}
private static boolean hasParent(StyleData style) {
if (style == null) return false;
String parent = style.getParent();
return parent != null && !parent.isEmpty();
}
private StyleData getParent(StyleData style) {
String parent = getParentStyleName(style);
if (parent == null) return null;
if (parent.startsWith("@")) parent = parent.substring(1);
ResName styleRef = ResName.qualifyResName(parent, style.getPackageName(), "style");
styleRef = dereferenceResName(styleRef);
TypedResource typedResource = resourceLoader.getValue(styleRef, qualifiers);
if (typedResource == null) {
StringBuilder builder = new StringBuilder("Could not find any resource ")
.append(" from reference ").append(styleRef)
.append(" from style ").append(style)
.append(" with theme ").append(theme);
throw new RuntimeException(builder.toString());
}
Object data = typedResource.getData();
if (data instanceof StyleData) {
return (StyleData) data;
} else {
StringBuilder builder = new StringBuilder(styleRef.toString())
.append(" does not resolve to a Style.")
.append(" got ").append(data).append(" instead. ")
.append(" from style ").append(style)
.append(" with theme ").append(theme);
throw new RuntimeException(builder.toString());
}
}
private ResName dereferenceResName(ResName res) {
ResName styleRef = res;
boolean dereferencing = true;
while ("attr".equals(styleRef.type) && dereferencing) {
dereferencing = false;
for (StyleData parentStyle : styles) {
Attribute value = parentStyle.getAttrValue(styleRef);
if (value != null) {
styleRef = dereferenceAttr(value);
dereferencing = true;
break;
}
}
if (!dereferencing && theme != null) {
Attribute value = theme.getAttrValue(styleRef);
if (value != null) {
styleRef = dereferenceAttr(value);
dereferencing = true;
}
}
}
return styleRef;
}
private ResName dereferenceAttr(Attribute attr) {
if (attr.isResourceReference()) {
return attr.getResourceReference();
} else if (attr.isStyleReference()) {
return attr.getStyleReference();
}
throw new RuntimeException("Found a " + attr + " but can't cast it :(");
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof StyleResolver)) {
return false;
}
StyleResolver other = (StyleResolver) obj;
return ((theme == null && other.theme == null) || (theme != null && theme.equals(other.theme)))
&& ((myResName == null && other.myResName == null)
|| (myResName != null && myResName.equals(other.myResName)))
&& Strings.equals(qualifiers, other.qualifiers);
}
@Override
public int hashCode() {
int hashCode = 0;
hashCode = 31 * hashCode + (theme != null ? theme.hashCode() : 0);
hashCode = 31 * hashCode + (myResName != null ? myResName.hashCode() : 0);
hashCode = 31 * hashCode + Strings.nullToEmpty(qualifiers).hashCode();
return hashCode;
}
@Override
public String toString() {
return "StyleResolver{"
+ "name=" + myResName
+ ", of=" + styles.get(0)
+ "}";
}
}
@Implementation
public static AssetManager getSystem() {
AssetManager assetManager = new AssetManager();
ShadowAssetManager shadowAssetManager = shadowOf(assetManager);
shadowAssetManager.resourceLoader = systemResources;
return assetManager;
}
List<Attribute> buildAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int themeResourceId, int defStyleRes) {
/*
* When determining the final value of a particular attribute, there are four inputs that come into play:
*
* 1. Any attribute values in the given AttributeSet.
* 2. The style resource specified in the AttributeSet (named "style").
* 3. The default style specified by defStyleAttr and defStyleRes
* 4. The base values in this theme.
*/
ResourceLoader resourceLoader = getResourceLoader();
Style defStyleFromAttr = null;
Style defStyleFromRes = null;
Style styleAttrStyle = null;
Style theme = null;
List<ShadowAssetManager.OverlayedStyle> overlayedStyles = getOverlayThemeStyles(themeResourceId);
if (themeResourceId != 0) {
// Load the style for the theme we represent. E.g. "@style/Theme.Robolectric"
ResName themeStyleName = getResName(themeResourceId);
theme = ShadowAssetManager.resolveStyle(resourceLoader, null, themeStyleName, getQualifiers());
if (defStyleAttr != 0) {
// Load the theme attribute for the default style attributes. E.g., attr/buttonStyle
ResName defStyleName = getResName(defStyleAttr);
// Load the style for the default style attribute. E.g. "@style/Widget.Robolectric.Button";
Attribute defStyleAttribute = getOverlayedThemeValue(defStyleName, theme, overlayedStyles);
if (defStyleAttribute != null) {
while (defStyleAttribute.isStyleReference()) {
Attribute other = getOverlayedThemeValue(defStyleAttribute.getStyleReference(), theme, overlayedStyles);
if (other == null) {
throw new RuntimeException("couldn't dereference " + defStyleAttribute);
}
defStyleAttribute = other;
}
if (defStyleAttribute.isResourceReference()) {
ResName defStyleResName = defStyleAttribute.getResourceReference();
defStyleFromAttr = ShadowAssetManager.resolveStyle(resourceLoader, theme, defStyleResName, getQualifiers());
}
}
}
}
int styleAttrResId = set.getStyleAttribute();
if (styleAttrResId != 0) {
ResName styleAttributeResName = getResName(styleAttrResId);
while (styleAttributeResName.type.equals("attr")) {
Attribute attrValue = getOverlayedThemeValue(styleAttributeResName, theme, overlayedStyles);
if (attrValue.isResourceReference()) {
styleAttributeResName = attrValue.getResourceReference();
} else if (attrValue.isStyleReference()) {
styleAttributeResName = attrValue.getStyleReference();
}
}
styleAttrStyle = ShadowAssetManager.resolveStyle(resourceLoader, theme, styleAttributeResName, getQualifiers());
}
if (defStyleRes != 0) {
ResName resName = getResName(defStyleRes);
if (resName.type.equals("attr")) {
Attribute attributeValue = findAttributeValue(getResName(defStyleRes), set, styleAttrStyle, defStyleFromAttr, defStyleFromAttr, theme, overlayedStyles);
if (attributeValue != null) {
if (attributeValue.isStyleReference()) {
resName = getOverlayedThemeValue(attributeValue.getStyleReference(), theme, overlayedStyles).getResourceReference();
} else if (attributeValue.isResourceReference()) {
resName = attributeValue.getResourceReference();
}
}
}
defStyleFromRes = ShadowAssetManager.resolveStyle(resourceLoader, theme, resName, getQualifiers());
}
List<Attribute> attributes = new ArrayList<>();
if (attrs == null) attrs = new int[0];
for (int attr : attrs) {
ResName attrName = tryResName(attr); // todo probably getResName instead here?
if (attrName == null) continue;
Attribute attribute = findAttributeValue(attrName, set, styleAttrStyle, defStyleFromAttr, defStyleFromRes, theme, overlayedStyles);
while (attribute != null && attribute.isStyleReference()) {
ResName otherAttrName = attribute.getStyleReference();
// TODO: this is just a debugging hack to avoid the problem of Resources.loadDrawableForCookie not working.
// TODO: We need to address the real problem instead, but are putting it off for a day or two -AV, ED 2014-12-03
if (theme == null) break;
attribute = getOverlayedThemeValue(otherAttrName, theme, overlayedStyles);
if (attribute != null) {
attribute = new Attribute(attrName, attribute.value, attribute.contextPackageName);
}
}
if (attribute != null) {
Attribute.put(attributes, attribute);
}
}
return attributes;
}
private Attribute findAttributeValue(ResName attrName, AttributeSet attributeSet, Style styleAttrStyle, Style defStyleFromAttr, Style defStyleFromRes, Style theme, List<ShadowAssetManager.OverlayedStyle> overlayedStyles) {
String attrValue = attributeSet.getAttributeValue(attrName.getNamespaceUri(), attrName.name);
if (attrValue != null) {
return new Attribute(attrName, attrValue, "fixme!!!");
}
if (styleAttrStyle != null) {
Attribute attribute = styleAttrStyle.getAttrValue(attrName);
if (attribute != null) {
return attribute;
}
}
// else if attr in defStyleFromAttr, use its value
if (defStyleFromAttr != null) {
Attribute attribute = defStyleFromAttr.getAttrValue(attrName);
if (attribute != null) {
return attribute;
}
}
if (defStyleFromRes != null) {
Attribute attribute = defStyleFromRes.getAttrValue(attrName);
if (attribute != null) {
return attribute;
}
}
// else if attr in theme, use its value
if (theme != null) {
return getOverlayedThemeValue(attrName, theme, overlayedStyles);
}
return null;
}
private static Attribute getOverlayedThemeValue(ResName attrName, Style theme, List<ShadowAssetManager.OverlayedStyle> overlayedStyles) {
Attribute attribute = theme.getAttrValue(attrName);
if (overlayedStyles != null) {
for (ShadowAssetManager.OverlayedStyle overlayedStyle : overlayedStyles) {
Attribute overlayedAttribute = overlayedStyle.style.getAttrValue(attrName);
if (overlayedAttribute != null && (attribute == null || overlayedStyle.force)) {
attribute = overlayedAttribute;
}
}
}
return attribute;
}
ResName tryResName(int id) {
return getResourceLoader().getResourceIndex().getResName(id);
}
@NotNull ResName getResName(int id) {
ResName resName = getResourceLoader().getResourceIndex().getResName(id);
return checkResName(id, resName);
}
@NotNull ResName resolveResName(int id) {
ResName resName = resolveResName(id, getQualifiers());
return checkResName(id, resName);
}
private ResName checkResName(int id, ResName resName) {
if (resName == null) {
throw new Resources.NotFoundException("Unable to find resource ID #0x" + Integer.toHexString(id));
}
return resName;
}
}