15117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richardpackage com.xtremelabs.robolectric.res;
25117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard
3284a307f028fc1b9c6175a28b60280bc53b5dbe6adriancowhamimport android.R;
4aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wuimport android.graphics.drawable.AnimationDrawable;
5fac8bbf7dfc7904540716be8649b2f5a5c9f6d15adriancowhamimport android.graphics.drawable.Drawable;
6fac8bbf7dfc7904540716be8649b2f5a5c9f6d15adriancowhamimport android.graphics.drawable.LayerDrawable;
7fac8bbf7dfc7904540716be8649b2f5a5c9f6d15adriancowhamimport android.graphics.drawable.StateListDrawable;
8284a307f028fc1b9c6175a28b60280bc53b5dbe6adriancowhamimport com.xtremelabs.robolectric.Robolectric;
9284a307f028fc1b9c6175a28b60280bc53b5dbe6adriancowhamimport com.xtremelabs.robolectric.shadows.ShadowStateListDrawable;
10aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wuimport org.w3c.dom.Document;
11aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wuimport org.w3c.dom.NamedNodeMap;
12aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wuimport org.w3c.dom.Node;
13aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wuimport org.w3c.dom.NodeList;
14aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu
15aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wuimport java.io.File;
16aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wuimport java.io.IOException;
17aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wuimport java.util.HashMap;
18aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wuimport java.util.Map;
19284a307f028fc1b9c6175a28b60280bc53b5dbe6adriancowham
205117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard/**
215117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard * DrawableResourceLoader
225117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard */
235117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richardpublic class DrawableResourceLoader extends XmlLoader {
24aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu
25aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu    // Put all the states for a StateListDrawable in the into a Map for looking up
26aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu    // http://developer.android.com/guide/topics/resources/drawable-resource.html#StateList
27aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu    private static final Map<String, Integer> stateMap = new HashMap<String, Integer>();
28aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu    static {
29aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        stateMap.put("android:state_selected", R.attr.state_selected);
30aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        stateMap.put("android:state_pressed", R.attr.state_pressed);
31aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        stateMap.put("android:state_focused", R.attr.state_focused);
32aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        stateMap.put("android:state_checkable", R.attr.state_checkable);
33aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        stateMap.put("android:state_checked", R.attr.state_checked);
34aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        stateMap.put("android:state_enabled", R.attr.state_enabled);
35aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        stateMap.put("android:state_window_focused", R.attr.state_window_focused);
36aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu    }
37aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu
385117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard    /** document */
395117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard    protected Map<String, Document> documents = new HashMap<String, Document>();
405117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard
415117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard    /** resource directory */
425117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard    protected File resourceDirectory;
435117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard
445117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard    /**
455117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard     * DrawableResourceLoader constructor.
46aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu     *
47aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu     * @param extractor         Extractor
48aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu     * @param resourceDirectory Resource directory
495117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard     */
50f2906888dbe454d6b607e123cc5fdfebd190372bJesse Toth & Tim Labeeuw    public DrawableResourceLoader(ResourceExtractor extractor, File resourceDirectory) {
515117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard        super(extractor);
525117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard        this.resourceDirectory = resourceDirectory;
535117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard    }
545117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard
555117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard    /**
565117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard     * Check if resource is xml.
57aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu     *
58aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu     * @param resourceId Resource id
595117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard     * @return Boolean
605117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard     */
615117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard    public boolean isXml(int resourceId) {
62f2906888dbe454d6b607e123cc5fdfebd190372bJesse Toth & Tim Labeeuw        return documents.containsKey(resourceExtractor.getResourceName(resourceId));
635117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard    }
645117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard
65aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu    public Drawable getXmlDrawable(int resId) {
66aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu
67aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        if (!isXml(resId)) {
68aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu            return null;
69aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        }
70fac8bbf7dfc7904540716be8649b2f5a5c9f6d15adriancowham
71aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        Document xmlDoc = documents.get(resourceExtractor.getResourceName(resId));
72aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        NodeList nodes = xmlDoc.getElementsByTagName("selector");
73aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        if (nodes != null && nodes.getLength() > 0) {
74aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu            return buildStateListDrawable(xmlDoc);
75aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        }
76fac8bbf7dfc7904540716be8649b2f5a5c9f6d15adriancowham
77aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        nodes = xmlDoc.getElementsByTagName("layer-list");
78aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        if (nodes != null && nodes.getLength() > 0) {
79aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu            return new LayerDrawable(null);
80aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        }
81fac8bbf7dfc7904540716be8649b2f5a5c9f6d15adriancowham
82aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        nodes = xmlDoc.getElementsByTagName("animation-list");
83aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        if (nodes != null && nodes.getLength() > 0) {
84aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu            return new AnimationDrawable();
85aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        }
86aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu
87aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        return null;
88fac8bbf7dfc7904540716be8649b2f5a5c9f6d15adriancowham    }
89aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu
905117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard    /**
915117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard     * Store document locally keyed by resource name.
92aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu     *
93aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu     * @param xmlFile  Xml file
94aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu     * @param document Document
95aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu     * @param isSystem System resource
965117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard     * @throws Exception
975117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard     * @see com.xtremelabs.robolectric.res.XmlLoader#processResourceXml(java.io.File,
985117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard     *      org.w3c.dom.Document, boolean)
995117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard     */
1005117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard    @Override
101f2906888dbe454d6b607e123cc5fdfebd190372bJesse Toth & Tim Labeeuw    protected void processResourceXml(File xmlFile, Document document, boolean isSystem) throws Exception {
1025117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard        String name = toResourceName(xmlFile);
1035117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard        if (!documents.containsKey(name)) {
104f2906888dbe454d6b607e123cc5fdfebd190372bJesse Toth & Tim Labeeuw            if (isSystem) {
105f2906888dbe454d6b607e123cc5fdfebd190372bJesse Toth & Tim Labeeuw                name = "android:" + name;
106f2906888dbe454d6b607e123cc5fdfebd190372bJesse Toth & Tim Labeeuw            }
1075117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard            documents.put(name, document);
1085117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard        }
1095117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard    }
1105117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard
1115117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard    /**
1125117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard     * Convert file name to resource name.
113aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu     *
114aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu     * @param xmlFile Xml File
1155117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard     * @return Resource name
1165117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard     */
1175117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard    private String toResourceName(File xmlFile) {
1185117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard        try {
1195117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard            return xmlFile.getCanonicalPath().replaceAll("[/\\\\\\\\]", "/")
1205117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard                    .replaceAll("^.*?/res/", "").replaceAll("\\..+$", "");
1215117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard        } catch (IOException e) {
1225117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard            throw new RuntimeException(e);
1235117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard        }
1245117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard    }
1255117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard
126aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu
1275117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard    /**
1285117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard     * Get drawables by resource id.
129aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu     *
130aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu     * @param resourceId Resource id
1315117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard     * @return Drawables
1325117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard     */
1335117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard    protected int[] getDrawableIds(int resourceId) {
134f2906888dbe454d6b607e123cc5fdfebd190372bJesse Toth & Tim Labeeuw        String resourceName = resourceExtractor.getResourceName(resourceId);
135f2906888dbe454d6b607e123cc5fdfebd190372bJesse Toth & Tim Labeeuw        Document document = documents.get(resourceName);
1365117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard
1375117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard        NodeList items = document.getElementsByTagName("item");
1385117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard        int[] drawableIds = new int[items.getLength()];
1395117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard
1405117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard        for (int i = 0; i < items.getLength(); i++) {
141f2906888dbe454d6b607e123cc5fdfebd190372bJesse Toth & Tim Labeeuw            if (resourceName.startsWith("android:")) {
142f2906888dbe454d6b607e123cc5fdfebd190372bJesse Toth & Tim Labeeuw                drawableIds[i] = -1;
143f2906888dbe454d6b607e123cc5fdfebd190372bJesse Toth & Tim Labeeuw            } else {
144a6d34e0b33533736dd8425a582ed48b40f18ac06Michael Portuesi                Node item = items.item(i);
145a6d34e0b33533736dd8425a582ed48b40f18ac06Michael Portuesi                Node drawableName = item.getAttributes().getNamedItem("android:drawable");
146a6d34e0b33533736dd8425a582ed48b40f18ac06Michael Portuesi                if (drawableName != null) {
147a6d34e0b33533736dd8425a582ed48b40f18ac06Michael Portuesi                    drawableIds[i] = resourceExtractor.getResourceId(drawableName.getNodeValue());
148a6d34e0b33533736dd8425a582ed48b40f18ac06Michael Portuesi                }
149f2906888dbe454d6b607e123cc5fdfebd190372bJesse Toth & Tim Labeeuw            }
1505117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard        }
1515117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard
1525117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard        return drawableIds;
1535117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard    }
154aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu
155aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu    public boolean isAnimationDrawable(int resourceId) {
156aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        Document document = documents.get(resourceExtractor.getResourceName(resourceId));
157aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        return "animation-list".equals(document.getDocumentElement().getLocalName());
158aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu    }
159aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu
160aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu    private StateListDrawable buildStateListDrawable(Document d) {
161aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        StateListDrawable drawable = new StateListDrawable();
162aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        ShadowStateListDrawable shDrawable = Robolectric.shadowOf(drawable);
163aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        NodeList items = d.getElementsByTagName("item");
164aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        for (int i = 0; i < items.getLength(); i++) {
165aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu            Node node = items.item(i);
166aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu            Node drawableName = node.getAttributes().getNamedItem("android:drawable");
167aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu            if (drawableName != null) {
168aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu                int resId = resourceExtractor.getResourceId(drawableName.getNodeValue());
169aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu                int stateId = getStateId(node);
170aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu                shDrawable.addState(stateId, resId);
171aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu            }
172aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        }
173aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        return drawable;
174aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu    }
175aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu
176aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu    private int getStateId(Node node) {
177aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        NamedNodeMap attrs = node.getAttributes();
178aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        for (String state : stateMap.keySet()) {
179aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu            Node attr = attrs.getNamedItem(state);
180aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu            if (attr != null) {
181aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu                return stateMap.get(state);
182aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu            }
183aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        }
184aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu
185aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        // if a state wasn't specified, return the default state
186aec1d0888fe04efd0cde7514e155870b18c41286Rick Kawala & Wei Wu        return R.attr.state_active;
187284a307f028fc1b9c6175a28b60280bc53b5dbe6adriancowham    }
1885117035453920f3d4d0f3969bdab0305dcdb94baJoe Moore & Ryan Richard}
189