• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.xtremelabs.robolectric.res;
2 
3 import android.R;
4 import android.graphics.drawable.AnimationDrawable;
5 import android.graphics.drawable.Drawable;
6 import android.graphics.drawable.LayerDrawable;
7 import android.graphics.drawable.StateListDrawable;
8 import com.xtremelabs.robolectric.Robolectric;
9 import com.xtremelabs.robolectric.shadows.ShadowStateListDrawable;
10 import org.w3c.dom.Document;
11 import org.w3c.dom.NamedNodeMap;
12 import org.w3c.dom.Node;
13 import org.w3c.dom.NodeList;
14 
15 import java.io.File;
16 import java.io.IOException;
17 import java.util.HashMap;
18 import java.util.Map;
19 
20 /**
21  * DrawableResourceLoader
22  */
23 public class DrawableResourceLoader extends XmlLoader {
24 
25     // Put all the states for a StateListDrawable in the into a Map for looking up
26     // http://developer.android.com/guide/topics/resources/drawable-resource.html#StateList
27     private static final Map<String, Integer> stateMap = new HashMap<String, Integer>();
28     static {
29         stateMap.put("android:state_selected", R.attr.state_selected);
30         stateMap.put("android:state_pressed", R.attr.state_pressed);
31         stateMap.put("android:state_focused", R.attr.state_focused);
32         stateMap.put("android:state_checkable", R.attr.state_checkable);
33         stateMap.put("android:state_checked", R.attr.state_checked);
34         stateMap.put("android:state_enabled", R.attr.state_enabled);
35         stateMap.put("android:state_window_focused", R.attr.state_window_focused);
36     }
37 
38     /** document */
39     protected Map<String, Document> documents = new HashMap<String, Document>();
40 
41     /** resource directory */
42     protected File resourceDirectory;
43 
44     /**
45      * DrawableResourceLoader constructor.
46      *
47      * @param extractor         Extractor
48      * @param resourceDirectory Resource directory
49      */
DrawableResourceLoader(ResourceExtractor extractor, File resourceDirectory)50     public DrawableResourceLoader(ResourceExtractor extractor, File resourceDirectory) {
51         super(extractor);
52         this.resourceDirectory = resourceDirectory;
53     }
54 
55     /**
56      * Check if resource is xml.
57      *
58      * @param resourceId Resource id
59      * @return Boolean
60      */
isXml(int resourceId)61     public boolean isXml(int resourceId) {
62         return documents.containsKey(resourceExtractor.getResourceName(resourceId));
63     }
64 
getXmlDrawable(int resId)65     public Drawable getXmlDrawable(int resId) {
66 
67         if (!isXml(resId)) {
68             return null;
69         }
70 
71         Document xmlDoc = documents.get(resourceExtractor.getResourceName(resId));
72         NodeList nodes = xmlDoc.getElementsByTagName("selector");
73         if (nodes != null && nodes.getLength() > 0) {
74             return buildStateListDrawable(xmlDoc);
75         }
76 
77         nodes = xmlDoc.getElementsByTagName("layer-list");
78         if (nodes != null && nodes.getLength() > 0) {
79             return new LayerDrawable(null);
80         }
81 
82         nodes = xmlDoc.getElementsByTagName("animation-list");
83         if (nodes != null && nodes.getLength() > 0) {
84             return new AnimationDrawable();
85         }
86 
87         return null;
88     }
89 
90     /**
91      * Store document locally keyed by resource name.
92      *
93      * @param xmlFile  Xml file
94      * @param document Document
95      * @param isSystem System resource
96      * @throws Exception
97      * @see com.xtremelabs.robolectric.res.XmlLoader#processResourceXml(java.io.File,
98      *      org.w3c.dom.Document, boolean)
99      */
100     @Override
processResourceXml(File xmlFile, Document document, boolean isSystem)101     protected void processResourceXml(File xmlFile, Document document, boolean isSystem) throws Exception {
102         String name = toResourceName(xmlFile);
103         if (!documents.containsKey(name)) {
104             if (isSystem) {
105                 name = "android:" + name;
106             }
107             documents.put(name, document);
108         }
109     }
110 
111     /**
112      * Convert file name to resource name.
113      *
114      * @param xmlFile Xml File
115      * @return Resource name
116      */
toResourceName(File xmlFile)117     private String toResourceName(File xmlFile) {
118         try {
119             return xmlFile.getCanonicalPath().replaceAll("[/\\\\\\\\]", "/")
120                     .replaceAll("^.*?/res/", "").replaceAll("\\..+$", "");
121         } catch (IOException e) {
122             throw new RuntimeException(e);
123         }
124     }
125 
126 
127     /**
128      * Get drawables by resource id.
129      *
130      * @param resourceId Resource id
131      * @return Drawables
132      */
getDrawableIds(int resourceId)133     protected int[] getDrawableIds(int resourceId) {
134         String resourceName = resourceExtractor.getResourceName(resourceId);
135         Document document = documents.get(resourceName);
136 
137         NodeList items = document.getElementsByTagName("item");
138         int[] drawableIds = new int[items.getLength()];
139 
140         for (int i = 0; i < items.getLength(); i++) {
141             if (resourceName.startsWith("android:")) {
142                 drawableIds[i] = -1;
143             } else {
144                 Node item = items.item(i);
145                 Node drawableName = item.getAttributes().getNamedItem("android:drawable");
146                 if (drawableName != null) {
147                     drawableIds[i] = resourceExtractor.getResourceId(drawableName.getNodeValue());
148                 }
149             }
150         }
151 
152         return drawableIds;
153     }
154 
isAnimationDrawable(int resourceId)155     public boolean isAnimationDrawable(int resourceId) {
156         Document document = documents.get(resourceExtractor.getResourceName(resourceId));
157         return "animation-list".equals(document.getDocumentElement().getLocalName());
158     }
159 
buildStateListDrawable(Document d)160     private StateListDrawable buildStateListDrawable(Document d) {
161         StateListDrawable drawable = new StateListDrawable();
162         ShadowStateListDrawable shDrawable = Robolectric.shadowOf(drawable);
163         NodeList items = d.getElementsByTagName("item");
164         for (int i = 0; i < items.getLength(); i++) {
165             Node node = items.item(i);
166             Node drawableName = node.getAttributes().getNamedItem("android:drawable");
167             if (drawableName != null) {
168                 int resId = resourceExtractor.getResourceId(drawableName.getNodeValue());
169                 int stateId = getStateId(node);
170                 shDrawable.addState(stateId, resId);
171             }
172         }
173         return drawable;
174     }
175 
getStateId(Node node)176     private int getStateId(Node node) {
177         NamedNodeMap attrs = node.getAttributes();
178         for (String state : stateMap.keySet()) {
179             Node attr = attrs.getNamedItem(state);
180             if (attr != null) {
181                 return stateMap.get(state);
182             }
183         }
184 
185         // if a state wasn't specified, return the default state
186         return R.attr.state_active;
187     }
188 }
189