1d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod/** 2d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Copyright (C) 2013 The Android Open Source Project 3d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * 4d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Licensed under the Apache License, Version 2.0 (the "License"); 5d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * you may not use this file except in compliance with the License. 6d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * You may obtain a copy of the License at 7d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * 8d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * http://www.apache.org/licenses/LICENSE-2.0 9d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * 10d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Unless required by applicable law or agreed to in writing, software 11d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * distributed under the License is distributed on an "AS IS" BASIS, 12d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * See the License for the specific language governing permissions and 14d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * limitations under the License. 15d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 16d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbodpackage com.android.volley.toolbox; 17d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 18d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbodimport android.graphics.Bitmap; 19d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbodimport android.graphics.Bitmap.Config; 20d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbodimport android.os.Handler; 21d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbodimport android.os.Looper; 22d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbodimport android.widget.ImageView; 23ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmannimport android.widget.ImageView.ScaleType; 24d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbodimport com.android.volley.Request; 25d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbodimport com.android.volley.RequestQueue; 26d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbodimport com.android.volley.Response.ErrorListener; 27d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbodimport com.android.volley.Response.Listener; 28d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbodimport com.android.volley.VolleyError; 29d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 30d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbodimport java.util.HashMap; 31d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbodimport java.util.LinkedList; 32d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 33d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod/** 34d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Helper that handles loading and caching images from remote URLs. 35d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * 36d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * The simple way to use this class is to call {@link ImageLoader#get(String, ImageListener)} 37d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * and to pass in the default image listener provided by 38d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * {@link ImageLoader#getImageListener(ImageView, int, int)}. Note that all function calls to 39d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * this class must be made from the main thead, and all responses will be delivered to the main 40d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * thread as well. 41d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 42d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbodpublic class ImageLoader { 43d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** RequestQueue for dispatching ImageRequests onto. */ 44d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod private final RequestQueue mRequestQueue; 45d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 46d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** Amount of time to wait after first response arrives before delivering all responses. */ 47d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod private int mBatchResponseDelayMs = 100; 48d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 49d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** The cache implementation to be used as an L1 cache before calling into volley. */ 50d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod private final ImageCache mCache; 51d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 52d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 53d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * HashMap of Cache keys -> BatchedImageRequest used to track in-flight requests so 54d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * that we can coalesce multiple requests to the same URL into a single network request. 55d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 56d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod private final HashMap<String, BatchedImageRequest> mInFlightRequests = 57d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod new HashMap<String, BatchedImageRequest>(); 58d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 59d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** HashMap of the currently pending responses (waiting to be delivered). */ 60d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod private final HashMap<String, BatchedImageRequest> mBatchedResponses = 61d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod new HashMap<String, BatchedImageRequest>(); 62d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 63d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** Handler to the main thread. */ 64d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod private final Handler mHandler = new Handler(Looper.getMainLooper()); 65d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 66d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** Runnable for in-flight response delivery. */ 67d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod private Runnable mRunnable; 68d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 69d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 70d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Simple cache adapter interface. If provided to the ImageLoader, it 71d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * will be used as an L1 cache before dispatch to Volley. Implementations 72d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * must not block. Implementation with an LruCache is recommended. 73d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 74d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public interface ImageCache { 75d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public Bitmap getBitmap(String url); 76d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public void putBitmap(String url, Bitmap bitmap); 77d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 78d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 79d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 80d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Constructs a new ImageLoader. 81d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param queue The RequestQueue to use for making image requests. 82d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param imageCache The cache to use as an L1 cache. 83d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 84d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public ImageLoader(RequestQueue queue, ImageCache imageCache) { 85d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mRequestQueue = queue; 86d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mCache = imageCache; 87d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 88d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 89d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 90d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * The default implementation of ImageListener which handles basic functionality 91d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * of showing a default image until the network response is received, at which point 92d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * it will switch to either the actual image or the error image. 93f58de9c02dfc64c1ec4743ba040debc5cf87b560Ralph Bergmann * @param view The imageView that the listener is associated with. 94d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param defaultImageResId Default image resource ID to use, or 0 if it doesn't exist. 95d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param errorImageResId Error image resource ID to use, or 0 if it doesn't exist. 96d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 97d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public static ImageListener getImageListener(final ImageView view, 98d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod final int defaultImageResId, final int errorImageResId) { 99d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod return new ImageListener() { 100d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod @Override 101d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public void onErrorResponse(VolleyError error) { 102d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod if (errorImageResId != 0) { 103d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod view.setImageResource(errorImageResId); 104d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 105d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 106d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 107d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod @Override 108d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public void onResponse(ImageContainer response, boolean isImmediate) { 109d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod if (response.getBitmap() != null) { 110d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod view.setImageBitmap(response.getBitmap()); 111d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } else if (defaultImageResId != 0) { 112d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod view.setImageResource(defaultImageResId); 113d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 114d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 115d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod }; 116d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 117d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 118d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 119d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Interface for the response handlers on image requests. 120d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * 121d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * The call flow is this: 122d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * 1. Upon being attached to a request, onResponse(response, true) will 123d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * be invoked to reflect any cached data that was already available. If the 124d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * data was available, response.getBitmap() will be non-null. 125d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * 126d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * 2. After a network response returns, only one of the following cases will happen: 127d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * - onResponse(response, false) will be called if the image was loaded. 128d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * or 129d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * - onErrorResponse will be called if there was an error loading the image. 130d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 131d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public interface ImageListener extends ErrorListener { 132d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 133d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Listens for non-error changes to the loading of the image request. 134d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * 135d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param response Holds all information pertaining to the request, as well 136d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * as the bitmap (if it is loaded). 137d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param isImmediate True if this was called during ImageLoader.get() variants. 138d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * This can be used to differentiate between a cached image loading and a network 139d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * image loading in order to, for example, run an animation to fade in network loaded 140d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * images. 141d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 142d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public void onResponse(ImageContainer response, boolean isImmediate); 143d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 144d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 145d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 146285d4727a6c223e8f4c19b6b44078464605b7f8bandaagar * Checks if the item is available in the cache. 147285d4727a6c223e8f4c19b6b44078464605b7f8bandaagar * @param requestUrl The url of the remote image 148285d4727a6c223e8f4c19b6b44078464605b7f8bandaagar * @param maxWidth The maximum width of the returned image. 149285d4727a6c223e8f4c19b6b44078464605b7f8bandaagar * @param maxHeight The maximum height of the returned image. 150285d4727a6c223e8f4c19b6b44078464605b7f8bandaagar * @return True if the item exists in cache, false otherwise. 151285d4727a6c223e8f4c19b6b44078464605b7f8bandaagar */ 152285d4727a6c223e8f4c19b6b44078464605b7f8bandaagar public boolean isCached(String requestUrl, int maxWidth, int maxHeight) { 153d95219cb1c414de049f90c97fccad638b11bdc85Ralph Bergmann return isCached(requestUrl, maxWidth, maxHeight, ScaleType.CENTER_INSIDE); 154d95219cb1c414de049f90c97fccad638b11bdc85Ralph Bergmann } 155d95219cb1c414de049f90c97fccad638b11bdc85Ralph Bergmann 156d95219cb1c414de049f90c97fccad638b11bdc85Ralph Bergmann /** 157d95219cb1c414de049f90c97fccad638b11bdc85Ralph Bergmann * Checks if the item is available in the cache. 158d95219cb1c414de049f90c97fccad638b11bdc85Ralph Bergmann * 159d95219cb1c414de049f90c97fccad638b11bdc85Ralph Bergmann * @param requestUrl The url of the remote image 160d95219cb1c414de049f90c97fccad638b11bdc85Ralph Bergmann * @param maxWidth The maximum width of the returned image. 161d95219cb1c414de049f90c97fccad638b11bdc85Ralph Bergmann * @param maxHeight The maximum height of the returned image. 162d95219cb1c414de049f90c97fccad638b11bdc85Ralph Bergmann * @param scaleType The scaleType of the imageView. 163d95219cb1c414de049f90c97fccad638b11bdc85Ralph Bergmann * @return True if the item exists in cache, false otherwise. 164d95219cb1c414de049f90c97fccad638b11bdc85Ralph Bergmann */ 165d95219cb1c414de049f90c97fccad638b11bdc85Ralph Bergmann public boolean isCached(String requestUrl, int maxWidth, int maxHeight, ScaleType scaleType) { 166285d4727a6c223e8f4c19b6b44078464605b7f8bandaagar throwIfNotOnMainThread(); 167285d4727a6c223e8f4c19b6b44078464605b7f8bandaagar 168d95219cb1c414de049f90c97fccad638b11bdc85Ralph Bergmann String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType); 169285d4727a6c223e8f4c19b6b44078464605b7f8bandaagar return mCache.getBitmap(cacheKey) != null; 170285d4727a6c223e8f4c19b6b44078464605b7f8bandaagar } 171285d4727a6c223e8f4c19b6b44078464605b7f8bandaagar 172285d4727a6c223e8f4c19b6b44078464605b7f8bandaagar /** 173d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Returns an ImageContainer for the requested URL. 174d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * 175d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * The ImageContainer will contain either the specified default bitmap or the loaded bitmap. 176d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * If the default was returned, the {@link ImageLoader} will be invoked when the 177d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * request is fulfilled. 178d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * 179d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param requestUrl The URL of the image to be loaded. 180d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 181d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public ImageContainer get(String requestUrl, final ImageListener listener) { 182a67f7d1922920d115101a266e8902325800c5fb9Narayan Kamath return get(requestUrl, listener, 0, 0); 183a67f7d1922920d115101a266e8902325800c5fb9Narayan Kamath } 184a67f7d1922920d115101a266e8902325800c5fb9Narayan Kamath 185a67f7d1922920d115101a266e8902325800c5fb9Narayan Kamath /** 186a67f7d1922920d115101a266e8902325800c5fb9Narayan Kamath * Equivalent to calling {@link #get(String, ImageListener, int, int, ScaleType)} with 187a67f7d1922920d115101a266e8902325800c5fb9Narayan Kamath * {@code Scaletype == ScaleType.CENTER_INSIDE}. 188a67f7d1922920d115101a266e8902325800c5fb9Narayan Kamath */ 189a67f7d1922920d115101a266e8902325800c5fb9Narayan Kamath public ImageContainer get(String requestUrl, ImageListener imageListener, 190a67f7d1922920d115101a266e8902325800c5fb9Narayan Kamath int maxWidth, int maxHeight) { 191a67f7d1922920d115101a266e8902325800c5fb9Narayan Kamath return get(requestUrl, imageListener, maxWidth, maxHeight, ScaleType.CENTER_INSIDE); 192d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 193d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 194d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 195d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Issues a bitmap request with the given URL if that image is not available 196d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * in the cache, and returns a bitmap container that contains all of the data 197d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * relating to the request (as well as the default image if the requested 198d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * image is not available). 199d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param requestUrl The url of the remote image 200d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param imageListener The listener to call when the remote image is loaded 201d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param maxWidth The maximum width of the returned image. 202d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param maxHeight The maximum height of the returned image. 203ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann * @param scaleType The ImageViews ScaleType used to calculate the needed image size. 204d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @return A container object that contains all of the properties of the request, as well as 205d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * the currently available image (default if remote is not loaded). 206d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 207d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public ImageContainer get(String requestUrl, ImageListener imageListener, 208ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann int maxWidth, int maxHeight, ScaleType scaleType) { 209d95219cb1c414de049f90c97fccad638b11bdc85Ralph Bergmann 210d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // only fulfill requests that were initiated from the main thread. 211d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod throwIfNotOnMainThread(); 212d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 213d95219cb1c414de049f90c97fccad638b11bdc85Ralph Bergmann final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType); 214d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 215d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // Try to look up the request in the cache of remote images. 216d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod Bitmap cachedBitmap = mCache.getBitmap(cacheKey); 217d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod if (cachedBitmap != null) { 218d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // Return the cached bitmap. 219d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod ImageContainer container = new ImageContainer(cachedBitmap, requestUrl, null, null); 220d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod imageListener.onResponse(container, true); 221d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod return container; 222d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 223d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 224d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // The bitmap did not exist in the cache, fetch it! 225d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod ImageContainer imageContainer = 226d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod new ImageContainer(null, requestUrl, cacheKey, imageListener); 227d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 228d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // Update the caller to let them know that they should use the default bitmap. 229d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod imageListener.onResponse(imageContainer, true); 230d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 231d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // Check to see if a request is already in-flight. 232d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod BatchedImageRequest request = mInFlightRequests.get(cacheKey); 233d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod if (request != null) { 234d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // If it is, add this request to the list of listeners. 235d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod request.addContainer(imageContainer); 236d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod return imageContainer; 237d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 238d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 239d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // The request is not already in flight. Send the new request to the network and 240d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // track it. 241ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann Request<Bitmap> newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight, scaleType, 242ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann cacheKey); 243d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 244d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mRequestQueue.add(newRequest); 245d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mInFlightRequests.put(cacheKey, 246d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod new BatchedImageRequest(newRequest, imageContainer)); 247d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod return imageContainer; 248d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 249d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 250ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann protected Request<Bitmap> makeImageRequest(String requestUrl, int maxWidth, int maxHeight, 251ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann ScaleType scaleType, final String cacheKey) { 2526c8ba96246e9c1f0056341d9f1d16208489ba555Kevin Schultz return new ImageRequest(requestUrl, new Listener<Bitmap>() { 2536c8ba96246e9c1f0056341d9f1d16208489ba555Kevin Schultz @Override 2546c8ba96246e9c1f0056341d9f1d16208489ba555Kevin Schultz public void onResponse(Bitmap response) { 2556c8ba96246e9c1f0056341d9f1d16208489ba555Kevin Schultz onGetImageSuccess(cacheKey, response); 2566c8ba96246e9c1f0056341d9f1d16208489ba555Kevin Schultz } 257ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann }, maxWidth, maxHeight, scaleType, Config.RGB_565, new ErrorListener() { 2586c8ba96246e9c1f0056341d9f1d16208489ba555Kevin Schultz @Override 2596c8ba96246e9c1f0056341d9f1d16208489ba555Kevin Schultz public void onErrorResponse(VolleyError error) { 2606c8ba96246e9c1f0056341d9f1d16208489ba555Kevin Schultz onGetImageError(cacheKey, error); 2616c8ba96246e9c1f0056341d9f1d16208489ba555Kevin Schultz } 2626c8ba96246e9c1f0056341d9f1d16208489ba555Kevin Schultz }); 2636c8ba96246e9c1f0056341d9f1d16208489ba555Kevin Schultz } 2646c8ba96246e9c1f0056341d9f1d16208489ba555Kevin Schultz 265d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 266d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Sets the amount of time to wait after the first response arrives before delivering all 267d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * responses. Batching can be disabled entirely by passing in 0. 268d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param newBatchedResponseDelayMs The time in milliseconds to wait. 269d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 270d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public void setBatchedResponseDelay(int newBatchedResponseDelayMs) { 271d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mBatchResponseDelayMs = newBatchedResponseDelayMs; 272d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 273d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 274d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 275d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Handler for when an image was successfully loaded. 276d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param cacheKey The cache key that is associated with the image request. 277d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param response The bitmap that was returned from the network. 278d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 2796c8ba96246e9c1f0056341d9f1d16208489ba555Kevin Schultz protected void onGetImageSuccess(String cacheKey, Bitmap response) { 280d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // cache the image that was fetched. 281d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mCache.putBitmap(cacheKey, response); 282d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 283d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // remove the request from the list of in-flight requests. 284d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod BatchedImageRequest request = mInFlightRequests.remove(cacheKey); 285d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 286d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod if (request != null) { 287d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // Update the response bitmap. 288d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod request.mResponseBitmap = response; 289d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 290d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // Send the batched response 291dc5355251132a447e384e273127b1a8bdaf32f5aCameron Ketcham batchResponse(cacheKey, request); 292d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 293d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 294d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 295d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 296d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Handler for when an image failed to load. 297d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param cacheKey The cache key that is associated with the image request. 298d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 2996c8ba96246e9c1f0056341d9f1d16208489ba555Kevin Schultz protected void onGetImageError(String cacheKey, VolleyError error) { 300d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // Notify the requesters that something failed via a null result. 301d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // Remove this request from the list of in-flight requests. 302d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod BatchedImageRequest request = mInFlightRequests.remove(cacheKey); 303d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 304d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod if (request != null) { 305ee9d4500c337d5d2371651764dfcbbf8d5502757Andrew Sutherland // Set the error for this request 306ee9d4500c337d5d2371651764dfcbbf8d5502757Andrew Sutherland request.setError(error); 307ee9d4500c337d5d2371651764dfcbbf8d5502757Andrew Sutherland 308d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // Send the batched response 309dc5355251132a447e384e273127b1a8bdaf32f5aCameron Ketcham batchResponse(cacheKey, request); 310d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 311d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 312d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 313d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 314d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Container object for all of the data surrounding an image request. 315d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 316d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public class ImageContainer { 317d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 318d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * The most relevant bitmap for the container. If the image was in cache, the 319d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Holder to use for the final bitmap (the one that pairs to the requested URL). 320d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 321d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod private Bitmap mBitmap; 322d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 323d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod private final ImageListener mListener; 324d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 325d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** The cache key that was associated with the request */ 326d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod private final String mCacheKey; 327d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 328d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** The request URL that was specified */ 329d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod private final String mRequestUrl; 330d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 331d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 332d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Constructs a BitmapContainer object. 333d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param bitmap The final bitmap (if it exists). 334d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param requestUrl The requested URL for this container. 335d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param cacheKey The cache key that identifies the requested URL for this container. 336d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 337d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public ImageContainer(Bitmap bitmap, String requestUrl, 338d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod String cacheKey, ImageListener listener) { 339d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mBitmap = bitmap; 340d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mRequestUrl = requestUrl; 341d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mCacheKey = cacheKey; 342d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mListener = listener; 343d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 344d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 345d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 346d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Releases interest in the in-flight request (and cancels it if no one else is listening). 347d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 348d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public void cancelRequest() { 349d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod if (mListener == null) { 350d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod return; 351d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 352d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 353d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod BatchedImageRequest request = mInFlightRequests.get(mCacheKey); 354d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod if (request != null) { 355d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod boolean canceled = request.removeContainerAndCancelIfNecessary(this); 356d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod if (canceled) { 357d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mInFlightRequests.remove(mCacheKey); 358d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 359d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } else { 360d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // check to see if it is already batched for delivery. 361d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod request = mBatchedResponses.get(mCacheKey); 362d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod if (request != null) { 363d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod request.removeContainerAndCancelIfNecessary(this); 364d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod if (request.mContainers.size() == 0) { 365d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mBatchedResponses.remove(mCacheKey); 366d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 367d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 368d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 369d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 370d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 371d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 372d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Returns the bitmap associated with the request URL if it has been loaded, null otherwise. 373d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 374d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public Bitmap getBitmap() { 375d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod return mBitmap; 376d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 377d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 378d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 379d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Returns the requested URL for this container. 380d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 381d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public String getRequestUrl() { 382d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod return mRequestUrl; 383d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 384d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 385d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 386d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 387d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Wrapper class used to map a Request to the set of active ImageContainer objects that are 388d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * interested in its results. 389d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 390d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod private class BatchedImageRequest { 391d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** The request being tracked */ 392d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod private final Request<?> mRequest; 393d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 394d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** The result of the request being tracked by this item */ 395d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod private Bitmap mResponseBitmap; 396d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 397dc5355251132a447e384e273127b1a8bdaf32f5aCameron Ketcham /** Error if one occurred for this response */ 398dc5355251132a447e384e273127b1a8bdaf32f5aCameron Ketcham private VolleyError mError; 399dc5355251132a447e384e273127b1a8bdaf32f5aCameron Ketcham 400d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** List of all of the active ImageContainers that are interested in the request */ 401d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod private final LinkedList<ImageContainer> mContainers = new LinkedList<ImageContainer>(); 402d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 403d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 404d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Constructs a new BatchedImageRequest object 405d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param request The request being tracked 406d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param container The ImageContainer of the person who initiated the request. 407d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 408d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public BatchedImageRequest(Request<?> request, ImageContainer container) { 409d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mRequest = request; 410d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mContainers.add(container); 411d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 412d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 413d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 414dc5355251132a447e384e273127b1a8bdaf32f5aCameron Ketcham * Set the error for this response 415dc5355251132a447e384e273127b1a8bdaf32f5aCameron Ketcham */ 416dc5355251132a447e384e273127b1a8bdaf32f5aCameron Ketcham public void setError(VolleyError error) { 417dc5355251132a447e384e273127b1a8bdaf32f5aCameron Ketcham mError = error; 418dc5355251132a447e384e273127b1a8bdaf32f5aCameron Ketcham } 419dc5355251132a447e384e273127b1a8bdaf32f5aCameron Ketcham 420dc5355251132a447e384e273127b1a8bdaf32f5aCameron Ketcham /** 421dc5355251132a447e384e273127b1a8bdaf32f5aCameron Ketcham * Get the error for this response 422dc5355251132a447e384e273127b1a8bdaf32f5aCameron Ketcham */ 423dc5355251132a447e384e273127b1a8bdaf32f5aCameron Ketcham public VolleyError getError() { 424dc5355251132a447e384e273127b1a8bdaf32f5aCameron Ketcham return mError; 425dc5355251132a447e384e273127b1a8bdaf32f5aCameron Ketcham } 426dc5355251132a447e384e273127b1a8bdaf32f5aCameron Ketcham 427dc5355251132a447e384e273127b1a8bdaf32f5aCameron Ketcham /** 428d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Adds another ImageContainer to the list of those interested in the results of 429d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * the request. 430d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 431d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public void addContainer(ImageContainer container) { 432d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mContainers.add(container); 433d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 434d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 435d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 436d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Detatches the bitmap container from the request and cancels the request if no one is 437d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * left listening. 438d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param container The container to remove from the list 439d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @return True if the request was canceled, false otherwise. 440d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 441d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public boolean removeContainerAndCancelIfNecessary(ImageContainer container) { 442d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mContainers.remove(container); 443d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod if (mContainers.size() == 0) { 444d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mRequest.cancel(); 445d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod return true; 446d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 447d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod return false; 448d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 449d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 450d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 451d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 452d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Starts the runnable for batched delivery of responses if it is not already started. 453d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param cacheKey The cacheKey of the response being delivered. 454d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param request The BatchedImageRequest to be delivered. 455d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 456dc5355251132a447e384e273127b1a8bdaf32f5aCameron Ketcham private void batchResponse(String cacheKey, BatchedImageRequest request) { 457d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mBatchedResponses.put(cacheKey, request); 458d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // If we don't already have a batch delivery runnable in flight, make a new one. 459d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // Note that this will be used to deliver responses to all callers in mBatchedResponses. 460d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod if (mRunnable == null) { 461d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mRunnable = new Runnable() { 462d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod @Override 463d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public void run() { 464d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod for (BatchedImageRequest bir : mBatchedResponses.values()) { 465d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod for (ImageContainer container : bir.mContainers) { 466d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // If one of the callers in the batched request canceled the request 467d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // after the response was received but before it was delivered, 468d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // skip them. 469d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod if (container.mListener == null) { 470d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod continue; 471d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 472dc5355251132a447e384e273127b1a8bdaf32f5aCameron Ketcham if (bir.getError() == null) { 473d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod container.mBitmap = bir.mResponseBitmap; 474d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod container.mListener.onResponse(container, false); 475d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } else { 476dc5355251132a447e384e273127b1a8bdaf32f5aCameron Ketcham container.mListener.onErrorResponse(bir.getError()); 477d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 478d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 479d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 480d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mBatchedResponses.clear(); 481d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mRunnable = null; 482d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 483d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 484d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod }; 485d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // Post the runnable. 486d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mHandler.postDelayed(mRunnable, mBatchResponseDelayMs); 487d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 488d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 489d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 490d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod private void throwIfNotOnMainThread() { 491d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod if (Looper.myLooper() != Looper.getMainLooper()) { 492d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod throw new IllegalStateException("ImageLoader must be invoked from the main thread."); 493d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 494d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 495d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 496d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Creates a cache key for use with the L1 cache. 497d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param url The URL of the request. 498d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param maxWidth The max-width of the output. 499d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param maxHeight The max-height of the output. 500d95219cb1c414de049f90c97fccad638b11bdc85Ralph Bergmann * @param scaleType The scaleType of the imageView. 501d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 502d95219cb1c414de049f90c97fccad638b11bdc85Ralph Bergmann private static String getCacheKey(String url, int maxWidth, int maxHeight, ScaleType scaleType) { 503d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod return new StringBuilder(url.length() + 12).append("#W").append(maxWidth) 504d95219cb1c414de049f90c97fccad638b11bdc85Ralph Bergmann .append("#H").append(maxHeight).append("#S").append(scaleType.ordinal()).append(url) 505d95219cb1c414de049f90c97fccad638b11bdc85Ralph Bergmann .toString(); 506d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 507d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod} 508