org.robolectric.res.ResourceTableFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of resources Show documentation
Show all versions of resources Show documentation
An alternative Android testing framework.
package org.robolectric.res;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import org.robolectric.util.Logger;
import org.robolectric.util.PerfStatsCollector;
public class ResourceTableFactory {
/** Builds an Android framework resource table in the "android" package space. */
public PackageResourceTable newFrameworkResourceTable(ResourcePath resourcePath) {
return PerfStatsCollector.getInstance()
.measure(
"load legacy framework resources",
() -> {
PackageResourceTable resourceTable = new PackageResourceTable("android");
if (resourcePath.getRClass() != null) {
addRClassValues(resourceTable, resourcePath.getRClass());
addMissingStyleableAttributes(resourceTable, resourcePath.getRClass());
}
if (resourcePath.getInternalRClass() != null) {
addRClassValues(resourceTable, resourcePath.getInternalRClass());
addMissingStyleableAttributes(resourceTable, resourcePath.getInternalRClass());
}
parseResourceFiles(resourcePath, resourceTable);
return resourceTable;
});
}
/**
* Creates an application resource table which can be constructed with multiple resources paths
* representing overlayed resource libraries.
*/
public PackageResourceTable newResourceTable(String packageName, ResourcePath... resourcePaths) {
return PerfStatsCollector.getInstance()
.measure(
"load legacy app resources",
() -> {
PackageResourceTable resourceTable = new PackageResourceTable(packageName);
for (ResourcePath resourcePath : resourcePaths) {
if (resourcePath.getRClass() != null) {
addRClassValues(resourceTable, resourcePath.getRClass());
}
}
for (ResourcePath resourcePath : resourcePaths) {
parseResourceFiles(resourcePath, resourceTable);
}
return resourceTable;
});
}
private void addRClassValues(PackageResourceTable resourceTable, Class> rClass) {
for (Class innerClass : rClass.getClasses()) {
String resourceType = innerClass.getSimpleName();
if (!resourceType.equals("styleable")) {
for (Field field : innerClass.getDeclaredFields()) {
if (field.getType().equals(Integer.TYPE) && Modifier.isStatic(field.getModifiers())) {
int id;
try {
id = field.getInt(null);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
String resourceName = field.getName();
// Pre-release versions of Android use the resource value '0' to indicate that the
// resource is being staged for inclusion in the public Android SDK. Skip these
// resource ids.
if (id == 0) {
Logger.debug("Ignoring staged resource " + resourceName);
continue;
}
resourceTable.addResource(id, resourceType, resourceName);
}
}
}
}
}
/**
* Check the stylable elements. Not for aapt generated R files but for framework R files it is possible to
* have attributes in the styleable array for which there is no corresponding R.attr field.
*/
private void addMissingStyleableAttributes(PackageResourceTable resourceTable, Class> rClass) {
for (Class innerClass : rClass.getClasses()) {
if (innerClass.getSimpleName().equals("styleable")) {
String styleableName = null; // Current styleable name
int[] styleableArray = null; // Current styleable value array or references
for (Field field : innerClass.getDeclaredFields()) {
if (field.getType().equals(int[].class) && Modifier.isStatic(field.getModifiers())) {
styleableName = field.getName();
try {
styleableArray = (int[]) field.get(null);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
} else if (field.getType().equals(Integer.TYPE) && Modifier.isStatic(field.getModifiers())) {
String attributeName = field.getName().substring(styleableName.length() + 1);
try {
int styleableIndex = field.getInt(null);
int attributeResId = styleableArray[styleableIndex];
resourceTable.addResource(attributeResId, "attr", attributeName);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
}
}
private void parseResourceFiles(ResourcePath resourcePath, PackageResourceTable resourceTable) {
if (!resourcePath.hasResources()) {
Logger.debug("No resources for %s", resourceTable.getPackageName());
return;
}
Logger.debug(
"Loading resources for %s from %s...",
resourceTable.getPackageName(), resourcePath.getResourceBase());
try {
new StaxDocumentLoader(
resourceTable.getPackageName(),
resourcePath.getResourceBase(),
new NodeHandler()
.addHandler(
"resources",
new NodeHandler()
.addHandler(
"bool", new StaxValueLoader(resourceTable, "bool", ResType.BOOLEAN))
.addHandler(
"item[@type='bool']",
new StaxValueLoader(resourceTable, "bool", ResType.BOOLEAN))
.addHandler(
"color", new StaxValueLoader(resourceTable, "color", ResType.COLOR))
.addHandler(
"item[@type='color']",
new StaxValueLoader(resourceTable, "color", ResType.COLOR))
.addHandler(
"drawable",
new StaxValueLoader(resourceTable, "drawable", ResType.DRAWABLE))
.addHandler(
"item[@type='drawable']",
new StaxValueLoader(resourceTable, "drawable", ResType.DRAWABLE))
.addHandler(
"item[@type='mipmap']",
new StaxValueLoader(resourceTable, "mipmap", ResType.DRAWABLE))
.addHandler(
"dimen", new StaxValueLoader(resourceTable, "dimen", ResType.DIMEN))
.addHandler(
"item[@type='dimen']",
new StaxValueLoader(resourceTable, "dimen", ResType.DIMEN))
.addHandler(
"integer",
new StaxValueLoader(resourceTable, "integer", ResType.INTEGER))
.addHandler(
"item[@type='integer']",
new StaxValueLoader(resourceTable, "integer", ResType.INTEGER))
.addHandler(
"integer-array",
new StaxArrayLoader(
resourceTable, "array", ResType.INTEGER_ARRAY, ResType.INTEGER))
.addHandler(
"fraction",
new StaxValueLoader(resourceTable, "fraction", ResType.FRACTION))
.addHandler(
"item[@type='fraction']",
new StaxValueLoader(resourceTable, "fraction", ResType.FRACTION))
.addHandler(
"item[@type='layout']",
new StaxValueLoader(resourceTable, "layout", ResType.LAYOUT))
.addHandler(
"plurals",
new StaxPluralsLoader(
resourceTable, "plurals", ResType.CHAR_SEQUENCE))
.addHandler(
"string",
new StaxValueLoader(resourceTable, "string", ResType.CHAR_SEQUENCE))
.addHandler(
"item[@type='string']",
new StaxValueLoader(resourceTable, "string", ResType.CHAR_SEQUENCE))
.addHandler(
"string-array",
new StaxArrayLoader(
resourceTable,
"array",
ResType.CHAR_SEQUENCE_ARRAY,
ResType.CHAR_SEQUENCE))
.addHandler(
"array",
new StaxArrayLoader(
resourceTable, "array", ResType.TYPED_ARRAY, null))
.addHandler(
"id", new StaxValueLoader(resourceTable, "id", ResType.CHAR_SEQUENCE))
.addHandler(
"item[@type='id']",
new StaxValueLoader(resourceTable, "id", ResType.CHAR_SEQUENCE))
.addHandler(
"attr", new StaxAttrLoader(resourceTable, "attr", ResType.ATTR_DATA))
.addHandler(
"declare-styleable",
new NodeHandler()
.addHandler(
"attr",
new StaxAttrLoader(resourceTable, "attr", ResType.ATTR_DATA)))
.addHandler(
"style", new StaxStyleLoader(resourceTable, "style", ResType.STYLE))))
.load("values");
loadOpaque(resourcePath, resourceTable, "layout", ResType.LAYOUT);
loadOpaque(resourcePath, resourceTable, "menu", ResType.LAYOUT);
loadOpaque(resourcePath, resourceTable, "drawable", ResType.DRAWABLE);
loadOpaque(resourcePath, resourceTable, "mipmap", ResType.DRAWABLE);
loadOpaque(resourcePath, resourceTable, "anim", ResType.LAYOUT);
loadOpaque(resourcePath, resourceTable, "animator", ResType.LAYOUT);
loadOpaque(resourcePath, resourceTable, "color", ResType.COLOR_STATE_LIST);
loadOpaque(resourcePath, resourceTable, "xml", ResType.LAYOUT);
loadOpaque(resourcePath, resourceTable, "transition", ResType.LAYOUT);
loadOpaque(resourcePath, resourceTable, "interpolator", ResType.LAYOUT);
new DrawableResourceLoader(resourceTable).findDrawableResources(resourcePath);
new RawResourceLoader(resourcePath).loadTo(resourceTable);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void loadOpaque(
ResourcePath resourcePath,
final PackageResourceTable resourceTable,
final String type,
final ResType resType)
throws IOException {
new DocumentLoader(resourceTable.getPackageName(), resourcePath.getResourceBase()) {
@Override
protected void loadResourceXmlFile(XmlContext xmlContext) {
resourceTable.addResource(
type,
Fs.baseNameFor(xmlContext.getXmlFile()),
new FileTypedResource(xmlContext.getXmlFile(), resType, xmlContext));
}
}.load(type);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy