Make AnimationFactorys top level and add tests.
diff --git a/library/src/androidTest/java/com/bumptech/glide/request/animation/DrawableCrossFadeFactoryTest.java b/library/src/androidTest/java/com/bumptech/glide/request/animation/DrawableCrossFadeFactoryTest.java
new file mode 100644
index 0000000..54407b5
--- /dev/null
+++ b/library/src/androidTest/java/com/bumptech/glide/request/animation/DrawableCrossFadeFactoryTest.java
@@ -0,0 +1,43 @@
+package com.bumptech.glide.request.animation;
+
+import android.graphics.drawable.Drawable;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.mockito.Mockito.mock;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(manifest = Config.NONE, emulateSdk = 18)
+public class DrawableCrossFadeFactoryTest {
+
+    private DrawableCrossFadeFactory<Drawable> factory;
+
+    @SuppressWarnings("unchecked")
+    @Before
+    public void setUp() {
+        ViewAnimationFactory<Drawable> viewAnimationFactory = mock(ViewAnimationFactory.class);
+        factory = new DrawableCrossFadeFactory<Drawable>(viewAnimationFactory, 100 /*duration*/);
+    }
+
+    @Test
+    public void testReturnsNoAnimationIfFromMemoryCache() {
+        assertEquals(NoAnimation.<Drawable>get(), factory.build(true /*isFromMemoryCache*/, true /*isFirstResource*/));
+    }
+
+    @Test
+    public void testReturnsReturnsAnimationIfNotFromMemoryCacheAndIsFirstResource() {
+        assertNotEquals(NoAnimation.<Drawable>get(),
+                factory.build(false /*isFromMemoryCache*/, true /*isFirstResource*/));
+    }
+
+    @Test
+    public void testReturnsAnimationIfNotFromMemocyCacheAndNotIsFirstResource() {
+        assertNotEquals(NoAnimation.<Drawable>get(),
+                factory.build(false /*isFromMemoryCache*/, false /*isFirstResource*/));
+    }
+}
\ No newline at end of file
diff --git a/library/src/androidTest/java/com/bumptech/glide/request/animation/ViewAnimationFactoryTest.java b/library/src/androidTest/java/com/bumptech/glide/request/animation/ViewAnimationFactoryTest.java
new file mode 100644
index 0000000..56a4bbc
--- /dev/null
+++ b/library/src/androidTest/java/com/bumptech/glide/request/animation/ViewAnimationFactoryTest.java
@@ -0,0 +1,56 @@
+package com.bumptech.glide.request.animation;
+
+import android.view.View;
+import android.view.animation.Animation;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(JUnit4.class)
+public class ViewAnimationFactoryTest {
+    private ViewAnimation.AnimationFactory animationFactory;
+    private ViewAnimationFactory<Object> factory;
+
+    @Before
+    public void setUp() {
+        animationFactory = mock(ViewAnimation.AnimationFactory.class);
+        factory = new ViewAnimationFactory<Object>(animationFactory);
+    }
+
+    @Test
+    public void testFactoryReturnsNoAnimationIfFromMemoryCache() {
+        GlideAnimation<Object> animation = factory.build(true /*isFromMemoryCache*/, true /*isFirstResource*/);
+        assertEquals(NoAnimation.get(), animation);
+        verify(animationFactory, never()).build();
+    }
+
+    @Test
+    public void testFactoryReturnsNoAnimationIfNotFirstResource() {
+        GlideAnimation<Object> animation = factory.build(false /*isFromMemoryCache*/, false /*isFirstResource*/);
+        assertEquals(NoAnimation.get(), animation);
+        verify(animationFactory, never()).build();
+    }
+
+    @Test
+    public void testFactoryReturnsActualAnimationIfNotIsFromMemoryCacheAndIsFirstResource() {
+        GlideAnimation<Object> glideAnimation = factory.build(false /*isFromMemoryCache*/, true /*isFirstResource*/);
+
+        Animation animation = mock(Animation.class);
+        when(animationFactory.build()).thenReturn(animation);
+
+        GlideAnimation.ViewAdapter adapter = mock(GlideAnimation.ViewAdapter.class);
+        View view = mock(View.class);
+        when(adapter.getView()).thenReturn(view);
+        glideAnimation.animate(new Object(), adapter);
+
+        verify(view).startAnimation(eq(animation));
+    }
+}
\ No newline at end of file
diff --git a/library/src/androidTest/java/com/bumptech/glide/request/animation/ViewPropertyAnimationFactoryTest.java b/library/src/androidTest/java/com/bumptech/glide/request/animation/ViewPropertyAnimationFactoryTest.java
new file mode 100644
index 0000000..c53d6e2
--- /dev/null
+++ b/library/src/androidTest/java/com/bumptech/glide/request/animation/ViewPropertyAnimationFactoryTest.java
@@ -0,0 +1,37 @@
+package com.bumptech.glide.request.animation;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.mockito.Mockito.mock;
+
+@RunWith(JUnit4.class)
+public class ViewPropertyAnimationFactoryTest {
+
+    private ViewPropertyAnimationFactory<Object> factory;
+
+    @Before
+    public void setUp() {
+        ViewPropertyAnimation.Animator animator = mock(ViewPropertyAnimation.Animator.class);
+        factory = new ViewPropertyAnimationFactory<Object>(animator);
+    }
+
+    @Test
+    public void testReturnsNoAnimationIfFromMemoryCache() {
+        assertEquals(NoAnimation.get(), factory.build(true /*isFromMemoryCache*/, true /*isFirstResource*/));
+    }
+
+    @Test
+    public void testReturnsNoAnimationIfNotFirstResource() {
+        assertEquals(NoAnimation.get(), factory.build(false /*isFromMemoryCache*/, false /*isFirstResource*/));
+    }
+
+    @Test
+    public void testReturnsAnimationIfNotFromMemoryCacheAndFirstResource() {
+        assertNotEquals(NoAnimation.get(), factory.build(false /*isFromMemoryCache*/, true /*isFirstResource*/));
+    }
+}
\ No newline at end of file
diff --git a/library/src/main/java/com/bumptech/glide/DrawableRequestBuilder.java b/library/src/main/java/com/bumptech/glide/DrawableRequestBuilder.java
index cb49e4c..1be97d8 100644
--- a/library/src/main/java/com/bumptech/glide/DrawableRequestBuilder.java
+++ b/library/src/main/java/com/bumptech/glide/DrawableRequestBuilder.java
@@ -21,7 +21,7 @@
 import com.bumptech.glide.manager.RequestTracker;
 import com.bumptech.glide.provider.LoadProvider;
 import com.bumptech.glide.request.RequestListener;
-import com.bumptech.glide.request.animation.DrawableCrossFadeViewAnimation;
+import com.bumptech.glide.request.animation.DrawableCrossFadeFactory;
 import com.bumptech.glide.request.animation.ViewPropertyAnimation;
 import com.bumptech.glide.request.target.Target;
 
@@ -242,7 +242,7 @@
      * {@inheritDoc}
      */
     public final DrawableRequestBuilder<ModelType> crossFade() {
-        super.animate(new DrawableCrossFadeViewAnimation.DrawableCrossFadeFactory<GlideDrawable>());
+        super.animate(new DrawableCrossFadeFactory<GlideDrawable>());
         return this;
     }
 
@@ -250,7 +250,7 @@
      * {@inheritDoc}
      */
     public DrawableRequestBuilder<ModelType> crossFade(int duration) {
-        super.animate(new DrawableCrossFadeViewAnimation.DrawableCrossFadeFactory<GlideDrawable>(duration));
+        super.animate(new DrawableCrossFadeFactory<GlideDrawable>(duration));
         return this;
     }
 
@@ -259,7 +259,7 @@
      */
     @Deprecated
     public DrawableRequestBuilder<ModelType> crossFade(Animation animation, int duration) {
-        super.animate(new DrawableCrossFadeViewAnimation.DrawableCrossFadeFactory<GlideDrawable>(animation, duration));
+        super.animate(new DrawableCrossFadeFactory<GlideDrawable>(animation, duration));
         return this;
     }
 
