Delay image fetch until view size is stable

Fixes a case where we load an image for a view 
part way through a property animation so when
the animation finishes the loaded image only fits
in part of the view. 
diff --git a/src/com/bumptech/photos/view/ImagePresenter.java b/src/com/bumptech/photos/view/ImagePresenter.java
index 1226eb2..936a7fd 100644
--- a/src/com/bumptech/photos/view/ImagePresenter.java
+++ b/src/com/bumptech/photos/view/ImagePresenter.java
@@ -6,9 +6,10 @@
 
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.SystemClock;
 import android.view.ViewTreeObserver;
 import android.widget.ImageView;
-import com.bumptech.photos.util.Log;
 import com.bumptech.photos.view.loader.ImageLoader;
 
 import java.lang.ref.WeakReference;
@@ -21,8 +22,6 @@
  * To change this template use File | Settings | File Templates.
  */
 public class ImagePresenter<T> {
-    private Object pathToken;
-    private Object imageToken;
 
     public static class Builder<T> {
         private ImageView imageView;
@@ -74,6 +73,12 @@
         }
     }
 
+    private static final String PENDING_LOAD_TOKEN = "pending_load";
+    private static final int PENDING_LOAD_DELAY = 20; //60 fps = 1000/60 = 16.67 ms
+
+    private Object pathToken;
+    private Object imageToken;
+
     private final ImageLoader<T> imageLoader;
     private final Drawable placeholderDrawable;
     private final ImageSetCallback imageSetCallback;
@@ -83,27 +88,21 @@
     private int height = 0;
     private int width = 0;
 
+    private Handler handler = new Handler();
+
     private T currentModel;
     private int currentCount;
 
     private boolean isImageSet;
     private boolean loadedFromCache = false;
 
-    private boolean setLayoutListener = false;
     private final Runnable getDimens = new Runnable() {
-
         @Override
         public void run() {
-            Log.d("AP: getDimens run width=" + width + " height=" + height);
             width = imageView.getWidth();
             height = imageView.getHeight();
-            if (width == 0 || height == 0) {
-                if (!setLayoutListener) {
-                    imageView.getViewTreeObserver().addOnGlobalLayoutListener(new SizeObserver(imageView, ImagePresenter.this));
-                    setLayoutListener = true;
-                }
-            } else if (pendingLoad != null) {
-                imageView.post(pendingLoad);
+            if (width != 0 && height != 0) {
+                postPendingLoad();
             }
         }
     };
@@ -124,6 +123,7 @@
         }
         this.coordinator = builder.coordinator;
         this.imageSetCallback = builder.imageSetCallback;
+        imageView.getViewTreeObserver().addOnGlobalLayoutListener(new SizeObserver(imageView, ImagePresenter.this));
     }
 
     public ImageView getImageView() {
@@ -143,6 +143,7 @@
                 @Override
                 public void run() {
                     fetchPath(model, loadCount);
+                    pendingLoad = null;
                 }
             };
             getDimens();
@@ -179,6 +180,17 @@
         return height;
     }
 
+    private void postPendingLoad() {
+        if (pendingLoad == null) return;
+
+        //If an image view is actively changing sizes, we want to delay our resize job until
+        //the size has stabilized so that the image we load will match the final size, rather than some
+        //size part way through the change. One example of this is as part of an animation where a view is
+        //expanding or shrinking
+        handler.removeCallbacksAndMessages(PENDING_LOAD_TOKEN);
+        handler.postAtTime(pendingLoad, PENDING_LOAD_TOKEN, SystemClock.uptimeMillis() + PENDING_LOAD_DELAY);
+    }
+
     private void fetchPath(final T model, final int loadCount) {
         pathToken = imageLoader.fetchPath(model, getWidth(), getHeight(), new ImageLoader.PathReadyCallback() {
             @Override