From 197edd1d6145a022e08896697b8d5e7efa9f32bf Mon Sep 17 00:00:00 2001 From: Florens Douwes Date: Mon, 7 Apr 2014 19:58:16 +0200 Subject: [PATCH] Updated Volley --- .../com/android/volley/AuthFailureError.java | 3 + .../com/android/volley/CacheDispatcher.java | 13 +- .../com/android/volley/ExecutorDelivery.java | 4 +- .../com/android/volley/NetworkDispatcher.java | 25 +- Chan/src/com/android/volley/NetworkError.java | 2 + .../com/android/volley/NetworkResponse.java | 4 +- Chan/src/com/android/volley/ParseError.java | 2 + Chan/src/com/android/volley/Request.java | 67 ++++- Chan/src/com/android/volley/RequestQueue.java | 33 +-- Chan/src/com/android/volley/ServerError.java | 2 + Chan/src/com/android/volley/VolleyLog.java | 6 +- .../volley/toolbox/AndroidAuthenticator.java | 7 +- .../android/volley/toolbox/BasicNetwork.java | 35 +-- .../volley/toolbox/ClearCacheRequest.java | 6 +- .../volley/toolbox/DiskBasedCache.java | 14 +- .../volley/toolbox/HttpClientStack.java | 63 +++- .../volley/toolbox/HttpHeaderParser.java | 6 +- .../com/android/volley/toolbox/HttpStack.java | 8 +- .../com/android/volley/toolbox/HurlStack.java | 41 ++- .../android/volley/toolbox/ImageLoader.java | 13 +- .../android/volley/toolbox/ImageRequest.java | 8 +- .../volley/toolbox/JsonArrayRequest.java | 10 +- .../volley/toolbox/JsonObjectRequest.java | 10 +- .../android/volley/toolbox/JsonRequest.java | 4 +- .../volley/toolbox/NetworkImageView.java | 68 ++--- .../android/volley/toolbox/RequestFuture.java | 8 +- .../android/volley/toolbox/StringRequest.java | 4 +- .../com/android/volley/toolbox/Volley.java | 4 +- Chan/src/org/floens/chan/ChanApplication.java | 2 +- .../chan/core/net}/BitmapLruImageCache.java | 6 +- .../floens/chan/core/net/BoardsRequest.java | 1 - .../chan/core/net/ChanReaderRequest.java | 1 - .../chan/core/net}/JsonReaderRequest.java | 26 +- .../chan/ui/view/CustomNetworkImageView.java | 277 ++++++++++++++++++ .../floens/chan/ui/view/NetworkPhotoView.java | 29 +- .../src/org/floens/chan/ui/view/PostView.java | 4 +- 36 files changed, 595 insertions(+), 221 deletions(-) rename Chan/src/{com/android/volley/extra => org/floens/chan/core/net}/BitmapLruImageCache.java (93%) rename Chan/src/{com/android/volley/extra => org/floens/chan/core/net}/JsonReaderRequest.java (93%) create mode 100644 Chan/src/org/floens/chan/ui/view/CustomNetworkImageView.java diff --git a/Chan/src/com/android/volley/AuthFailureError.java b/Chan/src/com/android/volley/AuthFailureError.java index 87c811df..7bb2e15f 100644 --- a/Chan/src/com/android/volley/AuthFailureError.java +++ b/Chan/src/com/android/volley/AuthFailureError.java @@ -18,6 +18,9 @@ package com.android.volley; import android.content.Intent; +import com.android.volley.NetworkResponse; +import com.android.volley.VolleyError; + /** * Error indicating that there was an authentication failure when performing a Request. */ diff --git a/Chan/src/com/android/volley/CacheDispatcher.java b/Chan/src/com/android/volley/CacheDispatcher.java index 4326b5d4..18d219b4 100644 --- a/Chan/src/com/android/volley/CacheDispatcher.java +++ b/Chan/src/com/android/volley/CacheDispatcher.java @@ -16,10 +16,10 @@ package com.android.volley; -import java.util.concurrent.BlockingQueue; - import android.os.Process; +import java.util.concurrent.BlockingQueue; + /** * Provides a thread for performing cache triage on a queue of requests. * @@ -29,16 +29,15 @@ import android.os.Process; * refresh are enqueued on the specified network queue for processing * by a {@link NetworkDispatcher}. */ -@SuppressWarnings("rawtypes") public class CacheDispatcher extends Thread { private static final boolean DEBUG = VolleyLog.DEBUG; /** The queue of requests coming in for triage. */ - private final BlockingQueue mCacheQueue; + private final BlockingQueue> mCacheQueue; /** The queue of requests going out to the network. */ - private final BlockingQueue mNetworkQueue; + private final BlockingQueue> mNetworkQueue; /** The cache to read from. */ private final Cache mCache; @@ -59,7 +58,7 @@ public class CacheDispatcher extends Thread { * @param delivery Delivery interface to use for posting responses */ public CacheDispatcher( - BlockingQueue cacheQueue, BlockingQueue networkQueue, + BlockingQueue> cacheQueue, BlockingQueue> networkQueue, Cache cache, ResponseDelivery delivery) { mCacheQueue = cacheQueue; mNetworkQueue = networkQueue; @@ -88,7 +87,7 @@ public class CacheDispatcher extends Thread { try { // Get a request from the cache triage queue, blocking until // at least one is available. - final Request request = mCacheQueue.take(); + final Request request = mCacheQueue.take(); request.addMarker("cache-queue-take"); // If the request has been canceled, don't bother dispatching it. diff --git a/Chan/src/com/android/volley/ExecutorDelivery.java b/Chan/src/com/android/volley/ExecutorDelivery.java index 5d4a5840..1babfcd1 100644 --- a/Chan/src/com/android/volley/ExecutorDelivery.java +++ b/Chan/src/com/android/volley/ExecutorDelivery.java @@ -16,10 +16,10 @@ package com.android.volley; -import java.util.concurrent.Executor; - import android.os.Handler; +import java.util.concurrent.Executor; + /** * Delivers responses and errors. */ diff --git a/Chan/src/com/android/volley/NetworkDispatcher.java b/Chan/src/com/android/volley/NetworkDispatcher.java index 9ae39ba3..a654ead5 100644 --- a/Chan/src/com/android/volley/NetworkDispatcher.java +++ b/Chan/src/com/android/volley/NetworkDispatcher.java @@ -16,12 +16,13 @@ package com.android.volley; -import java.util.concurrent.BlockingQueue; - +import android.annotation.TargetApi; import android.net.TrafficStats; import android.os.Build; import android.os.Process; +import java.util.concurrent.BlockingQueue; + /** * Provides a thread for performing network dispatch from a queue of requests. * @@ -30,10 +31,9 @@ import android.os.Process; * eligible, using a specified {@link Cache} interface. Valid responses and * errors are posted back to the caller via a {@link ResponseDelivery}. */ -@SuppressWarnings("rawtypes") public class NetworkDispatcher extends Thread { /** The queue of requests to service. */ - private final BlockingQueue mQueue; + private final BlockingQueue> mQueue; /** The network interface for processing requests. */ private final Network mNetwork; /** The cache to write to. */ @@ -52,7 +52,7 @@ public class NetworkDispatcher extends Thread { * @param cache Cache interface to use for writing responses to cache * @param delivery Delivery interface to use for posting responses */ - public NetworkDispatcher(BlockingQueue queue, + public NetworkDispatcher(BlockingQueue> queue, Network network, Cache cache, ResponseDelivery delivery) { mQueue = queue; @@ -70,10 +70,18 @@ public class NetworkDispatcher extends Thread { interrupt(); } + @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) + private void addTrafficStatsTag(Request request) { + // Tag the request (if API >= 14) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + TrafficStats.setThreadStatsTag(request.getTrafficStatsTag()); + } + } + @Override public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - Request request; + Request request; while (true) { try { // Take a request from the queue. @@ -96,10 +104,7 @@ public class NetworkDispatcher extends Thread { continue; } - // Tag the request (if API >= 14) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - TrafficStats.setThreadStatsTag(request.getTrafficStatsTag()); - } + addTrafficStatsTag(request); // Perform the network request. NetworkResponse networkResponse = mNetwork.performRequest(request); diff --git a/Chan/src/com/android/volley/NetworkError.java b/Chan/src/com/android/volley/NetworkError.java index 63c388e1..42fbcc26 100644 --- a/Chan/src/com/android/volley/NetworkError.java +++ b/Chan/src/com/android/volley/NetworkError.java @@ -16,6 +16,8 @@ package com.android.volley; +import com.android.volley.NetworkResponse; +import com.android.volley.VolleyError; /** * Indicates that there was a network error when performing a Volley request. diff --git a/Chan/src/com/android/volley/NetworkResponse.java b/Chan/src/com/android/volley/NetworkResponse.java index c200a262..6a0b5c2b 100644 --- a/Chan/src/com/android/volley/NetworkResponse.java +++ b/Chan/src/com/android/volley/NetworkResponse.java @@ -16,11 +16,11 @@ package com.android.volley; +import org.apache.http.HttpStatus; + import java.util.Collections; import java.util.Map; -import org.apache.http.HttpStatus; - /** * Data and headers returned from {@link Network#performRequest(Request)}. */ diff --git a/Chan/src/com/android/volley/ParseError.java b/Chan/src/com/android/volley/ParseError.java index 81f32e07..a55da470 100644 --- a/Chan/src/com/android/volley/ParseError.java +++ b/Chan/src/com/android/volley/ParseError.java @@ -16,6 +16,8 @@ package com.android.volley; +import com.android.volley.NetworkResponse; +import com.android.volley.VolleyError; /** * Indicates that the server's response could not be parsed. diff --git a/Chan/src/com/android/volley/Request.java b/Chan/src/com/android/volley/Request.java index c2911cab..53093e68 100644 --- a/Chan/src/com/android/volley/Request.java +++ b/Chan/src/com/android/volley/Request.java @@ -16,11 +16,6 @@ package com.android.volley; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.util.Collections; -import java.util.Map; - import android.net.TrafficStats; import android.net.Uri; import android.os.Handler; @@ -30,6 +25,11 @@ import android.text.TextUtils; import com.android.volley.VolleyLog.MarkerLog; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.Collections; +import java.util.Map; + /** * Base class for all network requests. * @@ -51,12 +51,19 @@ public abstract class Request implements Comparable> { int POST = 1; int PUT = 2; int DELETE = 3; + int HEAD = 4; + int OPTIONS = 5; + int TRACE = 6; + int PATCH = 7; } /** An event log tracing the lifetime of this request; for debugging. */ private final MarkerLog mEventLog = MarkerLog.ENABLED ? new MarkerLog() : null; - /** Request method of this request. Currently supports GET, POST, PUT, and DELETE. */ + /** + * Request method of this request. Currently supports GET, POST, PUT, DELETE, HEAD, OPTIONS, + * TRACE, and PATCH. + */ private final int mMethod; /** URL of this request. */ @@ -127,7 +134,7 @@ public abstract class Request implements Comparable> { mErrorListener = listener; setRetryPolicy(new DefaultRetryPolicy()); - mDefaultTrafficStatsTag = TextUtils.isEmpty(url) ? 0: Uri.parse(url).getHost().hashCode(); + mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url); } /** @@ -140,9 +147,12 @@ public abstract class Request implements Comparable> { /** * Set a tag on this request. Can be used to cancel all requests with this * tag by {@link RequestQueue#cancelAll(Object)}. + * + * @return This Request object to allow for chaining. */ - public void setTag(Object tag) { + public Request setTag(Object tag) { mTag = tag; + return this; } /** @@ -160,11 +170,30 @@ public abstract class Request implements Comparable> { return mDefaultTrafficStatsTag; } + /** + * @return The hashcode of the URL's host component, or 0 if there is none. + */ + private static int findDefaultTrafficStatsTag(String url) { + if (!TextUtils.isEmpty(url)) { + Uri uri = Uri.parse(url); + if (uri != null) { + String host = uri.getHost(); + if (host != null) { + return host.hashCode(); + } + } + } + return 0; + } + /** * Sets the retry policy for this request. + * + * @return This Request object to allow for chaining. */ - public void setRetryPolicy(RetryPolicy retryPolicy) { + public Request setRetryPolicy(RetryPolicy retryPolicy) { mRetryPolicy = retryPolicy; + return this; } /** @@ -216,16 +245,22 @@ public abstract class Request implements Comparable> { /** * Associates this request with the given queue. The request queue will be notified when this * request has finished. + * + * @return This Request object to allow for chaining. */ - public void setRequestQueue(RequestQueue requestQueue) { + public Request setRequestQueue(RequestQueue requestQueue) { mRequestQueue = requestQueue; + return this; } /** * Sets the sequence number of this request. Used by {@link RequestQueue}. + * + * @return This Request object to allow for chaining. */ - public final void setSequence(int sequence) { + public final Request setSequence(int sequence) { mSequence = sequence; + return this; } /** @@ -255,9 +290,12 @@ public abstract class Request implements Comparable> { /** * Annotates this request with an entry retrieved for it from cache. * Used for cache coherency support. + * + * @return This Request object to allow for chaining. */ - public void setCacheEntry(Cache.Entry entry) { + public Request setCacheEntry(Cache.Entry entry) { mCacheEntry = entry; + return this; } /** @@ -419,9 +457,12 @@ public abstract class Request implements Comparable> { /** * Set whether or not responses to this request should be cached. + * + * @return This Request object to allow for chaining. */ - public final void setShouldCache(boolean shouldCache) { + public final Request setShouldCache(boolean shouldCache) { mShouldCache = shouldCache; + return this; } /** diff --git a/Chan/src/com/android/volley/RequestQueue.java b/Chan/src/com/android/volley/RequestQueue.java index 94898f47..5c0e7afb 100644 --- a/Chan/src/com/android/volley/RequestQueue.java +++ b/Chan/src/com/android/volley/RequestQueue.java @@ -16,6 +16,9 @@ package com.android.volley; +import android.os.Handler; +import android.os.Looper; + import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -25,9 +28,6 @@ import java.util.Set; import java.util.concurrent.PriorityBlockingQueue; import java.util.concurrent.atomic.AtomicInteger; -import android.os.Handler; -import android.os.Looper; - /** * A request dispatch queue with a thread pool of dispatchers. * @@ -35,7 +35,6 @@ import android.os.Looper; * resolving from either cache or network on a worker thread, and then delivering * a parsed response on the main thread. */ -@SuppressWarnings("rawtypes") public class RequestQueue { /** Used for generating monotonically-increasing sequence numbers for requests. */ @@ -51,28 +50,28 @@ public class RequestQueue { * is not contained in that list. Is null if no requests are staged. * */ - private final Map> mWaitingRequests = - new HashMap>(); + private final Map>> mWaitingRequests = + new HashMap>>(); /** * The set of all requests currently being processed by this RequestQueue. A Request * will be in this set if it is waiting in any queue or currently being processed by * any dispatcher. */ - private final Set mCurrentRequests = new HashSet(); + private final Set> mCurrentRequests = new HashSet>(); /** The cache triage queue. */ - private final PriorityBlockingQueue mCacheQueue = - new PriorityBlockingQueue(); + private final PriorityBlockingQueue> mCacheQueue = + new PriorityBlockingQueue>(); /** The queue of requests that are actually going out to the network. */ - private final PriorityBlockingQueue mNetworkQueue = - new PriorityBlockingQueue(); + private final PriorityBlockingQueue> mNetworkQueue = + new PriorityBlockingQueue>(); /** Number of network request dispatcher threads to start. */ private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4; - /** Cache interface for retrieving and storing respones. */ + /** Cache interface for retrieving and storing responses. */ private final Cache mCache; /** Network interface for performing requests. */ @@ -214,7 +213,7 @@ public class RequestQueue { * @param request The request to service * @return The passed-in request */ - public Request add(Request request) { + public Request add(Request request) { // Tag the request as belonging to this queue and add it to the set of current requests. request.setRequestQueue(this); synchronized (mCurrentRequests) { @@ -236,9 +235,9 @@ public class RequestQueue { String cacheKey = request.getCacheKey(); if (mWaitingRequests.containsKey(cacheKey)) { // There is already a request in flight. Queue up. - Queue stagedRequests = mWaitingRequests.get(cacheKey); + Queue> stagedRequests = mWaitingRequests.get(cacheKey); if (stagedRequests == null) { - stagedRequests = new LinkedList(); + stagedRequests = new LinkedList>(); } stagedRequests.add(request); mWaitingRequests.put(cacheKey, stagedRequests); @@ -262,7 +261,7 @@ public class RequestQueue { *

Releases waiting requests for request.getCacheKey() if * request.shouldCache().

*/ - void finish(Request request) { + void finish(Request request) { // Remove from the set of requests currently being processed. synchronized (mCurrentRequests) { mCurrentRequests.remove(request); @@ -271,7 +270,7 @@ public class RequestQueue { if (request.shouldCache()) { synchronized (mWaitingRequests) { String cacheKey = request.getCacheKey(); - Queue waitingRequests = mWaitingRequests.remove(cacheKey); + Queue> waitingRequests = mWaitingRequests.remove(cacheKey); if (waitingRequests != null) { if (VolleyLog.DEBUG) { VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.", diff --git a/Chan/src/com/android/volley/ServerError.java b/Chan/src/com/android/volley/ServerError.java index d76f979a..b29a6c66 100644 --- a/Chan/src/com/android/volley/ServerError.java +++ b/Chan/src/com/android/volley/ServerError.java @@ -16,6 +16,8 @@ package com.android.volley; +import com.android.volley.NetworkResponse; +import com.android.volley.VolleyError; /** * Indicates that the error responded with an error response. diff --git a/Chan/src/com/android/volley/VolleyLog.java b/Chan/src/com/android/volley/VolleyLog.java index c3ad5800..66846902 100644 --- a/Chan/src/com/android/volley/VolleyLog.java +++ b/Chan/src/com/android/volley/VolleyLog.java @@ -16,13 +16,13 @@ package com.android.volley; +import android.os.SystemClock; +import android.util.Log; + import java.util.ArrayList; import java.util.List; import java.util.Locale; -import android.os.SystemClock; -import android.util.Log; - /** Logging helper class. */ public class VolleyLog { public static String TAG = "Volley"; diff --git a/Chan/src/com/android/volley/toolbox/AndroidAuthenticator.java b/Chan/src/com/android/volley/toolbox/AndroidAuthenticator.java index 683a0e7f..371fd83d 100644 --- a/Chan/src/com/android/volley/toolbox/AndroidAuthenticator.java +++ b/Chan/src/com/android/volley/toolbox/AndroidAuthenticator.java @@ -16,6 +16,8 @@ package com.android.volley.toolbox; +import com.android.volley.AuthFailureError; + import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AccountManagerFuture; @@ -23,8 +25,6 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; -import com.android.volley.AuthFailureError; - /** * An Authenticator that uses {@link AccountManager} to get auth * tokens of a specified type for a specified account. @@ -67,10 +67,11 @@ public class AndroidAuthenticator implements Authenticator { return mAccount; } + // TODO: Figure out what to do about notifyAuthFailure + @SuppressWarnings("deprecation") @Override public String getAuthToken() throws AuthFailureError { final AccountManager accountManager = AccountManager.get(mContext); - @SuppressWarnings("deprecation") AccountManagerFuture future = accountManager.getAuthToken(mAccount, mAuthTokenType, mNotifyAuthFailure, null, null); Bundle result; diff --git a/Chan/src/com/android/volley/toolbox/BasicNetwork.java b/Chan/src/com/android/volley/toolbox/BasicNetwork.java index 94c82044..c82fc34a 100644 --- a/Chan/src/com/android/volley/toolbox/BasicNetwork.java +++ b/Chan/src/com/android/volley/toolbox/BasicNetwork.java @@ -16,22 +16,6 @@ package com.android.volley.toolbox; -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.SocketTimeoutException; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; - -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.StatusLine; -import org.apache.http.conn.ConnectTimeoutException; -import org.apache.http.impl.cookie.DateUtils; - import android.os.SystemClock; import com.android.volley.AuthFailureError; @@ -47,6 +31,22 @@ import com.android.volley.TimeoutError; import com.android.volley.VolleyError; import com.android.volley.VolleyLog; +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.StatusLine; +import org.apache.http.conn.ConnectTimeoutException; +import org.apache.http.impl.cookie.DateUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.SocketTimeoutException; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + /** * A network performing Volley requests over an {@link HttpStack}. */ @@ -98,7 +98,8 @@ public class BasicNetwork implements Network { // Handle cache validation. if (statusCode == HttpStatus.SC_NOT_MODIFIED) { return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, - request.getCacheEntry().data, responseHeaders, true); + request.getCacheEntry() == null ? null : request.getCacheEntry().data, + responseHeaders, true); } // Some responses such as 204s do not have content. We must check. diff --git a/Chan/src/com/android/volley/toolbox/ClearCacheRequest.java b/Chan/src/com/android/volley/toolbox/ClearCacheRequest.java index f61e34e0..a3478bf1 100644 --- a/Chan/src/com/android/volley/toolbox/ClearCacheRequest.java +++ b/Chan/src/com/android/volley/toolbox/ClearCacheRequest.java @@ -16,14 +16,14 @@ package com.android.volley.toolbox; -import android.os.Handler; -import android.os.Looper; - import com.android.volley.Cache; import com.android.volley.NetworkResponse; import com.android.volley.Request; import com.android.volley.Response; +import android.os.Handler; +import android.os.Looper; + /** * A synthetic request used for clearing the cache. */ diff --git a/Chan/src/com/android/volley/toolbox/DiskBasedCache.java b/Chan/src/com/android/volley/toolbox/DiskBasedCache.java index 287b0e72..1ba61596 100644 --- a/Chan/src/com/android/volley/toolbox/DiskBasedCache.java +++ b/Chan/src/com/android/volley/toolbox/DiskBasedCache.java @@ -55,7 +55,7 @@ public class DiskBasedCache implements Cache { private final int mMaxCacheSizeInBytes; /** Default maximum disk usage in bytes. */ - private static final int DEFAULT_DISK_USAGE_BYTES = 25 * 1024 * 1024; + private static final int DEFAULT_DISK_USAGE_BYTES = 50 * 1024 * 1024; /** High water mark percentage for the cache */ private static final float HYSTERESIS_FACTOR = 0.9f; @@ -361,12 +361,12 @@ public class DiskBasedCache implements Cache { */ public CacheHeader(String key, Entry entry) { this.key = key; - this.size = entry.data.length; - this.etag = entry.etag; - this.serverDate = entry.serverDate; - this.ttl = entry.ttl; - this.softTtl = entry.softTtl; - this.responseHeaders = entry.responseHeaders; + size = entry.data.length; + etag = entry.etag; + serverDate = entry.serverDate; + ttl = entry.ttl; + softTtl = entry.softTtl; + responseHeaders = entry.responseHeaders; } /** diff --git a/Chan/src/com/android/volley/toolbox/HttpClientStack.java b/Chan/src/com/android/volley/toolbox/HttpClientStack.java index 0fb10ea8..377110ef 100644 --- a/Chan/src/com/android/volley/toolbox/HttpClientStack.java +++ b/Chan/src/com/android/volley/toolbox/HttpClientStack.java @@ -16,10 +16,9 @@ package com.android.volley.toolbox; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import com.android.volley.AuthFailureError; +import com.android.volley.Request; +import com.android.volley.Request.Method; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; @@ -28,22 +27,26 @@ import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpHead; +import org.apache.http.client.methods.HttpOptions; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpTrace; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.message.BasicNameValuePair; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; -import com.android.volley.AuthFailureError; -import com.android.volley.Request; -import com.android.volley.Request.Method; +import java.io.IOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; /** * An HttpStack that performs request over an {@link HttpClient}. */ -@SuppressWarnings("deprecation") public class HttpClientStack implements HttpStack { protected final HttpClient mClient; @@ -87,6 +90,7 @@ public class HttpClientStack implements HttpStack { /** * Creates the appropriate subclass of HttpUriRequest for passed in request. */ + @SuppressWarnings("deprecation") /* protected */ static HttpUriRequest createHttpRequest(Request request, Map additionalHeaders) throws AuthFailureError { switch (request.getMethod()) { @@ -122,6 +126,18 @@ public class HttpClientStack implements HttpStack { setEntityIfNonEmptyBody(putRequest, request); return putRequest; } + case Method.HEAD: + return new HttpHead(request.getUrl()); + case Method.OPTIONS: + return new HttpOptions(request.getUrl()); + case Method.TRACE: + return new HttpTrace(request.getUrl()); + case Method.PATCH: { + HttpPatch patchRequest = new HttpPatch(request.getUrl()); + patchRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType()); + setEntityIfNonEmptyBody(patchRequest, request); + return patchRequest; + } default: throw new IllegalStateException("Unknown request method."); } @@ -144,4 +160,35 @@ public class HttpClientStack implements HttpStack { protected void onPrepareRequest(HttpUriRequest request) throws IOException { // Nothing. } + + /** + * The HttpPatch class does not exist in the Android framework, so this has been defined here. + */ + public static final class HttpPatch extends HttpEntityEnclosingRequestBase { + + public final static String METHOD_NAME = "PATCH"; + + public HttpPatch() { + super(); + } + + public HttpPatch(final URI uri) { + super(); + setURI(uri); + } + + /** + * @throws IllegalArgumentException if the uri is invalid. + */ + public HttpPatch(final String uri) { + super(); + setURI(URI.create(uri)); + } + + @Override + public String getMethod() { + return METHOD_NAME; + } + + } } diff --git a/Chan/src/com/android/volley/toolbox/HttpHeaderParser.java b/Chan/src/com/android/volley/toolbox/HttpHeaderParser.java index 42acf613..cb084322 100644 --- a/Chan/src/com/android/volley/toolbox/HttpHeaderParser.java +++ b/Chan/src/com/android/volley/toolbox/HttpHeaderParser.java @@ -16,14 +16,14 @@ package com.android.volley.toolbox; -import java.util.Map; +import com.android.volley.Cache; +import com.android.volley.NetworkResponse; import org.apache.http.impl.cookie.DateParseException; import org.apache.http.impl.cookie.DateUtils; import org.apache.http.protocol.HTTP; -import com.android.volley.Cache; -import com.android.volley.NetworkResponse; +import java.util.Map; /** * Utility methods for parsing HTTP headers. diff --git a/Chan/src/com/android/volley/toolbox/HttpStack.java b/Chan/src/com/android/volley/toolbox/HttpStack.java index 0f017615..a52fd06c 100644 --- a/Chan/src/com/android/volley/toolbox/HttpStack.java +++ b/Chan/src/com/android/volley/toolbox/HttpStack.java @@ -16,13 +16,13 @@ package com.android.volley.toolbox; -import java.io.IOException; -import java.util.Map; +import com.android.volley.AuthFailureError; +import com.android.volley.Request; import org.apache.http.HttpResponse; -import com.android.volley.AuthFailureError; -import com.android.volley.Request; +import java.io.IOException; +import java.util.Map; /** * An HTTP stack abstraction. diff --git a/Chan/src/com/android/volley/toolbox/HurlStack.java b/Chan/src/com/android/volley/toolbox/HurlStack.java index 96296a99..49bdf6ac 100644 --- a/Chan/src/com/android/volley/toolbox/HurlStack.java +++ b/Chan/src/com/android/volley/toolbox/HurlStack.java @@ -16,6 +16,20 @@ package com.android.volley.toolbox; +import com.android.volley.AuthFailureError; +import com.android.volley.Request; +import com.android.volley.Request.Method; + +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.ProtocolVersion; +import org.apache.http.StatusLine; +import org.apache.http.entity.BasicHttpEntity; +import org.apache.http.message.BasicHeader; +import org.apache.http.message.BasicHttpResponse; +import org.apache.http.message.BasicStatusLine; + import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; @@ -29,20 +43,6 @@ import java.util.Map.Entry; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSocketFactory; -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.ProtocolVersion; -import org.apache.http.StatusLine; -import org.apache.http.entity.BasicHttpEntity; -import org.apache.http.message.BasicHeader; -import org.apache.http.message.BasicHttpResponse; -import org.apache.http.message.BasicStatusLine; - -import com.android.volley.AuthFailureError; -import com.android.volley.Request; -import com.android.volley.Request.Method; - /** * An {@link HttpStack} based on {@link HttpURLConnection}. */ @@ -213,6 +213,19 @@ public class HurlStack implements HttpStack { connection.setRequestMethod("PUT"); addBodyIfExists(connection, request); break; + case Method.HEAD: + connection.setRequestMethod("HEAD"); + break; + case Method.OPTIONS: + connection.setRequestMethod("OPTIONS"); + break; + case Method.TRACE: + connection.setRequestMethod("TRACE"); + break; + case Method.PATCH: + addBodyIfExists(connection, request); + connection.setRequestMethod("PATCH"); + break; default: throw new IllegalStateException("Unknown method type."); } diff --git a/Chan/src/com/android/volley/toolbox/ImageLoader.java b/Chan/src/com/android/volley/toolbox/ImageLoader.java index 4335a89a..fe0f32ad 100644 --- a/Chan/src/com/android/volley/toolbox/ImageLoader.java +++ b/Chan/src/com/android/volley/toolbox/ImageLoader.java @@ -15,9 +15,6 @@ */ package com.android.volley.toolbox; -import java.util.HashMap; -import java.util.LinkedList; - import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.os.Handler; @@ -29,6 +26,10 @@ import com.android.volley.RequestQueue; import com.android.volley.Response.ErrorListener; import com.android.volley.Response.Listener; import com.android.volley.VolleyError; +import com.android.volley.toolbox.ImageRequest; + +import java.util.HashMap; +import java.util.LinkedList; /** * Helper that handles loading and caching images from remote URLs. @@ -274,10 +275,10 @@ public class ImageLoader { // Remove this request from the list of in-flight requests. BatchedImageRequest request = mInFlightRequests.remove(cacheKey); - // Set the error for this request - request.setError(error); - if (request != null) { + // Set the error for this request + request.setError(error); + // Send the batched response batchResponse(cacheKey, request); } diff --git a/Chan/src/com/android/volley/toolbox/ImageRequest.java b/Chan/src/com/android/volley/toolbox/ImageRequest.java index 7de78e40..2ebe015b 100644 --- a/Chan/src/com/android/volley/toolbox/ImageRequest.java +++ b/Chan/src/com/android/volley/toolbox/ImageRequest.java @@ -16,10 +16,6 @@ package com.android.volley.toolbox; -import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.graphics.BitmapFactory; - import com.android.volley.DefaultRetryPolicy; import com.android.volley.NetworkResponse; import com.android.volley.ParseError; @@ -27,6 +23,10 @@ import com.android.volley.Request; import com.android.volley.Response; import com.android.volley.VolleyLog; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.BitmapFactory; + /** * A canned request for getting an image at a given URL and calling * back with a decoded Bitmap. diff --git a/Chan/src/com/android/volley/toolbox/JsonArrayRequest.java b/Chan/src/com/android/volley/toolbox/JsonArrayRequest.java index df83190b..b1eae805 100644 --- a/Chan/src/com/android/volley/toolbox/JsonArrayRequest.java +++ b/Chan/src/com/android/volley/toolbox/JsonArrayRequest.java @@ -16,17 +16,17 @@ package com.android.volley.toolbox; -import java.io.UnsupportedEncodingException; - -import org.json.JSONArray; -import org.json.JSONException; - import com.android.volley.NetworkResponse; import com.android.volley.ParseError; import com.android.volley.Response; import com.android.volley.Response.ErrorListener; import com.android.volley.Response.Listener; +import org.json.JSONArray; +import org.json.JSONException; + +import java.io.UnsupportedEncodingException; + /** * A request for retrieving a {@link JSONArray} response body at a given URL. */ diff --git a/Chan/src/com/android/volley/toolbox/JsonObjectRequest.java b/Chan/src/com/android/volley/toolbox/JsonObjectRequest.java index ca5225f4..74821cba 100644 --- a/Chan/src/com/android/volley/toolbox/JsonObjectRequest.java +++ b/Chan/src/com/android/volley/toolbox/JsonObjectRequest.java @@ -16,17 +16,17 @@ package com.android.volley.toolbox; -import java.io.UnsupportedEncodingException; - -import org.json.JSONException; -import org.json.JSONObject; - import com.android.volley.NetworkResponse; import com.android.volley.ParseError; import com.android.volley.Response; import com.android.volley.Response.ErrorListener; import com.android.volley.Response.Listener; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.UnsupportedEncodingException; + /** * A request for retrieving a {@link JSONObject} response body at a given URL, allowing for an * optional {@link JSONObject} to be passed in as part of the request body. diff --git a/Chan/src/com/android/volley/toolbox/JsonRequest.java b/Chan/src/com/android/volley/toolbox/JsonRequest.java index e60bce16..f11ac14e 100644 --- a/Chan/src/com/android/volley/toolbox/JsonRequest.java +++ b/Chan/src/com/android/volley/toolbox/JsonRequest.java @@ -16,8 +16,6 @@ package com.android.volley.toolbox; -import java.io.UnsupportedEncodingException; - import com.android.volley.NetworkResponse; import com.android.volley.Request; import com.android.volley.Response; @@ -25,6 +23,8 @@ import com.android.volley.Response.ErrorListener; import com.android.volley.Response.Listener; import com.android.volley.VolleyLog; +import java.io.UnsupportedEncodingException; + /** * A request for retrieving a T type response body at a given URL that also * optionally sends along a JSON body in the request specified. diff --git a/Chan/src/com/android/volley/toolbox/NetworkImageView.java b/Chan/src/com/android/volley/toolbox/NetworkImageView.java index 97379bd5..692e9885 100644 --- a/Chan/src/com/android/volley/toolbox/NetworkImageView.java +++ b/Chan/src/com/android/volley/toolbox/NetworkImageView.java @@ -48,14 +48,7 @@ public class NetworkImageView extends ImageView { /** Current ImageContainer. (either in-flight or finished) */ private ImageContainer mImageContainer; - - /** - * Max amount to scale the image inside the view - */ - protected float mMaxScale = 1; - - private int mFadeTime = -1; - + public NetworkImageView(Context context) { this(context, null); } @@ -67,23 +60,7 @@ public class NetworkImageView extends ImageView { public NetworkImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } - - /** - * How larger the inner bitmap is to the view size. - * @param amount - */ - public void setMaxScale(float amount) { - mMaxScale = amount; - } - - /** - * Animate the image fading in. - * @param duration of the animation in milliseconds - */ - public void setFadeIn(int time) { - mFadeTime = time; - } - + /** * Sets URL of the image that should be loaded into this view. Note that calling this will * immediately either set the cached image (if available) or the default image specified by @@ -123,15 +100,19 @@ public class NetworkImageView extends ImageView { * Loads the image for the view if it isn't already loaded. * @param isInLayoutPass True if this was invoked from a layout pass, false otherwise. */ - private void loadImageIfNecessary(final boolean isInLayoutPass) { + void loadImageIfNecessary(final boolean isInLayoutPass) { int width = getWidth(); int height = getHeight(); - boolean isFullyWrapContent = getLayoutParams() != null - && getLayoutParams().height == LayoutParams.WRAP_CONTENT - && getLayoutParams().width == LayoutParams.WRAP_CONTENT; + boolean wrapWidth = false, wrapHeight = false; + if (getLayoutParams() != null) { + wrapWidth = getLayoutParams().width == LayoutParams.WRAP_CONTENT; + wrapHeight = getLayoutParams().height == LayoutParams.WRAP_CONTENT; + } + // if the view's bounds aren't known yet, and this is not a wrap-content/wrap-content // view, hold off on loading the image. + boolean isFullyWrapContent = wrapWidth && wrapHeight; if (width == 0 && height == 0 && !isFullyWrapContent) { return; } @@ -143,7 +124,7 @@ public class NetworkImageView extends ImageView { mImageContainer.cancelRequest(); mImageContainer = null; } - setImageBitmap(null); + setDefaultImageOrNull(); return; } @@ -155,10 +136,14 @@ public class NetworkImageView extends ImageView { } else { // if there is a pre-existing request, cancel it if it's fetching a different URL. mImageContainer.cancelRequest(); - setImageBitmap(null); + setDefaultImageOrNull(); } } + // Calculate the max image width / height to use while ignoring WRAP_CONTENT dimens. + int maxWidth = wrapWidth ? 0 : width; + int maxHeight = wrapHeight ? 0 : height; + // The pre-existing content of this view didn't match the current URL. Load the new image // from the network. ImageContainer newContainer = mImageLoader.get(mUrl, @@ -168,8 +153,6 @@ public class NetworkImageView extends ImageView { if (mErrorImageId != 0) { setImageResource(mErrorImageId); } - - NetworkImageView.this.onErrorResponse(error); } @Override @@ -189,25 +172,26 @@ public class NetworkImageView extends ImageView { } if (response.getBitmap() != null) { - if (mFadeTime > 0 && !isImmediate) { - setAlpha(0f); - animate().alpha(1).setDuration(mFadeTime); - } - setImageBitmap(response.getBitmap()); } else if (mDefaultImageId != 0) { setImageResource(mDefaultImageId); } } - }, (int)(width * mMaxScale), (int)(height * mMaxScale)); + }, maxWidth, maxHeight); // update the ImageContainer to be the new bitmap container. mImageContainer = newContainer; } - - public void onErrorResponse(VolleyError error) { + + private void setDefaultImageOrNull() { + if(mDefaultImageId != 0) { + setImageResource(mDefaultImageId); + } + else { + setImageBitmap(null); + } } - + @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); diff --git a/Chan/src/com/android/volley/toolbox/RequestFuture.java b/Chan/src/com/android/volley/toolbox/RequestFuture.java index 78bfb750..173c44cc 100644 --- a/Chan/src/com/android/volley/toolbox/RequestFuture.java +++ b/Chan/src/com/android/volley/toolbox/RequestFuture.java @@ -16,15 +16,15 @@ package com.android.volley.toolbox; +import com.android.volley.Request; +import com.android.volley.Response; +import com.android.volley.VolleyError; + import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import com.android.volley.Request; -import com.android.volley.Response; -import com.android.volley.VolleyError; - /** * A Future that represents a Volley request. * diff --git a/Chan/src/com/android/volley/toolbox/StringRequest.java b/Chan/src/com/android/volley/toolbox/StringRequest.java index 1a5040c2..6b3dfcf8 100644 --- a/Chan/src/com/android/volley/toolbox/StringRequest.java +++ b/Chan/src/com/android/volley/toolbox/StringRequest.java @@ -16,14 +16,14 @@ package com.android.volley.toolbox; -import java.io.UnsupportedEncodingException; - import com.android.volley.NetworkResponse; import com.android.volley.Request; import com.android.volley.Response; import com.android.volley.Response.ErrorListener; import com.android.volley.Response.Listener; +import java.io.UnsupportedEncodingException; + /** * A canned request for retrieving the response body at a given URL as a String. */ diff --git a/Chan/src/com/android/volley/toolbox/Volley.java b/Chan/src/com/android/volley/toolbox/Volley.java index 21867288..0e04e876 100644 --- a/Chan/src/com/android/volley/toolbox/Volley.java +++ b/Chan/src/com/android/volley/toolbox/Volley.java @@ -16,8 +16,6 @@ package com.android.volley.toolbox; -import java.io.File; - import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager.NameNotFoundException; @@ -27,6 +25,8 @@ import android.os.Build; import com.android.volley.Network; import com.android.volley.RequestQueue; +import java.io.File; + public class Volley { /** Default on-disk cache directory. */ diff --git a/Chan/src/org/floens/chan/ChanApplication.java b/Chan/src/org/floens/chan/ChanApplication.java index eb1c8dc6..f92bce9a 100644 --- a/Chan/src/org/floens/chan/ChanApplication.java +++ b/Chan/src/org/floens/chan/ChanApplication.java @@ -6,6 +6,7 @@ import org.floens.chan.core.manager.BoardManager; import org.floens.chan.core.manager.PinnedManager; import org.floens.chan.core.manager.PinnedManager.PinListener; import org.floens.chan.core.manager.ReplyManager; +import org.floens.chan.core.net.BitmapLruImageCache; import org.floens.chan.database.DatabaseManager; import org.floens.chan.service.WatchService; import org.floens.chan.utils.IconCache; @@ -17,7 +18,6 @@ import android.preference.PreferenceManager; import android.view.ViewConfiguration; import com.android.volley.RequestQueue; -import com.android.volley.extra.BitmapLruImageCache; import com.android.volley.toolbox.ImageLoader; import com.android.volley.toolbox.Volley; diff --git a/Chan/src/com/android/volley/extra/BitmapLruImageCache.java b/Chan/src/org/floens/chan/core/net/BitmapLruImageCache.java similarity index 93% rename from Chan/src/com/android/volley/extra/BitmapLruImageCache.java rename to Chan/src/org/floens/chan/core/net/BitmapLruImageCache.java index 4b06acca..ebacf92c 100644 --- a/Chan/src/com/android/volley/extra/BitmapLruImageCache.java +++ b/Chan/src/org/floens/chan/core/net/BitmapLruImageCache.java @@ -1,4 +1,4 @@ -package com.android.volley.extra; +package org.floens.chan.core.net; import android.graphics.Bitmap; import android.util.LruCache; @@ -9,12 +9,12 @@ public class BitmapLruImageCache extends LruCache implements Ima public BitmapLruImageCache(int maxSize) { super(maxSize); } - + @Override protected int sizeOf(String key, Bitmap value) { return value.getRowBytes() * value.getHeight(); } - + @Override public Bitmap getBitmap(String url) { return get(url); diff --git a/Chan/src/org/floens/chan/core/net/BoardsRequest.java b/Chan/src/org/floens/chan/core/net/BoardsRequest.java index 408a9867..e398a6c9 100644 --- a/Chan/src/org/floens/chan/core/net/BoardsRequest.java +++ b/Chan/src/org/floens/chan/core/net/BoardsRequest.java @@ -9,7 +9,6 @@ import android.util.JsonReader; import com.android.volley.Response.ErrorListener; import com.android.volley.Response.Listener; -import com.android.volley.extra.JsonReaderRequest; public class BoardsRequest extends JsonReaderRequest> { public BoardsRequest(String url, Listener> listener, ErrorListener errorListener) { diff --git a/Chan/src/org/floens/chan/core/net/ChanReaderRequest.java b/Chan/src/org/floens/chan/core/net/ChanReaderRequest.java index ffc432b0..b18a7935 100644 --- a/Chan/src/org/floens/chan/core/net/ChanReaderRequest.java +++ b/Chan/src/org/floens/chan/core/net/ChanReaderRequest.java @@ -14,7 +14,6 @@ import android.util.JsonReader; import com.android.volley.ParseError; import com.android.volley.Response.ErrorListener; import com.android.volley.Response.Listener; -import com.android.volley.extra.JsonReaderRequest; public class ChanReaderRequest extends JsonReaderRequest> { private Loadable loadable; diff --git a/Chan/src/com/android/volley/extra/JsonReaderRequest.java b/Chan/src/org/floens/chan/core/net/JsonReaderRequest.java similarity index 93% rename from Chan/src/com/android/volley/extra/JsonReaderRequest.java rename to Chan/src/org/floens/chan/core/net/JsonReaderRequest.java index ea5f31a8..9ec1acac 100644 --- a/Chan/src/com/android/volley/extra/JsonReaderRequest.java +++ b/Chan/src/org/floens/chan/core/net/JsonReaderRequest.java @@ -1,4 +1,4 @@ -package com.android.volley.extra; +package org.floens.chan.core.net; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -19,41 +19,41 @@ import com.android.volley.toolbox.HttpHeaderParser; public abstract class JsonReaderRequest extends Request { protected final Listener listener; private VolleyError error; - + public JsonReaderRequest(String url, Listener listener, ErrorListener errorListener) { super(Method.GET, url, errorListener); - + this.listener = listener; } - + @Override protected void deliverResponse(T response) { listener.onResponse(response); } - + public void setError(VolleyError error) { this.error = error; } - + @Override protected Response parseNetworkResponse(NetworkResponse response) { try { ByteArrayInputStream baos = new ByteArrayInputStream(response.data); - + JsonReader reader = new JsonReader(new InputStreamReader(baos, "UTF-8")); - + // long start = System.currentTimeMillis(); - + T read = readJson(reader); - + try { reader.close(); } catch (IOException e) { e.printStackTrace(); } - + // Log.e("Chan", "Total time: " + (System.currentTimeMillis() - start)); - + if (read == null) { return Response.error(new VolleyError()); } else if (error != null) { @@ -65,7 +65,7 @@ public abstract class JsonReaderRequest extends Request { return Response.error(new ParseError(e)); } } - + public abstract T readJson(JsonReader reader); } diff --git a/Chan/src/org/floens/chan/ui/view/CustomNetworkImageView.java b/Chan/src/org/floens/chan/ui/view/CustomNetworkImageView.java new file mode 100644 index 00000000..9d606a2f --- /dev/null +++ b/Chan/src/org/floens/chan/ui/view/CustomNetworkImageView.java @@ -0,0 +1,277 @@ +/** + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.floens.chan.ui.view; + +import android.content.Context; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.ViewGroup.LayoutParams; +import android.widget.ImageView; + +import com.android.volley.VolleyError; +import com.android.volley.toolbox.ImageLoader; +import com.android.volley.toolbox.ImageLoader.ImageContainer; +import com.android.volley.toolbox.ImageLoader.ImageListener; + +/** + * Custom version of NetworkImageView + * + * Handles fetching an image from a URL as well as the life-cycle of the + * associated request. + */ +public class CustomNetworkImageView extends ImageView { + /** The URL of the network image to load */ + private String mUrl; + + /** + * Resource ID of the image to be used as a placeholder until the network + * image is loaded. + */ + private int mDefaultImageId; + + /** + * Resource ID of the image to be used if the network response fails. + */ + private int mErrorImageId; + + /** Local copy of the ImageLoader. */ + private ImageLoader mImageLoader; + + /** Current ImageContainer. (either in-flight or finished) */ + private ImageContainer mImageContainer; + + /** + * Max amount to scale the image inside the view + */ + private float mMaxScale = 1; + + private int mFadeTime; + + public CustomNetworkImageView(Context context) { + this(context, null); + } + + public CustomNetworkImageView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public CustomNetworkImageView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * How larger the inner bitmap is to the defined view size. + * + * @param amount + */ + public void setMaxScale(float amount) { + mMaxScale = amount; + } + + public float getMaxScale() { + return mMaxScale; + } + + /** + * Animate the image fading in. + * + * @param duration + * duration of the fade animation in milliseconds + */ + public void setFadeIn(int time) { + mFadeTime = time; + } + + /** + * Sets URL of the image that should be loaded into this view. Note that + * calling this will immediately either set the cached image (if available) + * or the default image specified by + * {@link CustomNetworkImageView#setDefaultImageResId(int)} on the view. + * + * NOTE: If applicable, + * {@link CustomNetworkImageView#setDefaultImageResId(int)} and + * {@link CustomNetworkImageView#setErrorImageResId(int)} should be called + * prior to calling this function. + * + * @param url + * The URL that should be loaded into this ImageView. + * @param imageLoader + * ImageLoader that will be used to make the request. + */ + public void setImageUrl(String url, ImageLoader imageLoader) { + mUrl = url; + mImageLoader = imageLoader; + // The URL has potentially changed. See if we need to load it. + loadImageIfNecessary(false); + } + + /** + * Sets the default image resource ID to be used for this view until the + * attempt to load it completes. + */ + public void setDefaultImageResId(int defaultImage) { + mDefaultImageId = defaultImage; + } + + /** + * Sets the error image resource ID to be used for this view in the event + * that the image requested fails to load. + */ + public void setErrorImageResId(int errorImage) { + mErrorImageId = errorImage; + } + + public void onErrorResponse(VolleyError error) { + } + + /** + * Loads the image for the view if it isn't already loaded. + * + * @param isInLayoutPass + * True if this was invoked from a layout pass, false otherwise. + */ + void loadImageIfNecessary(final boolean isInLayoutPass) { + int width = getWidth(); + int height = getHeight(); + + boolean wrapWidth = false, wrapHeight = false; + if (getLayoutParams() != null) { + wrapWidth = getLayoutParams().width == LayoutParams.WRAP_CONTENT; + wrapHeight = getLayoutParams().height == LayoutParams.WRAP_CONTENT; + } + + // if the view's bounds aren't known yet, and this is not a + // wrap-content/wrap-content + // view, hold off on loading the image. + boolean isFullyWrapContent = wrapWidth && wrapHeight; + if (width == 0 && height == 0 && !isFullyWrapContent) { + return; + } + + // if the URL to be loaded in this view is empty, cancel any old + // requests and clear the + // currently loaded image. + if (TextUtils.isEmpty(mUrl)) { + if (mImageContainer != null) { + mImageContainer.cancelRequest(); + mImageContainer = null; + } + setDefaultImageOrNull(); + return; + } + + // if there was an old request in this view, check if it needs to be + // canceled. + if (mImageContainer != null && mImageContainer.getRequestUrl() != null) { + if (mImageContainer.getRequestUrl().equals(mUrl)) { + // if the request is from the same URL, return. + return; + } else { + // if there is a pre-existing request, cancel it if it's + // fetching a different URL. + mImageContainer.cancelRequest(); + setDefaultImageOrNull(); + } + } + + // Calculate the max image width / height to use while ignoring + // WRAP_CONTENT dimens. + int maxWidth = wrapWidth ? 0 : width; + int maxHeight = wrapHeight ? 0 : height; + + // The pre-existing content of this view didn't match the current URL. + // Load the new image + // from the network. + ImageContainer newContainer = mImageLoader.get(mUrl, new ImageListener() { + @Override + public void onErrorResponse(VolleyError error) { + if (mErrorImageId != 0) { + setImageResource(mErrorImageId); + } + + CustomNetworkImageView.this.onErrorResponse(error); + } + + @Override + public void onResponse(final ImageContainer response, boolean isImmediate) { + // If this was an immediate response that was delivered inside + // of a layout + // pass do not set the image immediately as it will trigger a + // requestLayout + // inside of a layout. Instead, defer setting the image by + // posting back to + // the main thread. + if (isImmediate && isInLayoutPass) { + post(new Runnable() { + @Override + public void run() { + onResponse(response, false); + } + }); + return; + } + + if (response.getBitmap() != null) { + setImageBitmap(response.getBitmap()); + + if (mFadeTime > 0 && !isImmediate) { + setAlpha(0f); + animate().alpha(1f).setDuration(mFadeTime); + } + } else if (mDefaultImageId != 0) { + setImageResource(mDefaultImageId); + } + } + }, (int)(maxWidth * mMaxScale), (int)(maxHeight * mMaxScale)); + + // update the ImageContainer to be the new bitmap container. + mImageContainer = newContainer; + } + + private void setDefaultImageOrNull() { + if (mDefaultImageId != 0) { + setImageResource(mDefaultImageId); + } else { + setImageBitmap(null); + } + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + loadImageIfNecessary(true); + } + + @Override + protected void onDetachedFromWindow() { + if (mImageContainer != null) { + // If the view was bound to an image request, cancel it and clear + // out the image from the view. + mImageContainer.cancelRequest(); + setImageBitmap(null); + // also clear out the container so we can reload the image if + // necessary. + mImageContainer = null; + } + super.onDetachedFromWindow(); + } + + @Override + protected void drawableStateChanged() { + super.drawableStateChanged(); + invalidate(); + } +} diff --git a/Chan/src/org/floens/chan/ui/view/NetworkPhotoView.java b/Chan/src/org/floens/chan/ui/view/NetworkPhotoView.java index 6500d405..95dca7dc 100644 --- a/Chan/src/org/floens/chan/ui/view/NetworkPhotoView.java +++ b/Chan/src/org/floens/chan/ui/view/NetworkPhotoView.java @@ -10,62 +10,61 @@ import android.graphics.drawable.Drawable; import android.widget.Toast; import com.android.volley.VolleyError; -import com.android.volley.toolbox.NetworkImageView; /** * Extends NetworkImageView. * Attaches a PhotoViewAttacher when setBitmap is called. * Sets the progressBar to false when a bitmap gets set. */ -public class NetworkPhotoView extends NetworkImageView { +public class NetworkPhotoView extends CustomNetworkImageView { private PhotoViewAttacher attacher; private OnLongClickListener longClickListener; private OnViewTapListener viewTapListener; private ImageViewFragment fragment; - + public NetworkPhotoView(Context context) { super(context); } - + public void setImageViewFragment(ImageViewFragment fragment) { this.fragment = fragment; } - + public void setOnLongClickListenerToAttacher(OnLongClickListener listener) { longClickListener = listener; } - + public void setOnViewTapListenerToAttacher(OnViewTapListener listener) { viewTapListener = listener; } - + @Override public void setImageDrawable(Drawable drawable) { super.setImageDrawable(drawable); - + attacher = new PhotoViewAttacher(this); - attacher.setMaximumScale(mMaxScale); + attacher.setMaximumScale(getMaxScale()); attacher.setOnLongClickListener(longClickListener); attacher.setOnViewTapListener(viewTapListener); - + fragment.showProgressBar(false); } - + @Override public void onErrorResponse(VolleyError error) { super.onErrorResponse(error); - + // TODO: out of memory. We need a new image viewer for *large* images. Maybe copy the one from the gallery. System.gc(); Toast.makeText(getContext(), R.string.image_preview_failed, Toast.LENGTH_LONG).show(); - + fragment.showProgressBar(false); } - + @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - + if (attacher != null) { attacher.cleanup(); } diff --git a/Chan/src/org/floens/chan/ui/view/PostView.java b/Chan/src/org/floens/chan/ui/view/PostView.java index 9b7fcef8..484abfbf 100644 --- a/Chan/src/org/floens/chan/ui/view/PostView.java +++ b/Chan/src/org/floens/chan/ui/view/PostView.java @@ -46,7 +46,7 @@ public class PostView extends LinearLayout implements View.OnClickListener, View private boolean isBuild = false; private LinearLayout full; private LinearLayout right; - private NetworkImageView imageView; + private CustomNetworkImageView imageView; private TextView titleView; private TextView commentView; private TextView repliesCountView; @@ -234,7 +234,7 @@ public class PostView extends LinearLayout implements View.OnClickListener, View full.setOrientation(HORIZONTAL); // Create thumbnail - imageView = new NetworkImageView(context); + imageView = new CustomNetworkImageView(context); imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); imageView.setFadeIn(100);