Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

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