1d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru/*
2d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Copyright (C) 2011 The Android Open Source Project
3d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru *
4d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Licensed under the Apache License, Version 2.0 (the "License");
5d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * you may not use this file except in compliance with the License.
6d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * You may obtain a copy of the License at
7d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru *
8d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru *      http://www.apache.org/licenses/LICENSE-2.0
9d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru *
10d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Unless required by applicable law or agreed to in writing, software
11d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * distributed under the License is distributed on an "AS IS" BASIS,
12d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * See the License for the specific language governing permissions and
14d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * limitations under the License.
15d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */
16d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
17d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Querupackage com.android.volley.toolbox;
18d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
19d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.DefaultRetryPolicy;
20d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.NetworkResponse;
21d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.ParseError;
22d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.Request;
23d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.Response;
24d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.VolleyLog;
25e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru
26d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport android.graphics.Bitmap;
27d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport android.graphics.Bitmap.Config;
28d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport android.graphics.BitmapFactory;
29ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmannimport android.widget.ImageView.ScaleType;
30d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
31d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru/**
32d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * A canned request for getting an image at a given URL and calling
33d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * back with a decoded Bitmap.
34d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */
35d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Querupublic class ImageRequest extends Request<Bitmap> {
36d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    /** Socket timeout in milliseconds for image requests */
37d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    private static final int IMAGE_TIMEOUT_MS = 1000;
38d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
39d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    /** Default number of retries for image requests */
40d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    private static final int IMAGE_MAX_RETRIES = 2;
41d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
42d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    /** Default backoff multiplier for image requests */
43d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    private static final float IMAGE_BACKOFF_MULT = 2f;
44d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
45d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    private final Response.Listener<Bitmap> mListener;
46d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    private final Config mDecodeConfig;
47d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    private final int mMaxWidth;
48d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    private final int mMaxHeight;
49ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann    private ScaleType mScaleType;
50d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
51d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    /** Decoding lock so that we don't decode more than one image at a time (to avoid OOM's) */
52d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    private static final Object sDecodeLock = new Object();
53d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
54d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    /**
55d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * Creates a new image request, decoding to a maximum specified width and
56d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * height. If both width and height are zero, the image will be decoded to
57d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * its natural size. If one of the two is nonzero, that dimension will be
58d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * clamped and the other one will be set to preserve the image's aspect
59d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * ratio. If both width and height are nonzero, the image will be decoded to
60d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * be fit in the rectangle of dimensions width x height while keeping its
61d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * aspect ratio.
62d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     *
63d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * @param url URL of the image
64d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * @param listener Listener to receive the decoded bitmap
65d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * @param maxWidth Maximum width to decode this bitmap to, or zero for none
66d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * @param maxHeight Maximum height to decode this bitmap to, or zero for
67d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     *            none
68ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann     * @param scaleType The ImageViews ScaleType used to calculate the needed image size.
69d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * @param decodeConfig Format to decode the bitmap to
70d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * @param errorListener Error listener, or null to ignore errors
71d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     */
72d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    public ImageRequest(String url, Response.Listener<Bitmap> listener, int maxWidth, int maxHeight,
73ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann            ScaleType scaleType, Config decodeConfig, Response.ErrorListener errorListener) {
74ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann        super(Method.GET, url, errorListener);
75d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        setRetryPolicy(
76d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                new DefaultRetryPolicy(IMAGE_TIMEOUT_MS, IMAGE_MAX_RETRIES, IMAGE_BACKOFF_MULT));
77d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        mListener = listener;
78d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        mDecodeConfig = decodeConfig;
79d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        mMaxWidth = maxWidth;
80d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        mMaxHeight = maxHeight;
81ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann        mScaleType = scaleType;
82d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
83d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
84cf498d16c616989edb5e2791da801bd29f548628Ficus Kirkpatrick    /**
85cf498d16c616989edb5e2791da801bd29f548628Ficus Kirkpatrick     * For API compatibility with the pre-ScaleType variant of the constructor. Equivalent to
86cf498d16c616989edb5e2791da801bd29f548628Ficus Kirkpatrick     * the normal constructor with {@code ScaleType.CENTER_INSIDE}.
87cf498d16c616989edb5e2791da801bd29f548628Ficus Kirkpatrick     */
88cf498d16c616989edb5e2791da801bd29f548628Ficus Kirkpatrick    @Deprecated
89cf498d16c616989edb5e2791da801bd29f548628Ficus Kirkpatrick    public ImageRequest(String url, Response.Listener<Bitmap> listener, int maxWidth, int maxHeight,
90cf498d16c616989edb5e2791da801bd29f548628Ficus Kirkpatrick            Config decodeConfig, Response.ErrorListener errorListener) {
91cf498d16c616989edb5e2791da801bd29f548628Ficus Kirkpatrick        this(url, listener, maxWidth, maxHeight,
92cf498d16c616989edb5e2791da801bd29f548628Ficus Kirkpatrick                ScaleType.CENTER_INSIDE, decodeConfig, errorListener);
93cf498d16c616989edb5e2791da801bd29f548628Ficus Kirkpatrick    }
94d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    @Override
95d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    public Priority getPriority() {
96d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        return Priority.LOW;
97d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
98d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
99d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    /**
100d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * Scales one side of a rectangle to fit aspect ratio.
101d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     *
102d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * @param maxPrimary Maximum size of the primary dimension (i.e. width for
103d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     *        max width), or zero to maintain aspect ratio with secondary
104d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     *        dimension
105d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * @param maxSecondary Maximum size of the secondary dimension, or zero to
106d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     *        maintain aspect ratio with primary dimension
107d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * @param actualPrimary Actual size of the primary dimension
108d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * @param actualSecondary Actual size of the secondary dimension
109ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann     * @param scaleType The ScaleType used to calculate the needed image size.
110d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     */
111d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    private static int getResizedDimension(int maxPrimary, int maxSecondary, int actualPrimary,
112ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann            int actualSecondary, ScaleType scaleType) {
113ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann
114d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        // If no dominant value at all, just return the actual.
115ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann        if ((maxPrimary == 0) && (maxSecondary == 0)) {
116d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            return actualPrimary;
117d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        }
118d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
119ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann        // If ScaleType.FIT_XY fill the whole rectangle, ignore ratio.
120ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann        if (scaleType == ScaleType.FIT_XY) {
121ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann            if (maxPrimary == 0) {
122ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann                return actualPrimary;
123ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann            }
124ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann            return maxPrimary;
125ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann        }
126ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann
127d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        // If primary is unspecified, scale primary to match secondary's scaling ratio.
128d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        if (maxPrimary == 0) {
129d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            double ratio = (double) maxSecondary / (double) actualSecondary;
130d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            return (int) (actualPrimary * ratio);
131d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        }
132d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
133d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        if (maxSecondary == 0) {
134d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            return maxPrimary;
135d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        }
136d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
137d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        double ratio = (double) actualSecondary / (double) actualPrimary;
138d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        int resized = maxPrimary;
139ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann
140ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann        // If ScaleType.CENTER_CROP fill the whole rectangle, preserve aspect ratio.
141ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann        if (scaleType == ScaleType.CENTER_CROP) {
142ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann            if ((resized * ratio) < maxSecondary) {
143ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann                resized = (int) (maxSecondary / ratio);
144ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann            }
145ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann            return resized;
146ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann        }
147ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann
148ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann        if ((resized * ratio) > maxSecondary) {
149d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            resized = (int) (maxSecondary / ratio);
150d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        }
151d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        return resized;
152d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
153d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
154d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    @Override
155d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    protected Response<Bitmap> parseNetworkResponse(NetworkResponse response) {
156d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        // Serialize all decode on a global lock to reduce concurrent heap usage.
157d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        synchronized (sDecodeLock) {
158d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            try {
159d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                return doParse(response);
160d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            } catch (OutOfMemoryError e) {
161d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                VolleyLog.e("Caught OOM for %d byte image, url=%s", response.data.length, getUrl());
162d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                return Response.error(new ParseError(e));
163d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            }
164d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        }
165d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
166d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
167d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    /**
168d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * The real guts of parseNetworkResponse. Broken out for readability.
169d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     */
170d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    private Response<Bitmap> doParse(NetworkResponse response) {
171d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        byte[] data = response.data;
172d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
173d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        Bitmap bitmap = null;
174d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        if (mMaxWidth == 0 && mMaxHeight == 0) {
175d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            decodeOptions.inPreferredConfig = mDecodeConfig;
176d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
177d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        } else {
178d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            // If we have to resize this image, first get the natural bounds.
179d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            decodeOptions.inJustDecodeBounds = true;
180d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
181d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            int actualWidth = decodeOptions.outWidth;
182d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            int actualHeight = decodeOptions.outHeight;
183d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
184d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            // Then compute the dimensions we would ideally like to decode to.
185d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight,
186ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann                    actualWidth, actualHeight, mScaleType);
187d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth,
188ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann                    actualHeight, actualWidth, mScaleType);
189d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
190d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            // Decode to the nearest power of two scaling factor.
191d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            decodeOptions.inJustDecodeBounds = false;
192d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            // TODO(ficus): Do we need this or is it okay since API 8 doesn't support it?
193d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            // decodeOptions.inPreferQualityOverSpeed = PREFER_QUALITY_OVER_SPEED;
194d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            decodeOptions.inSampleSize =
195d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight);
196d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            Bitmap tempBitmap =
197d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
198d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
1996772bce3d3322ccbcf6481545ffe895d5d401b39Jean-Baptiste Queru            // If necessary, scale down to the maximal acceptable size.
2006772bce3d3322ccbcf6481545ffe895d5d401b39Jean-Baptiste Queru            if (tempBitmap != null && (tempBitmap.getWidth() > desiredWidth ||
2016772bce3d3322ccbcf6481545ffe895d5d401b39Jean-Baptiste Queru                    tempBitmap.getHeight() > desiredHeight)) {
202d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                bitmap = Bitmap.createScaledBitmap(tempBitmap,
203d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                        desiredWidth, desiredHeight, true);
204d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                tempBitmap.recycle();
205d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            } else {
206d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                bitmap = tempBitmap;
207d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            }
208d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        }
209d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
210d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        if (bitmap == null) {
2117c8e29e4cc2219275042a228abea0657bb7633cdEmmanuel Rodriguez            return Response.error(new ParseError(response));
212d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        } else {
213d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            return Response.success(bitmap, HttpHeaderParser.parseCacheHeaders(response));
214d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        }
215d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
216d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
217d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    @Override
218d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    protected void deliverResponse(Bitmap response) {
219d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        mListener.onResponse(response);
220d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
221d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
222d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    /**
223d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * Returns the largest power-of-two divisor for use in downscaling a bitmap
224d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * that will not result in the scaling past the desired dimensions.
225d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     *
226d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * @param actualWidth Actual width of the bitmap
227d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * @param actualHeight Actual height of the bitmap
228d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * @param desiredWidth Desired width of the bitmap
229d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * @param desiredHeight Desired height of the bitmap
230d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     */
231d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    // Visible for testing.
232d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    static int findBestSampleSize(
233d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            int actualWidth, int actualHeight, int desiredWidth, int desiredHeight) {
234d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        double wr = (double) actualWidth / desiredWidth;
235d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        double hr = (double) actualHeight / desiredHeight;
236d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        double ratio = Math.min(wr, hr);
237d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        float n = 1.0f;
238d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        while ((n * 2) <= ratio) {
239d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            n *= 2;
240d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        }
241d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
242d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        return (int) n;
243d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
244d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru}
245