diff options
Diffstat (limited to 'src/main/java/com')
8 files changed, 105 insertions, 21 deletions
diff --git a/src/main/java/com/android/volley/Cache.java b/src/main/java/com/android/volley/Cache.java index eafd118..f1ec757 100644 --- a/src/main/java/com/android/volley/Cache.java +++ b/src/main/java/com/android/volley/Cache.java @@ -74,6 +74,9 @@ public interface Cache { /** Date of this response as reported by the server. */ public long serverDate; + /** The last modified date for the requested object. */ + public long lastModified; + /** TTL for this record. */ public long ttl; diff --git a/src/main/java/com/android/volley/toolbox/AndroidAuthenticator.java b/src/main/java/com/android/volley/toolbox/AndroidAuthenticator.java index 371fd83..bdf7091 100644 --- a/src/main/java/com/android/volley/toolbox/AndroidAuthenticator.java +++ b/src/main/java/com/android/volley/toolbox/AndroidAuthenticator.java @@ -30,7 +30,7 @@ import android.os.Bundle; * tokens of a specified type for a specified account. */ public class AndroidAuthenticator implements Authenticator { - private final Context mContext; + private final AccountManager mAccountManager; private final Account mAccount; private final String mAuthTokenType; private final boolean mNotifyAuthFailure; @@ -54,7 +54,13 @@ public class AndroidAuthenticator implements Authenticator { */ public AndroidAuthenticator(Context context, Account account, String authTokenType, boolean notifyAuthFailure) { - mContext = context; + this(AccountManager.get(context), account, authTokenType, notifyAuthFailure); + } + + // Visible for testing. Allows injection of a mock AccountManager. + AndroidAuthenticator(AccountManager accountManager, Account account, + String authTokenType, boolean notifyAuthFailure) { + mAccountManager = accountManager; mAccount = account; mAuthTokenType = authTokenType; mNotifyAuthFailure = notifyAuthFailure; @@ -71,8 +77,7 @@ public class AndroidAuthenticator implements Authenticator { @SuppressWarnings("deprecation") @Override public String getAuthToken() throws AuthFailureError { - final AccountManager accountManager = AccountManager.get(mContext); - AccountManagerFuture<Bundle> future = accountManager.getAuthToken(mAccount, + AccountManagerFuture<Bundle> future = mAccountManager.getAuthToken(mAccount, mAuthTokenType, mNotifyAuthFailure, null, null); Bundle result; try { @@ -97,6 +102,6 @@ public class AndroidAuthenticator implements Authenticator { @Override public void invalidateAuthToken(String authToken) { - AccountManager.get(mContext).invalidateAuthToken(mAccount.type, authToken); + mAccountManager.invalidateAuthToken(mAccount.type, authToken); } } diff --git a/src/main/java/com/android/volley/toolbox/BasicNetwork.java b/src/main/java/com/android/volley/toolbox/BasicNetwork.java index bc1cfdb..4b1603b 100644 --- a/src/main/java/com/android/volley/toolbox/BasicNetwork.java +++ b/src/main/java/com/android/volley/toolbox/BasicNetwork.java @@ -212,8 +212,8 @@ public class BasicNetwork implements Network { headers.put("If-None-Match", entry.etag); } - if (entry.serverDate > 0) { - Date refTime = new Date(entry.serverDate); + if (entry.lastModified > 0) { + Date refTime = new Date(entry.lastModified); headers.put("If-Modified-Since", DateUtils.formatDate(refTime)); } } diff --git a/src/main/java/com/android/volley/toolbox/DiskBasedCache.java b/src/main/java/com/android/volley/toolbox/DiskBasedCache.java index b283788..036b55a 100644 --- a/src/main/java/com/android/volley/toolbox/DiskBasedCache.java +++ b/src/main/java/com/android/volley/toolbox/DiskBasedCache.java @@ -349,6 +349,9 @@ public class DiskBasedCache implements Cache { /** Date of this response as reported by the server. */ public long serverDate; + /** The last modified date for the requested object. */ + public long lastModified; + /** TTL for this record. */ public long ttl; @@ -370,6 +373,7 @@ public class DiskBasedCache implements Cache { this.size = entry.data.length; this.etag = entry.etag; this.serverDate = entry.serverDate; + this.lastModified = entry.lastModified; this.ttl = entry.ttl; this.softTtl = entry.softTtl; this.responseHeaders = entry.responseHeaders; @@ -396,6 +400,13 @@ public class DiskBasedCache implements Cache { entry.ttl = readLong(is); entry.softTtl = readLong(is); entry.responseHeaders = readStringStringMap(is); + + try { + entry.lastModified = readLong(is); + } catch (EOFException e) { + // the old cache entry format doesn't know lastModified + } + return entry; } @@ -407,6 +418,7 @@ public class DiskBasedCache implements Cache { e.data = data; e.etag = etag; e.serverDate = serverDate; + e.lastModified = lastModified; e.ttl = ttl; e.softTtl = softTtl; e.responseHeaders = responseHeaders; @@ -426,6 +438,7 @@ public class DiskBasedCache implements Cache { writeLong(os, ttl); writeLong(os, softTtl); writeStringStringMap(responseHeaders, os); + writeLong(os, lastModified); os.flush(); return true; } catch (IOException e) { diff --git a/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java b/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java index 601ac0f..7306052 100644 --- a/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java +++ b/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java @@ -42,9 +42,12 @@ public class HttpHeaderParser { Map<String, String> headers = response.headers; long serverDate = 0; + long lastModified = 0; long serverExpires = 0; long softExpire = 0; + long finalExpire = 0; long maxAge = 0; + long staleWhileRevalidate = 0; boolean hasCacheControl = false; String serverEtag = null; @@ -68,6 +71,11 @@ public class HttpHeaderParser { maxAge = Long.parseLong(token.substring(8)); } catch (Exception e) { } + } else if (token.startsWith("stale-while-revalidate=")) { + try { + staleWhileRevalidate = Long.parseLong(token.substring(23)); + } catch (Exception e) { + } } else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) { maxAge = 0; } @@ -79,23 +87,31 @@ public class HttpHeaderParser { serverExpires = parseDateAsEpoch(headerValue); } + headerValue = headers.get("Last-Modified"); + if (headerValue != null) { + lastModified = parseDateAsEpoch(headerValue); + } + serverEtag = headers.get("ETag"); // Cache-Control takes precedence over an Expires header, even if both exist and Expires // is more restrictive. if (hasCacheControl) { softExpire = now + maxAge * 1000; + finalExpire = softExpire + staleWhileRevalidate * 1000; } else if (serverDate > 0 && serverExpires >= serverDate) { // Default semantic for Expire header in HTTP specification is softExpire. softExpire = now + (serverExpires - serverDate); + finalExpire = softExpire; } Cache.Entry entry = new Cache.Entry(); entry.data = response.data; entry.etag = serverEtag; entry.softTtl = softExpire; - entry.ttl = entry.softTtl; + entry.ttl = finalExpire; entry.serverDate = serverDate; + entry.lastModified = lastModified; entry.responseHeaders = headers; return entry; diff --git a/src/main/java/com/android/volley/toolbox/ImageLoader.java b/src/main/java/com/android/volley/toolbox/ImageLoader.java index 5348dc6..151e022 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.graphics.Bitmap.Config; 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; @@ -171,6 +172,15 @@ public class ImageLoader { } /** + * Equivalent to calling {@link #get(String, ImageListener, int, int, ScaleType)} with + * {@code Scaletype == ScaleType.CENTER_INSIDE}. + */ + public ImageContainer get(String requestUrl, ImageListener imageListener, + int maxWidth, int maxHeight) { + return get(requestUrl, imageListener, maxWidth, maxHeight, ScaleType.CENTER_INSIDE); + } + + /** * Issues a bitmap request with the given URL if that image is not available * in the cache, and returns a bitmap container that contains all of the data * relating to the request (as well as the default image if the requested @@ -179,11 +189,13 @@ public class ImageLoader { * @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 +227,8 @@ public class ImageLoader { // 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 +236,14 @@ public class ImageLoader { 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..27c1fe2 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 com.android.volley.VolleyLog; 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 @@ public class ImageRequest extends Request<Bitmap> { 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,20 +65,32 @@ public class ImageRequest extends Request<Bitmap> { * @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; } + /** + * For API compatibility with the pre-ScaleType variant of the constructor. Equivalent to + * the normal constructor with {@code ScaleType.CENTER_INSIDE}. + */ + @Deprecated + public ImageRequest(String url, Response.Listener<Bitmap> listener, int maxWidth, int maxHeight, + Config decodeConfig, Response.ErrorListener errorListener) { + this(url, listener, maxWidth, maxHeight, + ScaleType.CENTER_INSIDE, decodeConfig, errorListener); + } @Override public Priority getPriority() { return Priority.LOW; @@ -92,14 +106,24 @@ public class ImageRequest extends Request<Bitmap> { * 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 +136,16 @@ public class ImageRequest extends Request<Bitmap> { 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 +183,9 @@ public class ImageRequest extends Request<Bitmap> { // 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 @@ public class NetworkImageView extends ImageView { 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 @@ public class NetworkImageView extends ImageView { setImageResource(mDefaultImageId); } } - }, maxWidth, maxHeight); + }, maxWidth, maxHeight, scaleType); // update the ImageContainer to be the new bitmap container. mImageContainer = newContainer; |