Use the view size and scale type to restrict NIV requests.

Change-Id: I77e705c09937f78af746cd73b6e6d94fd4ee2a03
diff --git a/src/main/java/com/android/volley/toolbox/ImageLoader.java b/src/main/java/com/android/volley/toolbox/ImageLoader.java
index 5348dc6..6dc86bb 100644
--- a/src/main/java/com/android/volley/toolbox/ImageLoader.java
+++ b/src/main/java/com/android/volley/toolbox/ImageLoader.java
@@ -20,6 +20,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
 
 import com.android.volley.Request;
 import com.android.volley.RequestQueue;
@@ -167,7 +168,7 @@
      * @param defaultImage Optional default image to return until the actual image is loaded.
      */
     public ImageContainer get(String requestUrl, final ImageListener listener) {
-        return get(requestUrl, listener, 0, 0);
+        return get(requestUrl, listener, 0, 0, ScaleType.CENTER_INSIDE);
     }
 
     /**
@@ -179,11 +180,13 @@
      * @param imageListener The listener to call when the remote image is loaded
      * @param maxWidth The maximum width of the returned image.
      * @param maxHeight The maximum height of the returned image.
+     * @param scaleType The ImageViews ScaleType used to calculate the needed image size.
      * @return A container object that contains all of the properties of the request, as well as
      *     the currently available image (default if remote is not loaded).
      */
     public ImageContainer get(String requestUrl, ImageListener imageListener,
-            int maxWidth, int maxHeight) {
+            int maxWidth, int maxHeight, ScaleType scaleType) {
+        
         // only fulfill requests that were initiated from the main thread.
         throwIfNotOnMainThread();
 
@@ -215,7 +218,8 @@
 
         // The request is not already in flight. Send the new request to the network and
         // track it.
-        Request<Bitmap> newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight, cacheKey);
+        Request<Bitmap> newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight, scaleType,
+                cacheKey);
 
         mRequestQueue.add(newRequest);
         mInFlightRequests.put(cacheKey,
@@ -223,14 +227,14 @@
         return imageContainer;
     }
 