@@ -267,7 +267,7 @@
      * {@inheritDoc}
      */
     public DrawableRequestBuilder<ModelType> crossFade(int animationId, int duration) {
-        super.animate(new DrawableCrossFadeViewAnimation.DrawableCrossFadeFactory<GlideDrawable>(context, animationId,
+        super.animate(new DrawableCrossFadeFactory<GlideDrawable>(context, animationId,
                 duration));
         return this;
     }
diff --git a/library/src/main/java/com/bumptech/glide/GenericRequestBuilder.java b/library/src/main/java/com/bumptech/glide/GenericRequestBuilder.java
index 2ab7d79..eeaa73a 100644
--- a/library/src/main/java/com/bumptech/glide/GenericRequestBuilder.java
+++ b/library/src/main/java/com/bumptech/glide/GenericRequestBuilder.java
@@ -4,7 +4,6 @@
 import android.graphics.drawable.Drawable;
 import android.view.animation.Animation;
 import android.widget.ImageView;
-import com.bumptech.glide.signature.EmptySignature;
 import com.bumptech.glide.load.Encoder;
 import com.bumptech.glide.load.Key;
 import com.bumptech.glide.load.MultiTransformation;
@@ -27,10 +26,12 @@
 import com.bumptech.glide.request.ThumbnailRequestCoordinator;
 import com.bumptech.glide.request.animation.GlideAnimationFactory;
 import com.bumptech.glide.request.animation.NoAnimation;
-import com.bumptech.glide.request.animation.ViewAnimation;
+import com.bumptech.glide.request.animation.ViewAnimationFactory;
 import com.bumptech.glide.request.animation.ViewPropertyAnimation;
+import com.bumptech.glide.request.animation.ViewPropertyAnimationFactory;
 import com.bumptech.glide.request.target.PreloadTarget;
 import com.bumptech.glide.request.target.Target;
+import com.bumptech.glide.signature.EmptySignature;
 import com.bumptech.glide.util.Util;
 
 import java.io.File;
@@ -367,7 +368,7 @@
      * @return This request builder.
      */
     public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> animate(int animationId) {
-        return animate(new ViewAnimation.ViewAnimationFactory<TranscodeType>(context, animationId));
+        return animate(new ViewAnimationFactory<TranscodeType>(context, animationId));
     }
 
     /**
@@ -387,7 +388,7 @@
      */
     @Deprecated
     public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> animate(Animation animation) {
-        return animate(new ViewAnimation.ViewAnimationFactory<TranscodeType>(animation));
+        return animate(new ViewAnimationFactory<TranscodeType>(animation));
     }
 
     /**
@@ -400,7 +401,7 @@
      */
     public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> animate(
             ViewPropertyAnimation.Animator animator) {
-        return animate(new ViewPropertyAnimation.ViewPropertyAnimationFactory<TranscodeType>(animator));
+        return animate(new ViewPropertyAnimationFactory<TranscodeType>(animator));
     }
 
     GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> animate(
diff --git a/library/src/main/java/com/bumptech/glide/GifRequestBuilder.java b/library/src/main/java/com/bumptech/glide/GifRequestBuilder.java
index 71d19ab..f26f13c 100644
--- a/library/src/main/java/com/bumptech/glide/GifRequestBuilder.java
+++ b/library/src/main/java/com/bumptech/glide/GifRequestBuilder.java
@@ -15,7 +15,7 @@
 import com.bumptech.glide.load.resource.transcode.ResourceTranscoder;
 import com.bumptech.glide.provider.LoadProvider;
 import com.bumptech.glide.request.RequestListener;
-import com.bumptech.glide.request.animation.DrawableCrossFadeViewAnimation;
+import com.bumptech.glide.request.animation.DrawableCrossFadeFactory;
 import com.bumptech.glide.request.animation.ViewPropertyAnimation;
 
 import java.io.File;
@@ -229,7 +229,7 @@
      */
     @Override
     public GifRequestBuilder<ModelType> crossFade() {
-        super.animate(new DrawableCrossFadeViewAnimation.DrawableCrossFadeFactory<GifDrawable>());
+        super.animate(new DrawableCrossFadeFactory<GifDrawable>());
         return this;
     }
 
@@ -238,7 +238,7 @@
      */
     @Override
     public GifRequestBuilder<ModelType> crossFade(int duration) {
-        super.animate(new DrawableCrossFadeViewAnimation.DrawableCrossFadeFactory<GifDrawable>(duration));
+        super.animate(new DrawableCrossFadeFactory<GifDrawable>(duration));
         return this;
     }
 
@@ -248,7 +248,7 @@
     @Deprecated
     @Override
     public GifRequestBuilder<ModelType> crossFade(Animation animation, int duration) {
-        super.animate(new DrawableCrossFadeViewAnimation.DrawableCrossFadeFactory<GifDrawable>(animation, duration));
+        super.animate(new DrawableCrossFadeFactory<GifDrawable>(animation, duration));
         return this;
     }
 
@@ -257,7 +257,7 @@
      */
     @Override
     public GifRequestBuilder<ModelType> crossFade(int animationId, int duration) {
-        super.animate(new DrawableCrossFadeViewAnimation.DrawableCrossFadeFactory<GifDrawable>(context, animationId,
+        super.animate(new DrawableCrossFadeFactory<GifDrawable>(context, animationId,
                 duration));
         return this;
     }
diff --git a/library/src/main/java/com/bumptech/glide/request/animation/DrawableCrossFadeFactory.java b/library/src/main/java/com/bumptech/glide/request/animation/DrawableCrossFadeFactory.java
new file mode 100644
index 0000000..5bd3305
--- /dev/null
+++ b/library/src/main/java/com/bumptech/glide/request/animation/DrawableCrossFadeFactory.java
@@ -0,0 +1,72 @@
+package com.bumptech.glide.request.animation;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+
+/**
+ * A factory class that produces a new {@link com.bumptech.glide.request.animation.GlideAnimation} that varies depending
+ * on whether or not the drawable was loaded from the memory cache and whether or not the drawable is the first
+ * image to be set on the target.
+ *
+ * <p>
+ *     Resources are usually loaded from the memory cache just before the user can see the view,
+ *     for example when the user changes screens or scrolls back and forth in a list. In those cases the user
+ *     typically does not expect to see an animation. As a result, when the resource is loaded from the memory
+ *     cache this factory produces an {@link com.bumptech.glide.request.animation.NoAnimation}.
+ * </p>
+ *
+ * @param <T> The type of the {@link android.graphics.drawable.Drawable} that will be animated.
+ */
+public class DrawableCrossFadeFactory<T extends Drawable> implements GlideAnimationFactory<T> {
+    private static final int DEFAULT_DURATION_MS = 300;
+    private final ViewAnimationFactory<T> animationFactory;
+    private final int duration;
+    private DrawableCrossFadeViewAnimation<T> animation;
+
+    public DrawableCrossFadeFactory() {
+        this(DEFAULT_DURATION_MS);
+    }
+
+    public DrawableCrossFadeFactory(int duration) {
+        this(new ViewAnimationFactory<T>(new DefaultAnimationFactory()), duration);
+    }
+
+    public DrawableCrossFadeFactory(Context context, int defaultAnimationId, int duration) {
+        this(new ViewAnimationFactory<T>(context, defaultAnimationId), duration);
+    }
+
+    public DrawableCrossFadeFactory(Animation defaultAnimation, int duration) {
+        this(new ViewAnimationFactory<T>(defaultAnimation), duration);
+    }
+
+    DrawableCrossFadeFactory(ViewAnimationFactory<T> animationFactory, int duration) {
+        this.animationFactory = animationFactory;
+        this.duration = duration;
+    }
+
+    @Override
+    public GlideAnimation<T> build(boolean isFromMemoryCache, boolean isFirstResource) {
+        if (isFromMemoryCache) {
+            return NoAnimation.get();
+        }
+
+        if (animation == null) {
+            GlideAnimation<T> defaultAnimation = animationFactory.build(false, isFirstResource);
+            animation = new DrawableCrossFadeViewAnimation<T>(defaultAnimation, duration);
+        }
+
+        return animation;
+    }
+
+    private static class DefaultAnimationFactory implements ViewAnimation.AnimationFactory {
+
+        @Override
+        public Animation build() {
+            AlphaAnimation animation = new AlphaAnimation(0f, 1f);
+            animation.setDuration(DEFAULT_DURATION_MS / 2);
+            return animation;
+        }
+    }
+}
diff --git a/library/src/main/java/com/bumptech/glide/request/animation/DrawableCrossFadeViewAnimation.java b/library/src/main/java/com/bumptech/glide/request/animation/DrawableCrossFadeViewAnimation.java
index bf2d3ee..a721aa1 100644
--- a/library/src/main/java/com/bumptech/glide/request/animation/DrawableCrossFadeViewAnimation.java
+++ b/library/src/main/java/com/bumptech/glide/request/animation/DrawableCrossFadeViewAnimation.java
@@ -1,10 +1,7 @@
 package com.bumptech.glide.request.animation;
 
-import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.TransitionDrawable;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
 
 /**
  * A cross fade {@link GlideAnimation} for {@link android.graphics.drawable.Drawable}s
@@ -15,65 +12,10 @@
  * @param <T> The type of the {@link android.graphics.drawable.Drawable} that will be animated.
  */
 public class DrawableCrossFadeViewAnimation<T extends Drawable> implements GlideAnimation<T> {
-    // 150 ms.
-    public static final int DEFAULT_DURATION = 300;
     private final GlideAnimation<T> defaultAnimation;
     private final int duration;
 
     /**
-     * A factory class that produces a new {@link GlideAnimation} that varies depending
-     * on whether or not the drawable was loaded from the memory cache and whether or not the drawable is the first
-     * image to be set on the target.
-     *
-     * <p>
-     *     Resources are usually loaded from the memory cache just before the user can see the view,
-     *     for example when the user changes screens or scrolls back and forth in a list. In those cases the user
-     *     typically does not expect to see an animation. As a result, when the resource is loaded from the memory
-     *     cache this factory producdes an {@link NoAnimation}.
-     * </p>
-     */
-    public static class DrawableCrossFadeFactory<T extends Drawable> implements GlideAnimationFactory<T> {
-        private final ViewAnimation.ViewAnimationFactory<T> animationFactory;
-        private final int duration;
-        private DrawableCrossFadeViewAnimation<T> animation;
-
-        public DrawableCrossFadeFactory() {
-            this(DEFAULT_DURATION);
-        }
-
-        public DrawableCrossFadeFactory(int duration) {
-            this(new ViewAnimation.ViewAnimationFactory<T>(new DefaultAnimationFactory()), duration);
-        }
-
-        public DrawableCrossFadeFactory(Context context, int defaultAnimationId, int duration) {
-            this(new ViewAnimation.ViewAnimationFactory<T>(context, defaultAnimationId), duration);
-        }
-
-        public DrawableCrossFadeFactory(Animation defaultAnimation, int duration) {
-            this(new ViewAnimation.ViewAnimationFactory<T>(defaultAnimation), duration);
-        }
-
-        DrawableCrossFadeFactory(ViewAnimation.ViewAnimationFactory<T> animationFactory, int duration) {
-            this.animationFactory = animationFactory;
-            this.duration = duration;
-        }
-
-        @Override
-        public GlideAnimation<T> build(boolean isFromMemoryCache, boolean isFirstResource) {
-            if (isFromMemoryCache) {
-                return NoAnimation.get();
-            }
-
-            if (animation == null) {
-                GlideAnimation<T> defaultAnimation = animationFactory.build(false, isFirstResource);
-                animation = new DrawableCrossFadeViewAnimation<T>(defaultAnimation, duration);
-            }
-
-            return animation;
-        }
-    }
-
-    /**
      * Constructor that takes a default animation and a duration in milliseconds that the cross fade animation should
      * last.
      * @param duration The duration that the cross fade animation should run if there is something to cross fade from
@@ -111,14 +53,4 @@
             return false;
         }
     }
-
-    private static class DefaultAnimationFactory implements ViewAnimation.AnimationFactory {
-
-        @Override
-        public Animation build() {
-            AlphaAnimation animation = new AlphaAnimation(0f, 1f);
-            animation.setDuration(DEFAULT_DURATION / 2);
-            return animation;
-        }
-    }
 }
diff --git a/library/src/main/java/com/bumptech/glide/request/animation/ViewAnimation.java b/library/src/main/java/com/bumptech/glide/request/animation/ViewAnimation.java
index aeea718..7f62719 100644
--- a/library/src/main/java/com/bumptech/glide/request/animation/ViewAnimation.java
+++ b/library/src/main/java/com/bumptech/glide/request/animation/ViewAnimation.java
@@ -1,9 +1,7 @@
 package com.bumptech.glide.request.animation;
 
-import android.content.Context;
 import android.view.View;
 import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
 
 /**
  * A {@link com.bumptech.glide.request.animation.GlideAnimation GlideAnimation} that can apply a
@@ -45,76 +43,6 @@
         return false;
     }
 
-    /**
-     * A {@link com.bumptech.glide.request.animation.GlideAnimationFactory} that produces ViewAnimations.
-     */
-    public static class ViewAnimationFactory<R> implements GlideAnimationFactory<R> {
-        private final AnimationFactory animationFactory;
-        private GlideAnimation<R> glideAnimation;
-
-        public ViewAnimationFactory(Animation animation) {
-            this(new ConcreteAnimationFactory(animation));
-        }
-
-        public ViewAnimationFactory(Context context, int animationId) {
-            this(new ResourceAnimationFactory(context, animationId));
-        }
-
-        ViewAnimationFactory(AnimationFactory animationFactory) {
-            this.animationFactory = animationFactory;
-        }
-
-        /**
-         * Returns a new {@link com.bumptech.glide.request.animation.GlideAnimation} for the given arguments. If
-         * isFromMemoryCache is {@code true} or isFirstImage is {@code false}, returns a
-         * {@link com.bumptech.glide.request.animation.NoAnimation} and otherwise returns a new
-         * {@link com.bumptech.glide.request.animation.ViewAnimation}.
-         *
-         * @param isFromMemoryCache {@inheritDoc}
-         * @param isFirstResource {@inheritDoc}
-         */
-        @Override
-        public GlideAnimation<R> build(boolean isFromMemoryCache, boolean isFirstResource) {
-            if (isFromMemoryCache || !isFirstResource) {
-                return NoAnimation.get();
-            }
-
-            if (glideAnimation == null) {
-                glideAnimation = new ViewAnimation<R>(animationFactory);
-            }
-
-            return glideAnimation;
-        }
-    }
-
-    private static class ConcreteAnimationFactory implements AnimationFactory {
-        private final Animation animation;
-
-        public ConcreteAnimationFactory(Animation animation) {
-            this.animation = animation;
-        }
-
-        @Override
-        public Animation build() {
-            return animation;
-        }
-    }
-
-    private static class ResourceAnimationFactory implements AnimationFactory {
-        private final Context context;
-        private final int animationId;
-
-        public ResourceAnimationFactory(Context context, int animationId) {
-            this.context = context.getApplicationContext();
-            this.animationId = animationId;
-        }
-
-        @Override
-        public Animation build() {
-            return AnimationUtils.loadAnimation(context, animationId);
-        }
-    }
-
     interface AnimationFactory {
         Animation build();
     }
diff --git a/library/src/main/java/com/bumptech/glide/request/animation/ViewAnimationFactory.java b/library/src/main/java/com/bumptech/glide/request/animation/ViewAnimationFactory.java
new file mode 100644
index 0000000..df2761a
--- /dev/null
+++ b/library/src/main/java/com/bumptech/glide/request/animation/ViewAnimationFactory.java
@@ -0,0 +1,78 @@
+package com.bumptech.glide.request.animation;
+
+import android.content.Context;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+
+/**
+ * A {@link com.bumptech.glide.request.animation.GlideAnimationFactory} that produces
+ * {@link com.bumptech.glide.request.animation.ViewAnimation}s.
+ *
+ * @param <R> The type of the resource displayed in the view that is animated
+ */
+public class ViewAnimationFactory<R> implements GlideAnimationFactory<R> {
+    private final ViewAnimation.AnimationFactory animationFactory;
+    private GlideAnimation<R> glideAnimation;
+
+    public ViewAnimationFactory(Animation animation) {
+        this(new ConcreteAnimationFactory(animation));
+    }
+
+    public ViewAnimationFactory(Context context, int animationId) {
+        this(new ResourceAnimationFactory(context, animationId));
+    }
+
+    ViewAnimationFactory(ViewAnimation.AnimationFactory animationFactory) {
+        this.animationFactory = animationFactory;
+    }
+
+    /**
+     * Returns a new {@link com.bumptech.glide.request.animation.GlideAnimation} for the given arguments. If
+     * isFromMemoryCache is {@code true} or isFirstImage is {@code false}, returns a
+     * {@link com.bumptech.glide.request.animation.NoAnimation} and otherwise returns a new
+     * {@link com.bumptech.glide.request.animation.ViewAnimation}.
+     *
+     * @param isFromMemoryCache {@inheritDoc}
+     * @param isFirstResource   {@inheritDoc}
+     */
+    @Override
+    public GlideAnimation<R> build(boolean isFromMemoryCache, boolean isFirstResource) {
+        if (isFromMemoryCache || !isFirstResource) {
+            return NoAnimation.get();
+        }
+
+        if (glideAnimation == null) {
+            glideAnimation = new ViewAnimation<R>(animationFactory);
+        }
+
+        return glideAnimation;
+    }
+
+    private static class ConcreteAnimationFactory implements ViewAnimation.AnimationFactory {
+        private final Animation animation;
+
+        public ConcreteAnimationFactory(Animation animation) {
+            this.animation = animation;
+        }
+
+        @Override
+        public Animation build() {
+            return animation;
+        }
+    }
+
+    private static class ResourceAnimationFactory implements ViewAnimation.AnimationFactory {
+        private final Context context;
+        private final int animationId;
+
+        public ResourceAnimationFactory(Context context, int animationId) {
+            this.context = context.getApplicationContext();
+            this.animationId = animationId;
+        }
+
+        @Override
+        public Animation build() {
+            return AnimationUtils.loadAnimation(context, animationId);
+        }
+    }
+}
diff --git a/library/src/main/java/com/bumptech/glide/request/animation/ViewPropertyAnimation.java b/library/src/main/java/com/bumptech/glide/request/animation/ViewPropertyAnimation.java
index 9c664df..6b42e81 100644
--- a/library/src/main/java/com/bumptech/glide/request/animation/ViewPropertyAnimation.java
+++ b/library/src/main/java/com/bumptech/glide/request/animation/ViewPropertyAnimation.java
@@ -54,34 +54,4 @@
         void animate(View view);
     }
 
-    /**
-     * A {@link com.bumptech.glide.request.animation.GlideAnimationFactory} that produces ViewPropertyAnimations.
-     */
-    public static class ViewPropertyAnimationFactory<R> implements GlideAnimationFactory<R> {
-        private final Animator animator;
-        private ViewPropertyAnimation<R> animation;
-
-        public ViewPropertyAnimationFactory(Animator animator) {
-            this.animator = animator;
-        }
-
-        /**
-         * Returns a new {@link com.bumptech.glide.request.animation.GlideAnimation} for the given arguments. If
-         * isMemoryCache is {@code true} or isFirstImage is {@code false}, returns a
-         * {@link com.bumptech.glide.request.animation.NoAnimation} and otherwise returns a new
-         * {@link com.bumptech.glide.request.animation.ViewPropertyAnimation} for the
-         * {@link com.bumptech.glide.request.animation.ViewPropertyAnimation.Animator} provided in the constructor.
-         */
-        @Override
-        public GlideAnimation<R> build(boolean isFromMemoryCache, boolean isFirstResource) {
-            if (isFromMemoryCache || !isFirstResource) {
-                return NoAnimation.get();
-            }
-            if (animation == null) {
-                animation = new ViewPropertyAnimation<R>(animator);
-            }
-
-            return animation;
-        }
-    }
 }
diff --git a/library/src/main/java/com/bumptech/glide/request/animation/ViewPropertyAnimationFactory.java b/library/src/main/java/com/bumptech/glide/request/animation/ViewPropertyAnimationFactory.java
new file mode 100644
index 0000000..f8c5002
--- /dev/null
+++ b/library/src/main/java/com/bumptech/glide/request/animation/ViewPropertyAnimationFactory.java
@@ -0,0 +1,34 @@
+package com.bumptech.glide.request.animation;
+
+/**
+ * A {@link GlideAnimationFactory} that produces ViewPropertyAnimations.
+ *
+ * @param <R> The type of the resource displayed in the view that is animated
+ */
+public class ViewPropertyAnimationFactory<R> implements GlideAnimationFactory<R> {
+    private final ViewPropertyAnimation.Animator animator;
+    private ViewPropertyAnimation<R> animation;
+
+    public ViewPropertyAnimationFactory(ViewPropertyAnimation.Animator animator) {
+        this.animator = animator;
+    }
+
+    /**
+     * Returns a new {@link GlideAnimation} for the given arguments. If
+     * isMemoryCache is {@code true} or isFirstImage is {@code false}, returns a
+     * {@link NoAnimation} and otherwise returns a new
+     * {@link ViewPropertyAnimation} for the
+     * {@link com.bumptech.glide.request.animation.ViewPropertyAnimation.Animator} provided in the constructor.
+     */
+    @Override
+    public GlideAnimation<R> build(boolean isFromMemoryCache, boolean isFirstResource) {
+        if (isFromMemoryCache || !isFirstResource) {
+            return NoAnimation.get();
+        }
+        if (animation == null) {
+            animation = new ViewPropertyAnimation<R>(animator);
+        }
+
+        return animation;
+    }
+}