add support for animation drawable
diff --git a/src/main/java/com/xtremelabs/robolectric/Robolectric.java b/src/main/java/com/xtremelabs/robolectric/Robolectric.java
index 4814f80..f3ef735 100644
--- a/src/main/java/com/xtremelabs/robolectric/Robolectric.java
+++ b/src/main/java/com/xtremelabs/robolectric/Robolectric.java
@@ -123,6 +123,7 @@
ShadowAlertDialog.ShadowBuilder.class,
ShadowAndroidHttpClient.class,
ShadowAnimation.class,
+ ShadowAnimationDrawable.class,
ShadowAnimationUtils.class,
ShadowApplication.class,
ShadowAppWidgetManager.class,
diff --git a/src/main/java/com/xtremelabs/robolectric/res/DrawableResourceLoader.java b/src/main/java/com/xtremelabs/robolectric/res/DrawableResourceLoader.java
index 1a6fe21..e8e3a8c 100644
--- a/src/main/java/com/xtremelabs/robolectric/res/DrawableResourceLoader.java
+++ b/src/main/java/com/xtremelabs/robolectric/res/DrawableResourceLoader.java
@@ -1,54 +1,56 @@
package com.xtremelabs.robolectric.res;
-import java.io.File;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
+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 android.R;
-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 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 */
+
+ // 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 */
+ /**
+ * resource directory
+ */
protected File resourceDirectory;
/**
* DrawableResourceLoader constructor.
- *
- * @param extractor
- * Extractor
- * @param resourceDirectory
- * Resource directory
+ *
+ * @param extractor Extractor
+ * @param resourceDirectory Resource directory
*/
public DrawableResourceLoader(ResourceExtractor extractor, File resourceDirectory) {
super(extractor);
@@ -57,38 +59,45 @@
/**
* Check if resource is xml.
- *
- * @param resourceId
- * Resource id
+ *
+ * @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 ); }
+ public Drawable getXmlDrawable(int resId) {
- nodes = xmlDoc.getElementsByTagName("layer-list");
- if( nodes != null && nodes.getLength() > 0 ) { return new LayerDrawable( null ); }
+ if (!isXml(resId)) {
+ return null;
+ }
- 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
+ *
+ * @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)
@@ -106,9 +115,8 @@
/**
* Convert file name to resource name.
- *
- * @param xmlFile
- * Xml File
+ *
+ * @param xmlFile Xml File
* @return Resource name
*/
private String toResourceName(File xmlFile) {
@@ -120,12 +128,11 @@
}
}
-
+
/**
* Get drawables by resource id.
- *
- * @param resourceId
- * Resource id
+ *
+ * @param resourceId Resource id
* @return Drawables
*/
protected int[] getDrawableIds(int resourceId) {
@@ -149,34 +156,41 @@
return drawableIds;
}
-
- 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;
+
+ public boolean isAnimationDrawable(int resourceId) {
+ Document document = documents.get(resourceExtractor.getResourceName(resourceId));
+ return "animation-list".equals(document.getDocumentElement().getLocalName());
}
-
- 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;
- }
+
+ 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;
+ }
}
diff --git a/src/main/java/com/xtremelabs/robolectric/res/ResourceLoader.java b/src/main/java/com/xtremelabs/robolectric/res/ResourceLoader.java
index 17c7168..d86405b 100644
--- a/src/main/java/com/xtremelabs/robolectric/res/ResourceLoader.java
+++ b/src/main/java/com/xtremelabs/robolectric/res/ResourceLoader.java
@@ -407,6 +407,11 @@
return drawableResourceLoader.isXml( resourceId );
}
+ public boolean isAnimatableXml( int resourceId ) {
+ init();
+ return drawableResourceLoader.isAnimationDrawable( resourceId );
+ }
+
public int[] getDrawableIds( int resourceId ) {
init();
return drawableResourceLoader.getDrawableIds( resourceId );
diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowAnimationDrawable.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowAnimationDrawable.java
new file mode 100644
index 0000000..5510c0d
--- /dev/null
+++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowAnimationDrawable.java
@@ -0,0 +1,25 @@
+package com.xtremelabs.robolectric.shadows;
+
+import android.graphics.drawable.AnimationDrawable;
+import android.graphics.drawable.Drawable;
+import com.xtremelabs.robolectric.internal.Implementation;
+import com.xtremelabs.robolectric.internal.Implements;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Implements(AnimationDrawable.class)
+public class ShadowAnimationDrawable extends ShadowDrawable {
+
+ private List<Drawable> frames = new ArrayList<Drawable>();
+
+ @Implementation
+ public void addFrame(Drawable frame, int duration) {
+ frames.add(frame);
+ }
+
+ @Implementation
+ public int getNumberOfFrames() {
+ return frames.size();
+ }
+}
diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowImageView.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowImageView.java
index a64eab3..7d205da 100644
--- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowImageView.java
+++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowImageView.java
@@ -4,6 +4,7 @@
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
+import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
@@ -11,6 +12,7 @@
import com.xtremelabs.robolectric.Robolectric;
import com.xtremelabs.robolectric.internal.Implementation;
import com.xtremelabs.robolectric.internal.Implements;
+import com.xtremelabs.robolectric.res.ResourceLoader;
import static com.xtremelabs.robolectric.Robolectric.shadowOf;
@@ -60,18 +62,24 @@
*/
protected Drawable buildDrawable(int resourceId) {
if (isDrawableXml(resourceId)) {
- int[] resourceIds = shadowOf(Robolectric.application)
- .getResourceLoader().getDrawableIds(resourceId);
-
+ ResourceLoader resourceLoader = shadowOf(Robolectric.application).getResourceLoader();
+ int[] resourceIds = resourceLoader.getDrawableIds(resourceId);
Drawable[] drawables = new Drawable[resourceIds.length];
for (int i = 0; i < resourceIds.length; i++) {
drawables[i] = buildDrawable(resourceIds[i]);
}
-
- LayerDrawable layerDrawable = new LayerDrawable(drawables);
- shadowOf(layerDrawable).setLoadedFromResourceId(resourceId);
- return layerDrawable;
+ if (resourceLoader.isAnimatableXml(resourceId)) {
+ AnimationDrawable animationDrawable = new AnimationDrawable();
+ for (Drawable drawable : drawables) {
+ animationDrawable.addFrame(drawable, -1);
+ }
+ return animationDrawable;
+ } else {
+ LayerDrawable layerDrawable = new LayerDrawable(drawables);
+ shadowOf(layerDrawable).setLoadedFromResourceId(resourceId);
+ return layerDrawable;
+ }
} else {
return new BitmapDrawable(BitmapFactory.decodeResource(
getResources(), resourceId));
diff --git a/src/test/java/com/xtremelabs/robolectric/R.java b/src/test/java/com/xtremelabs/robolectric/R.java
index cbee3c3..7828b4b 100644
--- a/src/test/java/com/xtremelabs/robolectric/R.java
+++ b/src/test/java/com/xtremelabs/robolectric/R.java
@@ -102,6 +102,7 @@
public static final int l7_white = nextId++;
public static final int rainbow = nextId++;
public static final int state_drawable = nextId++;
+ public static final int animation_list = nextId++;
}
public static final class layout {
diff --git a/src/test/java/com/xtremelabs/robolectric/res/DrawableResourceLoaderTest.java b/src/test/java/com/xtremelabs/robolectric/res/DrawableResourceLoaderTest.java
index 2fc4eb7..4357e55 100644
--- a/src/test/java/com/xtremelabs/robolectric/res/DrawableResourceLoaderTest.java
+++ b/src/test/java/com/xtremelabs/robolectric/res/DrawableResourceLoaderTest.java
@@ -1,27 +1,25 @@
package com.xtremelabs.robolectric.res;
-import static com.xtremelabs.robolectric.util.TestUtil.getSystemResourceDir;
-import static com.xtremelabs.robolectric.util.TestUtil.resourceFile;
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.StateListDrawable;
-
import com.xtremelabs.robolectric.R;
import com.xtremelabs.robolectric.Robolectric;
import com.xtremelabs.robolectric.WithTestDefaultsRunner;
import com.xtremelabs.robolectric.shadows.ShadowStateListDrawable;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static com.xtremelabs.robolectric.util.TestUtil.getSystemResourceDir;
+import static com.xtremelabs.robolectric.util.TestUtil.resourceFile;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
/**
* DrawableResourceLoaderTest
@@ -46,7 +44,7 @@
@Test
public void testProcessResourceXml() throws Exception {
assertTrue("drawable/rainbow", resourceLoader.documents.containsKey("drawable/rainbow"));
- assertEquals("documents.size", 115, resourceLoader.documents.size());
+ assertEquals("documents.size", 116, resourceLoader.documents.size());
}
@Test
diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/ImageViewTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/ImageViewTest.java
index 11aad6f..1519291 100644
--- a/src/test/java/com/xtremelabs/robolectric/shadows/ImageViewTest.java
+++ b/src/test/java/com/xtremelabs/robolectric/shadows/ImageViewTest.java
@@ -4,6 +4,7 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
+import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.widget.ImageView;
@@ -19,7 +20,9 @@
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
@RunWith(WithTestDefaultsRunner.class)
public class ImageViewTest {
@@ -73,6 +76,21 @@
}
@Test
+ public void testSetAnimatedImage_drawable() {
+ imageView.setImageResource(R.drawable.animation_list);
+ Drawable animation = imageView.getDrawable();
+ assertTrue(animation instanceof Drawable);
+ assertTrue(animation instanceof AnimationDrawable);
+ }
+
+ @Test
+ public void testSetAnimationItem() throws Exception {
+ imageView.setImageResource(R.drawable.animation_list);
+ AnimationDrawable animation = (AnimationDrawable) imageView.getDrawable();
+ assertEquals(3, animation.getNumberOfFrames());
+ }
+
+ @Test
public void testSetImageResource_layerDrawable() {
imageView.setImageResource(R.drawable.rainbow);
assertTrue("Drawable", imageView.getDrawable() instanceof Drawable);
diff --git a/src/test/resources/res/drawable/animation_list.xml b/src/test/resources/res/drawable/animation_list.xml
new file mode 100644
index 0000000..621aa20
--- /dev/null
+++ b/src/test/resources/res/drawable/animation_list.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
+ <item android:drawable="@drawable/an_image" android:duration="400"/>
+ <item android:drawable="@drawable/an_other_image" android:duration="400"/>
+ <item android:drawable="@drawable/third_image" android:duration="400"/>
+</animation-list>
\ No newline at end of file