diff options
Diffstat (limited to 'v1/src/main/java/com/xtremelabs/robolectric/res/DrawableResourceLoader.java')
-rw-r--r-- | v1/src/main/java/com/xtremelabs/robolectric/res/DrawableResourceLoader.java | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/v1/src/main/java/com/xtremelabs/robolectric/res/DrawableResourceLoader.java b/v1/src/main/java/com/xtremelabs/robolectric/res/DrawableResourceLoader.java new file mode 100644 index 000000000..bac82e164 --- /dev/null +++ b/v1/src/main/java/com/xtremelabs/robolectric/res/DrawableResourceLoader.java @@ -0,0 +1,188 @@ +package com.xtremelabs.robolectric.res; + +import android.R; +import android.graphics.drawable.AnimationDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; +import android.graphics.drawable.StateListDrawable; +import com.xtremelabs.robolectric.Robolectric; +import com.xtremelabs.robolectric.shadows.ShadowStateListDrawable; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * DrawableResourceLoader + */ +public class DrawableResourceLoader extends XmlLoader { + + // Put all the states for a StateListDrawable in the into a Map for looking up + // http://developer.android.com/guide/topics/resources/drawable-resource.html#StateList + private static final Map<String, Integer> stateMap = new HashMap<String, Integer>(); + static { + stateMap.put("android:state_selected", R.attr.state_selected); + stateMap.put("android:state_pressed", R.attr.state_pressed); + stateMap.put("android:state_focused", R.attr.state_focused); + stateMap.put("android:state_checkable", R.attr.state_checkable); + stateMap.put("android:state_checked", R.attr.state_checked); + stateMap.put("android:state_enabled", R.attr.state_enabled); + stateMap.put("android:state_window_focused", R.attr.state_window_focused); + } + + /** document */ + protected Map<String, Document> documents = new HashMap<String, Document>(); + + /** resource directory */ + protected File resourceDirectory; + + /** + * DrawableResourceLoader constructor. + * + * @param extractor Extractor + * @param resourceDirectory Resource directory + */ + public DrawableResourceLoader(ResourceExtractor extractor, File resourceDirectory) { + super(extractor); + this.resourceDirectory = resourceDirectory; + } + + /** + * Check if resource is xml. + * + * @param resourceId Resource id + * @return Boolean + */ + public boolean isXml(int resourceId) { + return documents.containsKey(resourceExtractor.getResourceName(resourceId)); + } + + public Drawable getXmlDrawable(int resId) { + + if (!isXml(resId)) { + return null; + } + + Document xmlDoc = documents.get(resourceExtractor.getResourceName(resId)); + NodeList nodes = xmlDoc.getElementsByTagName("selector"); + if (nodes != null && nodes.getLength() > 0) { + return buildStateListDrawable(xmlDoc); + } + + nodes = xmlDoc.getElementsByTagName("layer-list"); + if (nodes != null && nodes.getLength() > 0) { + return new LayerDrawable(null); + } + + nodes = xmlDoc.getElementsByTagName("animation-list"); + if (nodes != null && nodes.getLength() > 0) { + return new AnimationDrawable(); + } + + return null; + } + + /** + * Store document locally keyed by resource name. + * + * @param xmlFile Xml file + * @param document Document + * @param isSystem System resource + * @throws Exception + * @see com.xtremelabs.robolectric.res.XmlLoader#processResourceXml(java.io.File, + * org.w3c.dom.Document, boolean) + */ + @Override + protected void processResourceXml(File xmlFile, Document document, boolean isSystem) throws Exception { + String name = toResourceName(xmlFile); + if (!documents.containsKey(name)) { + if (isSystem) { + name = "android:" + name; + } + documents.put(name, document); + } + } + + /** + * Convert file name to resource name. + * + * @param xmlFile Xml File + * @return Resource name + */ + private String toResourceName(File xmlFile) { + try { + return xmlFile.getCanonicalPath().replaceAll("[/\\\\\\\\]", "/") + .replaceAll("^.*?/res/", "").replaceAll("\\..+$", ""); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + + /** + * Get drawables by resource id. + * + * @param resourceId Resource id + * @return Drawables + */ + protected int[] getDrawableIds(int resourceId) { + String resourceName = resourceExtractor.getResourceName(resourceId); + Document document = documents.get(resourceName); + + NodeList items = document.getElementsByTagName("item"); + int[] drawableIds = new int[items.getLength()]; + + for (int i = 0; i < items.getLength(); i++) { + if (resourceName.startsWith("android:")) { + drawableIds[i] = -1; + } else { + Node item = items.item(i); + Node drawableName = item.getAttributes().getNamedItem("android:drawable"); + if (drawableName != null) { + drawableIds[i] = resourceExtractor.getResourceId(drawableName.getNodeValue()); + } + } + } + + return drawableIds; + } + + public boolean isAnimationDrawable(int resourceId) { + Document document = documents.get(resourceExtractor.getResourceName(resourceId)); + return "animation-list".equals(document.getDocumentElement().getLocalName()); + } + + private StateListDrawable buildStateListDrawable(Document d) { + StateListDrawable drawable = new StateListDrawable(); + ShadowStateListDrawable shDrawable = Robolectric.shadowOf(drawable); + NodeList items = d.getElementsByTagName("item"); + for (int i = 0; i < items.getLength(); i++) { + Node node = items.item(i); + Node drawableName = node.getAttributes().getNamedItem("android:drawable"); + if (drawableName != null) { + int resId = resourceExtractor.getResourceId(drawableName.getNodeValue()); + int stateId = getStateId(node); + shDrawable.addState(stateId, resId); + } + } + return drawable; + } + + private int getStateId(Node node) { + NamedNodeMap attrs = node.getAttributes(); + for (String state : stateMap.keySet()) { + Node attr = attrs.getNamedItem(state); + if (attr != null) { + return stateMap.get(state); + } + } + + // if a state wasn't specified, return the default state + return R.attr.state_active; + } +} |