-    protected Request<Bitmap> makeImageRequest(String requestUrl, int maxWidth, int maxHeight, final String cacheKey) {
+    protected Request<Bitmap> makeImageRequest(String requestUrl, int maxWidth, int maxHeight,
+            ScaleType scaleType, final String cacheKey) {
         return new ImageRequest(requestUrl, new Listener<Bitmap>() {
             @Override
             public void onResponse(Bitmap response) {
                 onGetImageSuccess(cacheKey, response);
             }
-        }, maxWidth, maxHeight,
-        Config.RGB_565, new ErrorListener() {
+        }, maxWidth, maxHeight, scaleType, Config.RGB_565, new ErrorListener() {
             @Override
             public void onErrorResponse(VolleyError error) {
                 onGetImageError(cacheKey, error);
diff --git a/src/main/java/com/android/volley/toolbox/ImageRequest.java b/src/main/java/com/android/volley/toolbox/ImageRequest.java
index 2ebe015..8a09ea1 100644
--- a/src/main/java/com/android/volley/toolbox/ImageRequest.java
+++ b/src/main/java/com/android/volley/toolbox/ImageRequest.java
@@ -26,6 +26,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Bitmap.Config;
 import android.graphics.BitmapFactory;
+import android.widget.ImageView.ScaleType;
 
 /**
  * A canned request for getting an image at a given URL and calling
@@ -45,6 +46,7 @@
     private final Config mDecodeConfig;
     private final int mMaxWidth;
     private final int mMaxHeight;
+    private ScaleType mScaleType;
 
     /** Decoding lock so that we don't decode more than one image at a time (to avoid OOM's) */
     private static final Object sDecodeLock = new Object();
@@ -63,18 +65,20 @@
      * @param maxWidth Maximum width to decode this bitmap to, or zero for none
      * @param maxHeight Maximum height to decode this bitmap to, or zero for
      *            none
+     * @param scaleType The ImageViews ScaleType used to calculate the needed image size.
      * @param decodeConfig Format to decode the bitmap to
      * @param errorListener Error listener, or null to ignore errors
      */
     public ImageRequest(String url, Response.Listener<Bitmap> listener, int maxWidth, int maxHeight,
-            Config decodeConfig, Response.ErrorListener errorListener) {
-        super(Method.GET, url, errorListener);
+            ScaleType scaleType, Config decodeConfig, Response.ErrorListener errorListener) {
+        super(Method.GET, url, errorListener); 
         setRetryPolicy(
                 new DefaultRetryPolicy(IMAGE_TIMEOUT_MS, IMAGE_MAX_RETRIES, IMAGE_BACKOFF_MULT));
         mListener = listener;
         mDecodeConfig = decodeConfig;
         mMaxWidth = maxWidth;
         mMaxHeight = maxHeight;
+        mScaleType = scaleType;
     }
 
     @Override
@@ -92,14 +96,24 @@
      *        maintain aspect ratio with primary dimension
      * @param actualPrimary Actual size of the primary dimension
      * @param actualSecondary Actual size of the secondary dimension
+     * @param scaleType The ScaleType used to calculate the needed image size.
      */
     private static int getResizedDimension(int maxPrimary, int maxSecondary, int actualPrimary,
-            int actualSecondary) {
+            int actualSecondary, ScaleType scaleType) {
+
         // If no dominant value at all, just return the actual.
-        if (maxPrimary == 0 && maxSecondary == 0) {
+        if ((maxPrimary == 0) && (maxSecondary == 0)) {
             return actualPrimary;
         }
 
+        // If ScaleType.FIT_XY fill the whole rectangle, ignore ratio.
+        if (scaleType == ScaleType.FIT_XY) {
+            if (maxPrimary == 0) {
+                return actualPrimary;
+            }
+            return maxPrimary;
+        }
+
         // If primary is unspecified, scale primary to match secondary's scaling ratio.
         if (maxPrimary == 0) {
             double ratio = (double) maxSecondary / (double) actualSecondary;
@@ -112,7 +126,16 @@
 
         double ratio = (double) actualSecondary / (double) actualPrimary;
         int resized = maxPrimary;
-        if (resized * ratio > maxSecondary) {
+
+        // If ScaleType.CENTER_CROP fill the whole rectangle, preserve aspect ratio.
+        if (scaleType == ScaleType.CENTER_CROP) {
+            if ((resized * ratio) < maxSecondary) {
+                resized = (int) (maxSecondary / ratio);
+            }
+            return resized;
+        }
+
+        if ((resized * ratio) > maxSecondary) {
             resized = (int) (maxSecondary / ratio);
         }
         return resized;
@@ -150,9 +173,9 @@
 
             // Then compute the dimensions we would ideally like to decode to.
             int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight,
-                    actualWidth, actualHeight);
+                    actualWidth, actualHeight, mScaleType);
             int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth,
-                    actualHeight, actualWidth);
+                    actualHeight, actualWidth, mScaleType);
 
             // Decode to the nearest power of two scaling factor.
             decodeOptions.inJustDecodeBounds = false;
diff --git a/src/main/java/com/android/volley/toolbox/NetworkImageView.java b/src/main/java/com/android/volley/toolbox/NetworkImageView.java
index 692e988..324dbc0 100644
--- a/src/main/java/com/android/volley/toolbox/NetworkImageView.java
+++ b/src/main/java/com/android/volley/toolbox/NetworkImageView.java
@@ -103,6 +103,7 @@
     void loadImageIfNecessary(final boolean isInLayoutPass) {
         int width = getWidth();
         int height = getHeight();
+        ScaleType scaleType = getScaleType();
 
         boolean wrapWidth = false, wrapHeight = false;
         if (getLayoutParams() != null) {
@@ -177,7 +178,7 @@
                             setImageResource(mDefaultImageId);
                         }
                     }
-                }, maxWidth, maxHeight);
+                }, maxWidth, maxHeight, scaleType);
 
         // update the ImageContainer to be the new bitmap container.
         mImageContainer = newContainer;
diff --git a/src/test/java/com/android/volley/toolbox/ImageRequestTest.java b/src/test/java/com/android/volley/toolbox/ImageRequestTest.java
index 2f4495a..bd98e7d 100644
--- a/src/test/java/com/android/volley/toolbox/ImageRequestTest.java
+++ b/src/test/java/com/android/volley/toolbox/ImageRequestTest.java
@@ -18,7 +18,7 @@
 
 import android.graphics.Bitmap;
 import android.graphics.Bitmap.Config;
-
+import android.widget.ImageView.ScaleType;
 import com.android.volley.NetworkResponse;
 import com.android.volley.Response;
 import org.junit.Test;
@@ -47,30 +47,84 @@
         ShadowBitmapFactory.provideWidthAndHeightHints("fake", 1024, 500);
         NetworkResponse jpeg = new NetworkResponse(jpegBytes);
 
-        // No resize
-        verifyResize(jpeg, 0, 0, 1024, 500);
+        // Scale the image uniformly (maintain the image's aspect ratio) so that
+        // both dimensions (width and height) of the image will be equal to or
+        // less than the corresponding dimension of the view.
+        ScaleType scalteType = ScaleType.CENTER_INSIDE;
 
         // Exact sizes
-        verifyResize(jpeg, 512, 250, 512, 250); // exactly half
-        verifyResize(jpeg, 511, 249, 509, 249); // just under half
-        verifyResize(jpeg, 1080, 500, 1024, 500); // larger
-        verifyResize(jpeg, 500, 500, 500, 244); // keep same ratio
+        verifyResize(jpeg, 512, 250, scalteType, 512, 250); // exactly half
+        verifyResize(jpeg, 511, 249, scalteType, 509, 249); // just under half
+        verifyResize(jpeg, 1080, 500, scalteType, 1024, 500); // larger
+        verifyResize(jpeg, 500, 500, scalteType, 500, 244); // keep same ratio
 
         // Specify only width, preserve aspect ratio
-        verifyResize(jpeg, 512, 0, 512, 250);
-        verifyResize(jpeg, 800, 0, 800, 390);
-        verifyResize(jpeg, 1024, 0, 1024, 500);
+        verifyResize(jpeg, 512, 0, scalteType, 512, 250);
+        verifyResize(jpeg, 800, 0, scalteType, 800, 390);
+        verifyResize(jpeg, 1024, 0, scalteType, 1024, 500);
 
         // Specify only height, preserve aspect ratio
-        verifyResize(jpeg, 0, 250, 512, 250);
-        verifyResize(jpeg, 0, 391, 800, 391);
-        verifyResize(jpeg, 0, 500, 1024, 500);
+        verifyResize(jpeg, 0, 250, scalteType, 512, 250);
+        verifyResize(jpeg, 0, 391, scalteType, 800, 391);
+        verifyResize(jpeg, 0, 500, scalteType, 1024, 500);
+
+        // No resize
+        verifyResize(jpeg, 0, 0, scalteType, 1024, 500);
+
+
+        // Scale the image uniformly (maintain the image's aspect ratio) so that
+        // both dimensions (width and height) of the image will be equal to or
+        // larger than the corresponding dimension of the view.
+        scalteType = ScaleType.CENTER_CROP;
+
+        // Exact sizes
+        verifyResize(jpeg, 512, 250, scalteType, 512, 250);
+        verifyResize(jpeg, 511, 249, scalteType, 511, 249);
+        verifyResize(jpeg, 1080, 500, scalteType, 1024, 500);
+        verifyResize(jpeg, 500, 500, scalteType, 1024, 500);
+
+        // Specify only width
+        verifyResize(jpeg, 512, 0, scalteType, 512, 250);
+        verifyResize(jpeg, 800, 0, scalteType, 800, 390);
+        verifyResize(jpeg, 1024, 0, scalteType, 1024, 500);
+
+        // Specify only height
+        verifyResize(jpeg, 0, 250, scalteType, 512, 250);
+        verifyResize(jpeg, 0, 391, scalteType, 800, 391);
+        verifyResize(jpeg, 0, 500, scalteType, 1024, 500);
+
+        // No resize
+        verifyResize(jpeg, 0, 0, scalteType, 1024, 500);
+
+
+        // Scale in X and Y independently, so that src matches dst exactly. This
+        // may change the aspect ratio of the src.
+        scalteType = ScaleType.FIT_XY;
+
+        // Exact sizes
+        verifyResize(jpeg, 512, 250, scalteType, 512, 250);
+        verifyResize(jpeg, 511, 249, scalteType, 511, 249);
+        verifyResize(jpeg, 1080, 500, scalteType, 1024, 500);
+        verifyResize(jpeg, 500, 500, scalteType, 500, 500);
+
+        // Specify only width
+        verifyResize(jpeg, 512, 0, scalteType, 512, 500);
+        verifyResize(jpeg, 800, 0, scalteType, 800, 500);
+        verifyResize(jpeg, 1024, 0, scalteType, 1024, 500);
+
+        // Specify only height
+        verifyResize(jpeg, 0, 250, scalteType, 1024, 250);
+        verifyResize(jpeg, 0, 391, scalteType, 1024, 391);
+        verifyResize(jpeg, 0, 500, scalteType, 1024, 500);
+
+        // No resize
+        verifyResize(jpeg, 0, 0, scalteType, 1024, 500);
     }
 
     private void verifyResize(NetworkResponse networkResponse, int maxWidth, int maxHeight,
-            int expectedWidth, int expectedHeight) {
-        ImageRequest request = new ImageRequest(
-                "", null, maxWidth, maxHeight, Config.RGB_565, null);
+                              ScaleType scaleType, int expectedWidth, int expectedHeight) {
+        ImageRequest request = new ImageRequest("", null, maxWidth, maxHeight, scaleType,
+                Config.RGB_565, null);
         Response<Bitmap> response = request.parseNetworkResponse(networkResponse);
         assertNotNull(response);
         assertTrue(response.isSuccess());
diff --git a/src/test/java/com/android/volley/toolbox/NetworkImageViewTest.java b/src/test/java/com/android/volley/toolbox/NetworkImageViewTest.java
index 48c81b6..bc2cc29 100644
--- a/src/test/java/com/android/volley/toolbox/NetworkImageViewTest.java
+++ b/src/test/java/com/android/volley/toolbox/NetworkImageViewTest.java
@@ -1,6 +1,7 @@
 package com.android.volley.toolbox;
 
 import android.view.ViewGroup.LayoutParams;
+import android.widget.ImageView.ScaleType;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -43,7 +44,7 @@
         public int lastMaxHeight;
 
         public ImageContainer get(String requestUrl, ImageListener imageListener, int maxWidth,
-                int maxHeight) {
+                int maxHeight, ScaleType scaleType) {
             lastRequestUrl = requestUrl;
             lastMaxWidth = maxWidth;
             lastMaxHeight = maxHeight;