From 65b100d2d156b95944a6622fd416e30cd64ec5c5 Mon Sep 17 00:00:00 2001 From: Floens Date: Sun, 21 Apr 2019 13:12:32 +0200 Subject: [PATCH 1/9] build: set target sdk to version 28, update gradle --- Clover/app/build.gradle | 6 +++--- Clover/build.gradle | 2 +- Clover/gradle/wrapper/gradle-wrapper.properties | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Clover/app/build.gradle b/Clover/app/build.gradle index 8eb4f098..1663472c 100644 --- a/Clover/app/build.gradle +++ b/Clover/app/build.gradle @@ -13,13 +13,13 @@ def getCommitHash = { -> } android { - compileSdkVersion 27 + compileSdkVersion 28 // update the travis config when changing this buildToolsVersion '28.0.3' defaultConfig { minSdkVersion 15 - targetSdkVersion 25 + targetSdkVersion 28 versionName "v3.0.2" // of the format XXYYZZ, where XX is major, YY is minor, ZZ is patch @@ -127,7 +127,7 @@ android { } dependencies { - def supportVersion = '27.1.1' + def supportVersion = '28.0.0' implementation "com.android.support:support-v13:${supportVersion}" implementation "com.android.support:appcompat-v7:${supportVersion}" diff --git a/Clover/build.gradle b/Clover/build.gradle index 2b29ba25..6ac36dd8 100644 --- a/Clover/build.gradle +++ b/Clover/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:3.4.0' } } diff --git a/Clover/gradle/wrapper/gradle-wrapper.properties b/Clover/gradle/wrapper/gradle-wrapper.properties index fedcafd2..74d569ce 100644 --- a/Clover/gradle/wrapper/gradle-wrapper.properties +++ b/Clover/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sat Oct 06 14:30:14 CEST 2018 +#Sun Apr 21 13:06:50 CEST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip From 8dac5e7b4cde78e897234387ba6e4b405a7d951d Mon Sep 17 00:00:00 2001 From: Floens Date: Sun, 21 Apr 2019 14:04:42 +0200 Subject: [PATCH 2/9] build/net: use upstream Volley Removes all custom volley code we had with a dependency on the official volley dependency. This fixes any build issues with the old apache http library, since that library doesn't exist on new platforms anymore. We had custom logic in the DiskBasedCache to limit the amount of cache files, we'll see if this has any impact now. It used to be a big slow down at start-up. The cache size has also gone from 10MB to 5MB because we can't change it. --- Clover/app/build.gradle | 6 +- .../com/android/volley/AuthFailureError.java | 61 -- .../main/java/com/android/volley/Cache.java | 97 --- .../com/android/volley/CacheDispatcher.java | 158 ----- .../android/volley/DefaultRetryPolicy.java | 98 --- .../com/android/volley/ExecutorDelivery.java | 118 ---- .../main/java/com/android/volley/Network.java | 30 - .../com/android/volley/NetworkDispatcher.java | 147 ----- .../java/com/android/volley/NetworkError.java | 38 -- .../com/android/volley/NetworkResponse.java | 62 -- .../com/android/volley/NoConnectionError.java | 31 - .../java/com/android/volley/ParseError.java | 36 -- .../main/java/com/android/volley/Request.java | 603 ------------------ .../java/com/android/volley/RequestQueue.java | 286 --------- .../java/com/android/volley/Response.java | 85 --- .../com/android/volley/ResponseDelivery.java | 35 - .../java/com/android/volley/RetryPolicy.java | 41 -- .../java/com/android/volley/ServerError.java | 34 - .../java/com/android/volley/TimeoutError.java | 23 - .../java/com/android/volley/VolleyError.java | 48 -- .../java/com/android/volley/VolleyLog.java | 179 ------ .../volley/compat/DelegateSSLSocket.java | 331 ---------- .../android/volley/compat/NoSSLv3Compat.java | 113 ---- .../volley/toolbox/AndroidAuthenticator.java | 102 --- .../android/volley/toolbox/Authenticator.java | 36 -- .../android/volley/toolbox/BasicNetwork.java | 263 -------- .../android/volley/toolbox/ByteArrayPool.java | 135 ---- .../volley/toolbox/ClearCacheRequest.java | 70 -- .../volley/toolbox/DiskBasedCache.java | 569 ----------------- .../volley/toolbox/HttpClientStack.java | 194 ------ .../volley/toolbox/HttpHeaderParser.java | 137 ---- .../com/android/volley/toolbox/HttpStack.java | 45 -- .../com/android/volley/toolbox/HurlStack.java | 248 ------- .../android/volley/toolbox/ImageLoader.java | 482 -------------- .../android/volley/toolbox/ImageRequest.java | 217 ------- .../volley/toolbox/JsonArrayRequest.java | 58 -- .../volley/toolbox/JsonObjectRequest.java | 76 --- .../android/volley/toolbox/JsonRequest.java | 102 --- .../volley/toolbox/NetworkImageView.java | 219 ------- .../com/android/volley/toolbox/NoCache.java | 49 -- .../toolbox/PoolingByteArrayOutputStream.java | 94 --- .../android/volley/toolbox/RequestFuture.java | 153 ----- .../android/volley/toolbox/StringRequest.java | 73 --- .../com/android/volley/toolbox/Volley.java | 58 -- .../org/floens/chan/core/di/NetModule.java | 8 +- .../chan/core/net/ProxiedHurlStack.java | 20 +- 46 files changed, 20 insertions(+), 6048 deletions(-) delete mode 100644 Clover/app/src/main/java/com/android/volley/AuthFailureError.java delete mode 100644 Clover/app/src/main/java/com/android/volley/Cache.java delete mode 100644 Clover/app/src/main/java/com/android/volley/CacheDispatcher.java delete mode 100644 Clover/app/src/main/java/com/android/volley/DefaultRetryPolicy.java delete mode 100644 Clover/app/src/main/java/com/android/volley/ExecutorDelivery.java delete mode 100644 Clover/app/src/main/java/com/android/volley/Network.java delete mode 100644 Clover/app/src/main/java/com/android/volley/NetworkDispatcher.java delete mode 100644 Clover/app/src/main/java/com/android/volley/NetworkError.java delete mode 100644 Clover/app/src/main/java/com/android/volley/NetworkResponse.java delete mode 100644 Clover/app/src/main/java/com/android/volley/NoConnectionError.java delete mode 100644 Clover/app/src/main/java/com/android/volley/ParseError.java delete mode 100644 Clover/app/src/main/java/com/android/volley/Request.java delete mode 100644 Clover/app/src/main/java/com/android/volley/RequestQueue.java delete mode 100644 Clover/app/src/main/java/com/android/volley/Response.java delete mode 100644 Clover/app/src/main/java/com/android/volley/ResponseDelivery.java delete mode 100644 Clover/app/src/main/java/com/android/volley/RetryPolicy.java delete mode 100644 Clover/app/src/main/java/com/android/volley/ServerError.java delete mode 100644 Clover/app/src/main/java/com/android/volley/TimeoutError.java delete mode 100644 Clover/app/src/main/java/com/android/volley/VolleyError.java delete mode 100644 Clover/app/src/main/java/com/android/volley/VolleyLog.java delete mode 100644 Clover/app/src/main/java/com/android/volley/compat/DelegateSSLSocket.java delete mode 100644 Clover/app/src/main/java/com/android/volley/compat/NoSSLv3Compat.java delete mode 100644 Clover/app/src/main/java/com/android/volley/toolbox/AndroidAuthenticator.java delete mode 100644 Clover/app/src/main/java/com/android/volley/toolbox/Authenticator.java delete mode 100644 Clover/app/src/main/java/com/android/volley/toolbox/BasicNetwork.java delete mode 100644 Clover/app/src/main/java/com/android/volley/toolbox/ByteArrayPool.java delete mode 100644 Clover/app/src/main/java/com/android/volley/toolbox/ClearCacheRequest.java delete mode 100644 Clover/app/src/main/java/com/android/volley/toolbox/DiskBasedCache.java delete mode 100644 Clover/app/src/main/java/com/android/volley/toolbox/HttpClientStack.java delete mode 100644 Clover/app/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java delete mode 100644 Clover/app/src/main/java/com/android/volley/toolbox/HttpStack.java delete mode 100644 Clover/app/src/main/java/com/android/volley/toolbox/HurlStack.java delete mode 100644 Clover/app/src/main/java/com/android/volley/toolbox/ImageLoader.java delete mode 100644 Clover/app/src/main/java/com/android/volley/toolbox/ImageRequest.java delete mode 100644 Clover/app/src/main/java/com/android/volley/toolbox/JsonArrayRequest.java delete mode 100644 Clover/app/src/main/java/com/android/volley/toolbox/JsonObjectRequest.java delete mode 100644 Clover/app/src/main/java/com/android/volley/toolbox/JsonRequest.java delete mode 100644 Clover/app/src/main/java/com/android/volley/toolbox/NetworkImageView.java delete mode 100644 Clover/app/src/main/java/com/android/volley/toolbox/NoCache.java delete mode 100644 Clover/app/src/main/java/com/android/volley/toolbox/PoolingByteArrayOutputStream.java delete mode 100644 Clover/app/src/main/java/com/android/volley/toolbox/RequestFuture.java delete mode 100644 Clover/app/src/main/java/com/android/volley/toolbox/StringRequest.java delete mode 100644 Clover/app/src/main/java/com/android/volley/toolbox/Volley.java diff --git a/Clover/app/build.gradle b/Clover/app/build.gradle index 1663472c..a6678e31 100644 --- a/Clover/app/build.gradle +++ b/Clover/app/build.gradle @@ -36,9 +36,6 @@ android { abortOnError false } - // Needed for volley - useLibrary 'org.apache.http.legacy' - /* If you want to sign releases, make a file in app/keys.properties with the following content: keystoreFile=yourkey.store @@ -137,11 +134,12 @@ dependencies { implementation "com.android.support:exifinterface:${supportVersion}" implementation "com.android.support:design:${supportVersion}" implementation "com.android.support:customtabs:${supportVersion}" - implementation 'com.android.support.constraint:constraint-layout:1.1.2' + implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'com.google.android.exoplayer:exoplayer-core:2.9.0' implementation 'com.google.android.exoplayer:exoplayer-ui:2.9.0' + implementation 'com.android.volley:volley:1.1.1' implementation 'com.squareup.okhttp3:okhttp:3.10.0' //noinspection GradleDependency implementation 'com.j256.ormlite:ormlite-core:4.48' diff --git a/Clover/app/src/main/java/com/android/volley/AuthFailureError.java b/Clover/app/src/main/java/com/android/volley/AuthFailureError.java deleted file mode 100644 index 7bb2e15f..00000000 --- a/Clover/app/src/main/java/com/android/volley/AuthFailureError.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2011 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 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. - */ -@SuppressWarnings("serial") -public class AuthFailureError extends VolleyError { - /** An intent that can be used to resolve this exception. (Brings up the password dialog.) */ - private Intent mResolutionIntent; - - public AuthFailureError() { } - - public AuthFailureError(Intent intent) { - mResolutionIntent = intent; - } - - public AuthFailureError(NetworkResponse response) { - super(response); - } - - public AuthFailureError(String message) { - super(message); - } - - public AuthFailureError(String message, Exception reason) { - super(message, reason); - } - - public Intent getResolutionIntent() { - return mResolutionIntent; - } - - @Override - public String getMessage() { - if (mResolutionIntent != null) { - return "User needs to (re)enter credentials."; - } - return super.getMessage(); - } -} diff --git a/Clover/app/src/main/java/com/android/volley/Cache.java b/Clover/app/src/main/java/com/android/volley/Cache.java deleted file mode 100644 index eafd1188..00000000 --- a/Clover/app/src/main/java/com/android/volley/Cache.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley; - -import java.util.Collections; -import java.util.Map; - -/** - * An interface for a cache keyed by a String with a byte array as data. - */ -public interface Cache { - /** - * Retrieves an entry from the cache. - * @param key Cache key - * @return An {@link Entry} or null in the event of a cache miss - */ - public Entry get(String key); - - /** - * Adds or replaces an entry to the cache. - * @param key Cache key - * @param entry Data to store and metadata for cache coherency, TTL, etc. - */ - public void put(String key, Entry entry); - - /** - * Performs any potentially long-running actions needed to initialize the cache; - * will be called from a worker thread. - */ - public void initialize(); - - /** - * Invalidates an entry in the cache. - * @param key Cache key - * @param fullExpire True to fully expire the entry, false to soft expire - */ - public void invalidate(String key, boolean fullExpire); - - /** - * Removes an entry from the cache. - * @param key Cache key - */ - public void remove(String key); - - /** - * Empties the cache. - */ - public void clear(); - - /** - * Data and metadata for an entry returned by the cache. - */ - public static class Entry { - /** The data returned from cache. */ - public byte[] data; - - /** ETag for cache coherency. */ - public String etag; - - /** Date of this response as reported by the server. */ - public long serverDate; - - /** TTL for this record. */ - public long ttl; - - /** Soft TTL for this record. */ - public long softTtl; - - /** Immutable response headers as received from server; must be non-null. */ - public Map responseHeaders = Collections.emptyMap(); - - /** True if the entry is expired. */ - public boolean isExpired() { - return this.ttl < System.currentTimeMillis(); - } - - /** True if a refresh is needed from the original data source. */ - public boolean refreshNeeded() { - return this.softTtl < System.currentTimeMillis(); - } - } - -} diff --git a/Clover/app/src/main/java/com/android/volley/CacheDispatcher.java b/Clover/app/src/main/java/com/android/volley/CacheDispatcher.java deleted file mode 100644 index 18d219b4..00000000 --- a/Clover/app/src/main/java/com/android/volley/CacheDispatcher.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley; - -import android.os.Process; - -import java.util.concurrent.BlockingQueue; - -/** - * Provides a thread for performing cache triage on a queue of requests. - * - * Requests added to the specified cache queue are resolved from cache. - * Any deliverable response is posted back to the caller via a - * {@link ResponseDelivery}. Cache misses and responses that require - * refresh are enqueued on the specified network queue for processing - * by a {@link NetworkDispatcher}. - */ -public class CacheDispatcher extends Thread { - - private static final boolean DEBUG = VolleyLog.DEBUG; - - /** The queue of requests coming in for triage. */ - private final BlockingQueue> mCacheQueue; - - /** The queue of requests going out to the network. */ - private final BlockingQueue> mNetworkQueue; - - /** The cache to read from. */ - private final Cache mCache; - - /** For posting responses. */ - private final ResponseDelivery mDelivery; - - /** Used for telling us to die. */ - private volatile boolean mQuit = false; - - /** - * Creates a new cache triage dispatcher thread. You must call {@link #start()} - * in order to begin processing. - * - * @param cacheQueue Queue of incoming requests for triage - * @param networkQueue Queue to post requests that require network to - * @param cache Cache interface to use for resolution - * @param delivery Delivery interface to use for posting responses - */ - public CacheDispatcher( - BlockingQueue> cacheQueue, BlockingQueue> networkQueue, - Cache cache, ResponseDelivery delivery) { - mCacheQueue = cacheQueue; - mNetworkQueue = networkQueue; - mCache = cache; - mDelivery = delivery; - } - - /** - * Forces this dispatcher to quit immediately. If any requests are still in - * the queue, they are not guaranteed to be processed. - */ - public void quit() { - mQuit = true; - interrupt(); - } - - @Override - public void run() { - if (DEBUG) VolleyLog.v("start new dispatcher"); - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - - // Make a blocking call to initialize the cache. - mCache.initialize(); - - while (true) { - try { - // Get a request from the cache triage queue, blocking until - // at least one is available. - final Request request = mCacheQueue.take(); - request.addMarker("cache-queue-take"); - - // If the request has been canceled, don't bother dispatching it. - if (request.isCanceled()) { - request.finish("cache-discard-canceled"); - continue; - } - - // Attempt to retrieve this item from cache. - Cache.Entry entry = mCache.get(request.getCacheKey()); - if (entry == null) { - request.addMarker("cache-miss"); - // Cache miss; send off to the network dispatcher. - mNetworkQueue.put(request); - continue; - } - - // If it is completely expired, just send it to the network. - if (entry.isExpired()) { - request.addMarker("cache-hit-expired"); - request.setCacheEntry(entry); - mNetworkQueue.put(request); - continue; - } - - // We have a cache hit; parse its data for delivery back to the request. - request.addMarker("cache-hit"); - Response response = request.parseNetworkResponse( - new NetworkResponse(entry.data, entry.responseHeaders)); - request.addMarker("cache-hit-parsed"); - - if (!entry.refreshNeeded()) { - // Completely unexpired cache hit. Just deliver the response. - mDelivery.postResponse(request, response); - } else { - // Soft-expired cache hit. We can deliver the cached response, - // but we need to also send the request to the network for - // refreshing. - request.addMarker("cache-hit-refresh-needed"); - request.setCacheEntry(entry); - - // Mark the response as intermediate. - response.intermediate = true; - - // Post the intermediate response back to the user and have - // the delivery then forward the request along to the network. - mDelivery.postResponse(request, response, new Runnable() { - @Override - public void run() { - try { - mNetworkQueue.put(request); - } catch (InterruptedException e) { - // Not much we can do about this. - } - } - }); - } - - } catch (InterruptedException e) { - // We may have been interrupted because it was time to quit. - if (mQuit) { - return; - } - continue; - } - } - } -} diff --git a/Clover/app/src/main/java/com/android/volley/DefaultRetryPolicy.java b/Clover/app/src/main/java/com/android/volley/DefaultRetryPolicy.java deleted file mode 100644 index ce4f9e0a..00000000 --- a/Clover/app/src/main/java/com/android/volley/DefaultRetryPolicy.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley; - -/** - * Default retry policy for requests. - */ -public class DefaultRetryPolicy implements RetryPolicy { - /** The current timeout in milliseconds. */ - private int mCurrentTimeoutMs; - - /** The current retry count. */ - private int mCurrentRetryCount; - - /** The maximum number of attempts. */ - private final int mMaxNumRetries; - - /** The backoff multiplier for for the policy. */ - private final float mBackoffMultiplier; - - /** The default socket timeout in milliseconds */ - public static final int DEFAULT_TIMEOUT_MS = 2500; - - /** The default number of retries */ - public static final int DEFAULT_MAX_RETRIES = 1; - - /** The default backoff multiplier */ - public static final float DEFAULT_BACKOFF_MULT = 1f; - - /** - * Constructs a new retry policy using the default timeouts. - */ - public DefaultRetryPolicy() { - this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT); - } - - /** - * Constructs a new retry policy. - * @param initialTimeoutMs The initial timeout for the policy. - * @param maxNumRetries The maximum number of retries. - * @param backoffMultiplier Backoff multiplier for the policy. - */ - public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) { - mCurrentTimeoutMs = initialTimeoutMs; - mMaxNumRetries = maxNumRetries; - mBackoffMultiplier = backoffMultiplier; - } - - /** - * Returns the current timeout. - */ - @Override - public int getCurrentTimeout() { - return mCurrentTimeoutMs; - } - - /** - * Returns the current retry count. - */ - @Override - public int getCurrentRetryCount() { - return mCurrentRetryCount; - } - - /** - * Prepares for the next retry by applying a backoff to the timeout. - * @param error The error code of the last attempt. - */ - @Override - public void retry(VolleyError error) throws VolleyError { - mCurrentRetryCount++; - mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier); - if (!hasAttemptRemaining()) { - throw error; - } - } - - /** - * Returns true if this policy has attempts remaining, false otherwise. - */ - protected boolean hasAttemptRemaining() { - return mCurrentRetryCount <= mMaxNumRetries; - } -} diff --git a/Clover/app/src/main/java/com/android/volley/ExecutorDelivery.java b/Clover/app/src/main/java/com/android/volley/ExecutorDelivery.java deleted file mode 100644 index 1babfcd1..00000000 --- a/Clover/app/src/main/java/com/android/volley/ExecutorDelivery.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley; - -import android.os.Handler; - -import java.util.concurrent.Executor; - -/** - * Delivers responses and errors. - */ -public class ExecutorDelivery implements ResponseDelivery { - /** Used for posting responses, typically to the main thread. */ - private final Executor mResponsePoster; - - /** - * Creates a new response delivery interface. - * @param handler {@link Handler} to post responses on - */ - public ExecutorDelivery(final Handler handler) { - // Make an Executor that just wraps the handler. - mResponsePoster = new Executor() { - @Override - public void execute(Runnable command) { - handler.post(command); - } - }; - } - - /** - * Creates a new response delivery interface, mockable version - * for testing. - * @param executor For running delivery tasks - */ - public ExecutorDelivery(Executor executor) { - mResponsePoster = executor; - } - - @Override - public void postResponse(Request request, Response response) { - postResponse(request, response, null); - } - - @Override - public void postResponse(Request request, Response response, Runnable runnable) { - request.markDelivered(); - request.addMarker("post-response"); - mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable)); - } - - @Override - public void postError(Request request, VolleyError error) { - request.addMarker("post-error"); - Response response = Response.error(error); - mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null)); - } - - /** - * A Runnable used for delivering network responses to a listener on the - * main thread. - */ - @SuppressWarnings("rawtypes") - private class ResponseDeliveryRunnable implements Runnable { - private final Request mRequest; - private final Response mResponse; - private final Runnable mRunnable; - - public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) { - mRequest = request; - mResponse = response; - mRunnable = runnable; - } - - @SuppressWarnings("unchecked") - @Override - public void run() { - // If this request has canceled, finish it and don't deliver. - if (mRequest.isCanceled()) { - mRequest.finish("canceled-at-delivery"); - return; - } - - // Deliver a normal response or error, depending. - if (mResponse.isSuccess()) { - mRequest.deliverResponse(mResponse.result); - } else { - mRequest.deliverError(mResponse.error); - } - - // If this is an intermediate response, add a marker, otherwise we're done - // and the request can be finished. - if (mResponse.intermediate) { - mRequest.addMarker("intermediate-response"); - } else { - mRequest.finish("done"); - } - - // If we have been provided a post-delivery runnable, run it. - if (mRunnable != null) { - mRunnable.run(); - } - } - } -} diff --git a/Clover/app/src/main/java/com/android/volley/Network.java b/Clover/app/src/main/java/com/android/volley/Network.java deleted file mode 100644 index ab45830f..00000000 --- a/Clover/app/src/main/java/com/android/volley/Network.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley; - -/** - * An interface for performing requests. - */ -public interface Network { - /** - * Performs the specified request. - * @param request Request to process - * @return A {@link NetworkResponse} with data and caching metadata; will never be null - * @throws VolleyError on errors - */ - public NetworkResponse performRequest(Request request) throws VolleyError; -} diff --git a/Clover/app/src/main/java/com/android/volley/NetworkDispatcher.java b/Clover/app/src/main/java/com/android/volley/NetworkDispatcher.java deleted file mode 100644 index 9c1c2e34..00000000 --- a/Clover/app/src/main/java/com/android/volley/NetworkDispatcher.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley; - -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. - * - * Requests added to the specified queue are processed from the network via a - * specified {@link Network} interface. Responses are committed to cache, if - * eligible, using a specified {@link Cache} interface. Valid responses and - * errors are posted back to the caller via a {@link ResponseDelivery}. - */ -public class NetworkDispatcher extends Thread { - /** The queue of requests to service. */ - private final BlockingQueue> mQueue; - /** The network interface for processing requests. */ - private final Network mNetwork; - /** The cache to write to. */ - private final Cache mCache; - /** For posting responses and errors. */ - private final ResponseDelivery mDelivery; - /** Used for telling us to die. */ - private volatile boolean mQuit = false; - - /** - * Creates a new network dispatcher thread. You must call {@link #start()} - * in order to begin processing. - * - * @param queue Queue of incoming requests for triage - * @param network Network interface to use for performing requests - * @param cache Cache interface to use for writing responses to cache - * @param delivery Delivery interface to use for posting responses - */ - public NetworkDispatcher(BlockingQueue> queue, - Network network, Cache cache, - ResponseDelivery delivery) { - mQueue = queue; - mNetwork = network; - mCache = cache; - mDelivery = delivery; - } - - /** - * Forces this dispatcher to quit immediately. If any requests are still in - * the queue, they are not guaranteed to be processed. - */ - public void quit() { - mQuit = true; - 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); - while (true) { - Request request; - try { - // Take a request from the queue. - request = mQueue.take(); - } catch (InterruptedException e) { - // We may have been interrupted because it was time to quit. - if (mQuit) { - return; - } - continue; - } - - try { - request.addMarker("network-queue-take"); - - // If the request was cancelled already, do not perform the - // network request. - if (request.isCanceled()) { - request.finish("network-discard-cancelled"); - continue; - } - - addTrafficStatsTag(request); - - // Perform the network request. - NetworkResponse networkResponse = mNetwork.performRequest(request); - request.addMarker("network-http-complete"); - - // If the server returned 304 AND we delivered a response already, - // we're done -- don't deliver a second identical response. - if (networkResponse.notModified && request.hasHadResponseDelivered()) { - request.finish("not-modified"); - continue; - } - - // Parse the response here on the worker thread. - Response response = request.parseNetworkResponse(networkResponse); - request.addMarker("network-parse-complete"); - - // Write to cache if applicable. - // TODO: Only update cache metadata instead of entire record for 304s. - if (request.shouldCache() && response.cacheEntry != null) { - mCache.put(request.getCacheKey(), response.cacheEntry); - request.addMarker("network-cache-written"); - } - - // Post the response back. - request.markDelivered(); - mDelivery.postResponse(request, response); - } catch (VolleyError volleyError) { - parseAndDeliverNetworkError(request, volleyError); - } catch (Exception e) { - VolleyLog.e(e, "Unhandled exception %s", e.toString()); - mDelivery.postError(request, new VolleyError(e)); - } - } - } - - private void parseAndDeliverNetworkError(Request request, VolleyError error) { - error = request.parseNetworkError(error); - mDelivery.postError(request, error); - } -} diff --git a/Clover/app/src/main/java/com/android/volley/NetworkError.java b/Clover/app/src/main/java/com/android/volley/NetworkError.java deleted file mode 100644 index 42fbcc26..00000000 --- a/Clover/app/src/main/java/com/android/volley/NetworkError.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2011 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 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. - */ -@SuppressWarnings("serial") -public class NetworkError extends VolleyError { - public NetworkError() { - super(); - } - - public NetworkError(Throwable cause) { - super(cause); - } - - public NetworkError(NetworkResponse networkResponse) { - super(networkResponse); - } -} diff --git a/Clover/app/src/main/java/com/android/volley/NetworkResponse.java b/Clover/app/src/main/java/com/android/volley/NetworkResponse.java deleted file mode 100644 index 6a0b5c2b..00000000 --- a/Clover/app/src/main/java/com/android/volley/NetworkResponse.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley; - -import org.apache.http.HttpStatus; - -import java.util.Collections; -import java.util.Map; - -/** - * Data and headers returned from {@link Network#performRequest(Request)}. - */ -public class NetworkResponse { - /** - * Creates a new network response. - * @param statusCode the HTTP status code - * @param data Response body - * @param headers Headers returned with this response, or null for none - * @param notModified True if the server returned a 304 and the data was already in cache - */ - public NetworkResponse(int statusCode, byte[] data, Map headers, - boolean notModified) { - this.statusCode = statusCode; - this.data = data; - this.headers = headers; - this.notModified = notModified; - } - - public NetworkResponse(byte[] data) { - this(HttpStatus.SC_OK, data, Collections.emptyMap(), false); - } - - public NetworkResponse(byte[] data, Map headers) { - this(HttpStatus.SC_OK, data, headers, false); - } - - /** The HTTP status code. */ - public final int statusCode; - - /** Raw data from this response. */ - public final byte[] data; - - /** Response headers. */ - public final Map headers; - - /** True if the server returned a 304 (Not Modified). */ - public final boolean notModified; -} \ No newline at end of file diff --git a/Clover/app/src/main/java/com/android/volley/NoConnectionError.java b/Clover/app/src/main/java/com/android/volley/NoConnectionError.java deleted file mode 100644 index fc231562..00000000 --- a/Clover/app/src/main/java/com/android/volley/NoConnectionError.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley; - -/** - * Error indicating that no connection could be established when performing a Volley request. - */ -@SuppressWarnings("serial") -public class NoConnectionError extends NetworkError { - public NoConnectionError() { - super(); - } - - public NoConnectionError(Throwable reason) { - super(reason); - } -} diff --git a/Clover/app/src/main/java/com/android/volley/ParseError.java b/Clover/app/src/main/java/com/android/volley/ParseError.java deleted file mode 100644 index a55da470..00000000 --- a/Clover/app/src/main/java/com/android/volley/ParseError.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley; - -import com.android.volley.NetworkResponse; -import com.android.volley.VolleyError; - -/** - * Indicates that the server's response could not be parsed. - */ -@SuppressWarnings("serial") -public class ParseError extends VolleyError { - public ParseError() { } - - public ParseError(NetworkResponse networkResponse) { - super(networkResponse); - } - - public ParseError(Throwable cause) { - super(cause); - } -} diff --git a/Clover/app/src/main/java/com/android/volley/Request.java b/Clover/app/src/main/java/com/android/volley/Request.java deleted file mode 100644 index 98329527..00000000 --- a/Clover/app/src/main/java/com/android/volley/Request.java +++ /dev/null @@ -1,603 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley; - -import android.net.TrafficStats; -import android.net.Uri; -import android.os.Handler; -import android.os.Looper; -import android.os.SystemClock; -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. - * - * @param The type of parsed response this request expects. - */ -public abstract class Request implements Comparable> { - - /** - * Default encoding for POST or PUT parameters. See {@link #getParamsEncoding()}. - */ - private static final String DEFAULT_PARAMS_ENCODING = "UTF-8"; - - /** - * Supported request methods. - */ - public interface Method { - int DEPRECATED_GET_OR_POST = -1; - int GET = 0; - 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, DELETE, HEAD, OPTIONS, - * TRACE, and PATCH. - */ - private final int mMethod; - - /** URL of this request. */ - private final String mUrl; - - /** Default tag for {@link TrafficStats}. */ - private final int mDefaultTrafficStatsTag; - - /** Listener interface for errors. */ - private final Response.ErrorListener mErrorListener; - - /** Sequence number of this request, used to enforce FIFO ordering. */ - private Integer mSequence; - - /** The request queue this request is associated with. */ - private RequestQueue mRequestQueue; - - /** Whether or not responses to this request should be cached. */ - private boolean mShouldCache = true; - - /** Whether or not this request has been canceled. */ - private boolean mCanceled = false; - - /** Whether or not a response has been delivered for this request yet. */ - private boolean mResponseDelivered = false; - - // A cheap variant of request tracing used to dump slow requests. - private long mRequestBirthTime = 0; - - /** Threshold at which we should log the request (even when debug logging is not enabled). */ - private static final long SLOW_REQUEST_THRESHOLD_MS = 3000; - - /** The retry policy for this request. */ - private RetryPolicy mRetryPolicy; - - /** - * When a request can be retrieved from cache but must be refreshed from - * the network, the cache entry will be stored here so that in the event of - * a "Not Modified" response, we can be sure it hasn't been evicted from cache. - */ - private Cache.Entry mCacheEntry = null; - - /** An opaque token tagging this request; used for bulk cancellation. */ - private Object mTag; - - /** - * Creates a new request with the given URL and error listener. Note that - * the normal response listener is not provided here as delivery of responses - * is provided by subclasses, who have a better idea of how to deliver an - * already-parsed response. - * - * @deprecated Use {@link #Request(int, String, com.android.volley.Response.ErrorListener)}. - */ - @Deprecated - public Request(String url, Response.ErrorListener listener) { - this(Method.DEPRECATED_GET_OR_POST, url, listener); - } - - /** - * Creates a new request with the given method (one of the values from {@link Method}), - * URL, and error listener. Note that the normal response listener is not provided here as - * delivery of responses is provided by subclasses, who have a better idea of how to deliver - * an already-parsed response. - */ - public Request(int method, String url, Response.ErrorListener listener) { - mMethod = method; - mUrl = url; - mErrorListener = listener; - setRetryPolicy(new DefaultRetryPolicy()); - - mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url); - } - - /** - * Return the method for this request. Can be one of the values in {@link Method}. - */ - public int getMethod() { - return mMethod; - } - - /** - * 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 Request setTag(Object tag) { - mTag = tag; - return this; - } - - /** - * Returns this request's tag. - * @see Request#setTag(Object) - */ - public Object getTag() { - return mTag; - } - - /** - * @return this request's {@link com.android.volley.Response.ErrorListener}. - */ - public Response.ErrorListener getErrorListener() { - return mErrorListener; - } - - /** - * @return A tag for use with {@link TrafficStats#setThreadStatsTag(int)} - */ - public int getTrafficStatsTag() { - 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 Request setRetryPolicy(RetryPolicy retryPolicy) { - mRetryPolicy = retryPolicy; - return this; - } - - /** - * Adds an event to this request's event log; for debugging. - */ - public void addMarker(String tag) { - if (MarkerLog.ENABLED) { - mEventLog.add(tag, Thread.currentThread().getId()); - } else if (mRequestBirthTime == 0) { - mRequestBirthTime = SystemClock.elapsedRealtime(); - } - } - - /** - * Notifies the request queue that this request has finished (successfully or with error). - * - *

Also dumps all events from this request's event log; for debugging.

- */ - void finish(final String tag) { - if (mRequestQueue != null) { - mRequestQueue.finish(this); - } - if (MarkerLog.ENABLED) { - final long threadId = Thread.currentThread().getId(); - if (Looper.myLooper() != Looper.getMainLooper()) { - // If we finish marking off of the main thread, we need to - // actually do it on the main thread to ensure correct ordering. - Handler mainThread = new Handler(Looper.getMainLooper()); - mainThread.post(new Runnable() { - @Override - public void run() { - mEventLog.add(tag, threadId); - mEventLog.finish(this.toString()); - } - }); - return; - } - - mEventLog.add(tag, threadId); - mEventLog.finish(this.toString()); - } else { - long requestTime = SystemClock.elapsedRealtime() - mRequestBirthTime; - if (requestTime >= SLOW_REQUEST_THRESHOLD_MS) { - VolleyLog.d("%d ms: %s", requestTime, this.toString()); - } - } - } - - /** - * 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 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 Request setSequence(int sequence) { - mSequence = sequence; - return this; - } - - /** - * Returns the sequence number of this request. - */ - public final int getSequence() { - if (mSequence == null) { - throw new IllegalStateException("getSequence called before setSequence"); - } - return mSequence; - } - - /** - * Returns the URL of this request. - */ - public String getUrl() { - return mUrl; - } - - /** - * Returns the cache key for this request. By default, this is the URL. - */ - public String getCacheKey() { - return getUrl(); - } - - /** - * 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 Request setCacheEntry(Cache.Entry entry) { - mCacheEntry = entry; - return this; - } - - /** - * Returns the annotated cache entry, or null if there isn't one. - */ - public Cache.Entry getCacheEntry() { - return mCacheEntry; - } - - /** - * Mark this request as canceled. No callback will be delivered. - */ - public void cancel() { - mCanceled = true; - } - - /** - * Returns true if this request has been canceled. - */ - public boolean isCanceled() { - return mCanceled; - } - - /** - * Returns a list of extra HTTP headers to go along with this request. Can - * throw {@link AuthFailureError} as authentication may be required to - * provide these values. - * @throws AuthFailureError In the event of auth failure - */ - public Map getHeaders() throws AuthFailureError { - return Collections.emptyMap(); - } - - /** - * Returns a Map of POST parameters to be used for this request, or null if - * a simple GET should be used. Can throw {@link AuthFailureError} as - * authentication may be required to provide these values. - * - *

Note that only one of getPostParams() and getPostBody() can return a non-null - * value.

- * @throws AuthFailureError In the event of auth failure - * - * @deprecated Use {@link #getParams()} instead. - */ - @Deprecated - protected Map getPostParams() throws AuthFailureError { - return getParams(); - } - - /** - * Returns which encoding should be used when converting POST parameters returned by - * {@link #getPostParams()} into a raw POST body. - * - *

This controls both encodings: - *

    - *
  1. The string encoding used when converting parameter names and values into bytes prior - * to URL encoding them.
  2. - *
  3. The string encoding used when converting the URL encoded parameters into a raw - * byte array.
  4. - *
- * - * @deprecated Use {@link #getParamsEncoding()} instead. - */ - @Deprecated - protected String getPostParamsEncoding() { - return getParamsEncoding(); - } - - /** - * @deprecated Use {@link #getBodyContentType()} instead. - */ - @Deprecated - public String getPostBodyContentType() { - return getBodyContentType(); - } - - /** - * Returns the raw POST body to be sent. - * - * @throws AuthFailureError In the event of auth failure - * - * @deprecated Use {@link #getBody()} instead. - */ - @Deprecated - public byte[] getPostBody() throws AuthFailureError { - // Note: For compatibility with legacy clients of volley, this implementation must remain - // here instead of simply calling the getBody() function because this function must - // call getPostParams() and getPostParamsEncoding() since legacy clients would have - // overridden these two member functions for POST requests. - Map postParams = getPostParams(); - if (postParams != null && postParams.size() > 0) { - return encodeParameters(postParams, getPostParamsEncoding()); - } - return null; - } - - /** - * Returns a Map of parameters to be used for a POST or PUT request. Can throw - * {@link AuthFailureError} as authentication may be required to provide these values. - * - *

Note that you can directly override {@link #getBody()} for custom data.

- * - * @throws AuthFailureError in the event of auth failure - */ - protected Map getParams() throws AuthFailureError { - return null; - } - - /** - * Returns which encoding should be used when converting POST or PUT parameters returned by - * {@link #getParams()} into a raw POST or PUT body. - * - *

This controls both encodings: - *

    - *
  1. The string encoding used when converting parameter names and values into bytes prior - * to URL encoding them.
  2. - *
  3. The string encoding used when converting the URL encoded parameters into a raw - * byte array.
  4. - *
- */ - protected String getParamsEncoding() { - return DEFAULT_PARAMS_ENCODING; - } - - /** - * Returns the content type of the POST or PUT body. - */ - public String getBodyContentType() { - return "application/x-www-form-urlencoded; charset=" + getParamsEncoding(); - } - - /** - * Returns the raw POST or PUT body to be sent. - * - *

By default, the body consists of the request parameters in - * application/x-www-form-urlencoded format. When overriding this method, consider overriding - * {@link #getBodyContentType()} as well to match the new body format. - * - * @throws AuthFailureError in the event of auth failure - */ - public byte[] getBody() throws AuthFailureError { - Map params = getParams(); - if (params != null && params.size() > 0) { - return encodeParameters(params, getParamsEncoding()); - } - return null; - } - - /** - * Converts params into an application/x-www-form-urlencoded encoded string. - */ - private byte[] encodeParameters(Map params, String paramsEncoding) { - StringBuilder encodedParams = new StringBuilder(); - try { - for (Map.Entry entry : params.entrySet()) { - encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding)); - encodedParams.append('='); - encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding)); - encodedParams.append('&'); - } - return encodedParams.toString().getBytes(paramsEncoding); - } catch (UnsupportedEncodingException uee) { - throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee); - } - } - - /** - * Set whether or not responses to this request should be cached. - * - * @return This Request object to allow for chaining. - */ - public final Request setShouldCache(boolean shouldCache) { - mShouldCache = shouldCache; - return this; - } - - /** - * Returns true if responses to this request should be cached. - */ - public final boolean shouldCache() { - return mShouldCache; - } - - /** - * Priority values. Requests will be processed from higher priorities to - * lower priorities, in FIFO order. - */ - public enum Priority { - LOW, - NORMAL, - HIGH, - IMMEDIATE - } - - /** - * Returns the {@link Priority} of this request; {@link Priority#NORMAL} by default. - */ - public Priority getPriority() { - return Priority.NORMAL; - } - - /** - * Returns the socket timeout in milliseconds per retry attempt. (This value can be changed - * per retry attempt if a backoff is specified via backoffTimeout()). If there are no retry - * attempts remaining, this will cause delivery of a {@link TimeoutError} error. - */ - public final int getTimeoutMs() { - return mRetryPolicy.getCurrentTimeout(); - } - - /** - * Returns the retry policy that should be used for this request. - */ - public RetryPolicy getRetryPolicy() { - return mRetryPolicy; - } - - /** - * Mark this request as having a response delivered on it. This can be used - * later in the request's lifetime for suppressing identical responses. - */ - public void markDelivered() { - mResponseDelivered = true; - } - - /** - * Returns true if this request has had a response delivered for it. - */ - public boolean hasHadResponseDelivered() { - return mResponseDelivered; - } - - /** - * Subclasses must implement this to parse the raw network response - * and return an appropriate response type. This method will be - * called from a worker thread. The response will not be delivered - * if you return null. - * @param response Response from the network - * @return The parsed response, or null in the case of an error - */ - abstract protected Response parseNetworkResponse(NetworkResponse response); - - /** - * Subclasses can override this method to parse 'networkError' and return a more specific error. - * - *

The default implementation just returns the passed 'networkError'.

- * - * @param volleyError the error retrieved from the network - * @return an NetworkError augmented with additional information - */ - protected VolleyError parseNetworkError(VolleyError volleyError) { - return volleyError; - } - - /** - * Subclasses must implement this to perform delivery of the parsed - * response to their listeners. The given response is guaranteed to - * be non-null; responses that fail to parse are not delivered. - * @param response The parsed response returned by - * {@link #parseNetworkResponse(NetworkResponse)} - */ - abstract protected void deliverResponse(T response); - - /** - * Delivers error message to the ErrorListener that the Request was - * initialized with. - * - * @param error Error details - */ - public void deliverError(VolleyError error) { - if (mErrorListener != null) { - mErrorListener.onErrorResponse(error); - } - } - - /** - * Our comparator sorts from high to low priority, and secondarily by - * sequence number to provide FIFO ordering. - */ - @Override - public int compareTo(Request other) { - Priority left = this.getPriority(); - Priority right = other.getPriority(); - - // High-priority requests are "lesser" so they are sorted to the front. - // Equal priorities are sorted by sequence number to provide FIFO ordering. - return left == right ? - this.mSequence - other.mSequence : - right.ordinal() - left.ordinal(); - } - - @Override - public String toString() { - String trafficStatsTag = "0x" + Integer.toHexString(getTrafficStatsTag()); - return (mCanceled ? "[X] " : "[ ] ") + getUrl() + " " + trafficStatsTag + " " - + getPriority() + " " + mSequence; - } -} diff --git a/Clover/app/src/main/java/com/android/volley/RequestQueue.java b/Clover/app/src/main/java/com/android/volley/RequestQueue.java deleted file mode 100644 index 5c0e7afb..00000000 --- a/Clover/app/src/main/java/com/android/volley/RequestQueue.java +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley; - -import android.os.Handler; -import android.os.Looper; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.Map; -import java.util.Queue; -import java.util.Set; -import java.util.concurrent.PriorityBlockingQueue; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * A request dispatch queue with a thread pool of dispatchers. - * - * Calling {@link #add(Request)} will enqueue the given Request for dispatch, - * resolving from either cache or network on a worker thread, and then delivering - * a parsed response on the main thread. - */ -public class RequestQueue { - - /** Used for generating monotonically-increasing sequence numbers for requests. */ - private AtomicInteger mSequenceGenerator = new AtomicInteger(); - - /** - * Staging area for requests that already have a duplicate request in flight. - * - *
    - *
  • containsKey(cacheKey) indicates that there is a request in flight for the given cache - * key.
  • - *
  • get(cacheKey) returns waiting requests for the given cache key. The in flight request - * is not contained in that list. Is null if no requests are staged.
  • - *
- */ - 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>(); - - /** The cache triage queue. */ - private final PriorityBlockingQueue> mCacheQueue = - new PriorityBlockingQueue>(); - - /** The queue of requests that are actually going out to the network. */ - 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 responses. */ - private final Cache mCache; - - /** Network interface for performing requests. */ - private final Network mNetwork; - - /** Response delivery mechanism. */ - private final ResponseDelivery mDelivery; - - /** The network dispatchers. */ - private NetworkDispatcher[] mDispatchers; - - /** The cache dispatcher. */ - private CacheDispatcher mCacheDispatcher; - - /** - * Creates the worker pool. Processing will not begin until {@link #start()} is called. - * - * @param cache A Cache to use for persisting responses to disk - * @param network A Network interface for performing HTTP requests - * @param threadPoolSize Number of network dispatcher threads to create - * @param delivery A ResponseDelivery interface for posting responses and errors - */ - public RequestQueue(Cache cache, Network network, int threadPoolSize, - ResponseDelivery delivery) { - mCache = cache; - mNetwork = network; - mDispatchers = new NetworkDispatcher[threadPoolSize]; - mDelivery = delivery; - } - - /** - * Creates the worker pool. Processing will not begin until {@link #start()} is called. - * - * @param cache A Cache to use for persisting responses to disk - * @param network A Network interface for performing HTTP requests - * @param threadPoolSize Number of network dispatcher threads to create - */ - public RequestQueue(Cache cache, Network network, int threadPoolSize) { - this(cache, network, threadPoolSize, - new ExecutorDelivery(new Handler(Looper.getMainLooper()))); - } - - /** - * Creates the worker pool. Processing will not begin until {@link #start()} is called. - * - * @param cache A Cache to use for persisting responses to disk - * @param network A Network interface for performing HTTP requests - */ - public RequestQueue(Cache cache, Network network) { - this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE); - } - - /** - * Starts the dispatchers in this queue. - */ - public void start() { - stop(); // Make sure any currently running dispatchers are stopped. - // Create the cache dispatcher and start it. - mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery); - mCacheDispatcher.start(); - - // Create network dispatchers (and corresponding threads) up to the pool size. - for (int i = 0; i < mDispatchers.length; i++) { - NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork, - mCache, mDelivery); - mDispatchers[i] = networkDispatcher; - networkDispatcher.start(); - } - } - - /** - * Stops the cache and network dispatchers. - */ - public void stop() { - if (mCacheDispatcher != null) { - mCacheDispatcher.quit(); - } - for (int i = 0; i < mDispatchers.length; i++) { - if (mDispatchers[i] != null) { - mDispatchers[i].quit(); - } - } - } - - /** - * Gets a sequence number. - */ - public int getSequenceNumber() { - return mSequenceGenerator.incrementAndGet(); - } - - /** - * Gets the {@link Cache} instance being used. - */ - public Cache getCache() { - return mCache; - } - - /** - * A simple predicate or filter interface for Requests, for use by - * {@link RequestQueue#cancelAll(RequestFilter)}. - */ - public interface RequestFilter { - public boolean apply(Request request); - } - - /** - * Cancels all requests in this queue for which the given filter applies. - * @param filter The filtering function to use - */ - public void cancelAll(RequestFilter filter) { - synchronized (mCurrentRequests) { - for (Request request : mCurrentRequests) { - if (filter.apply(request)) { - request.cancel(); - } - } - } - } - - /** - * Cancels all requests in this queue with the given tag. Tag must be non-null - * and equality is by identity. - */ - public void cancelAll(final Object tag) { - if (tag == null) { - throw new IllegalArgumentException("Cannot cancelAll with a null tag"); - } - cancelAll(new RequestFilter() { - @Override - public boolean apply(Request request) { - return request.getTag() == tag; - } - }); - } - - /** - * Adds a Request to the dispatch queue. - * @param request The request to service - * @return The passed-in 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) { - mCurrentRequests.add(request); - } - - // Process requests in the order they are added. - request.setSequence(getSequenceNumber()); - request.addMarker("add-to-queue"); - - // If the request is uncacheable, skip the cache queue and go straight to the network. - if (!request.shouldCache()) { - mNetworkQueue.add(request); - return request; - } - - // Insert request into stage if there's already a request with the same cache key in flight. - synchronized (mWaitingRequests) { - String cacheKey = request.getCacheKey(); - if (mWaitingRequests.containsKey(cacheKey)) { - // There is already a request in flight. Queue up. - Queue> stagedRequests = mWaitingRequests.get(cacheKey); - if (stagedRequests == null) { - stagedRequests = new LinkedList>(); - } - stagedRequests.add(request); - mWaitingRequests.put(cacheKey, stagedRequests); - if (VolleyLog.DEBUG) { - VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey); - } - } else { - // Insert 'null' queue for this cacheKey, indicating there is now a request in - // flight. - mWaitingRequests.put(cacheKey, null); - mCacheQueue.add(request); - } - return request; - } - } - - /** - * Called from {@link Request#finish(String)}, indicating that processing of the given request - * has finished. - * - *

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

- */ - void finish(Request request) { - // Remove from the set of requests currently being processed. - synchronized (mCurrentRequests) { - mCurrentRequests.remove(request); - } - - if (request.shouldCache()) { - synchronized (mWaitingRequests) { - String cacheKey = request.getCacheKey(); - Queue> waitingRequests = mWaitingRequests.remove(cacheKey); - if (waitingRequests != null) { - if (VolleyLog.DEBUG) { - VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.", - waitingRequests.size(), cacheKey); - } - // Process all queued up requests. They won't be considered as in flight, but - // that's not a problem as the cache has been primed by 'request'. - mCacheQueue.addAll(waitingRequests); - } - } - } - } -} diff --git a/Clover/app/src/main/java/com/android/volley/Response.java b/Clover/app/src/main/java/com/android/volley/Response.java deleted file mode 100644 index 1165595d..00000000 --- a/Clover/app/src/main/java/com/android/volley/Response.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley; - -/** - * Encapsulates a parsed response for delivery. - * - * @param Parsed type of this response - */ -public class Response { - - /** Callback interface for delivering parsed responses. */ - public interface Listener { - /** Called when a response is received. */ - public void onResponse(T response); - } - - /** Callback interface for delivering error responses. */ - public interface ErrorListener { - /** - * Callback method that an error has been occurred with the - * provided error code and optional user-readable message. - */ - public void onErrorResponse(VolleyError error); - } - - /** Returns a successful response containing the parsed result. */ - public static Response success(T result, Cache.Entry cacheEntry) { - return new Response(result, cacheEntry); - } - - /** - * Returns a failed response containing the given error code and an optional - * localized message displayed to the user. - */ - public static Response error(VolleyError error) { - return new Response(error); - } - - /** Parsed response, or null in the case of error. */ - public final T result; - - /** Cache metadata for this response, or null in the case of error. */ - public final Cache.Entry cacheEntry; - - /** Detailed error information if errorCode != OK. */ - public final VolleyError error; - - /** True if this response was a soft-expired one and a second one MAY be coming. */ - public boolean intermediate = false; - - /** - * Returns whether this response is considered successful. - */ - public boolean isSuccess() { - return error == null; - } - - - private Response(T result, Cache.Entry cacheEntry) { - this.result = result; - this.cacheEntry = cacheEntry; - this.error = null; - } - - private Response(VolleyError error) { - this.result = null; - this.cacheEntry = null; - this.error = error; - } -} diff --git a/Clover/app/src/main/java/com/android/volley/ResponseDelivery.java b/Clover/app/src/main/java/com/android/volley/ResponseDelivery.java deleted file mode 100644 index 87706afc..00000000 --- a/Clover/app/src/main/java/com/android/volley/ResponseDelivery.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley; - -public interface ResponseDelivery { - /** - * Parses a response from the network or cache and delivers it. - */ - public void postResponse(Request request, Response response); - - /** - * Parses a response from the network or cache and delivers it. The provided - * Runnable will be executed after delivery. - */ - public void postResponse(Request request, Response response, Runnable runnable); - - /** - * Posts an error for the given request. - */ - public void postError(Request request, VolleyError error); -} diff --git a/Clover/app/src/main/java/com/android/volley/RetryPolicy.java b/Clover/app/src/main/java/com/android/volley/RetryPolicy.java deleted file mode 100644 index 0dd198b2..00000000 --- a/Clover/app/src/main/java/com/android/volley/RetryPolicy.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley; - -/** - * Retry policy for a request. - */ -public interface RetryPolicy { - - /** - * Returns the current timeout (used for logging). - */ - public int getCurrentTimeout(); - - /** - * Returns the current retry count (used for logging). - */ - public int getCurrentRetryCount(); - - /** - * Prepares for the next retry by applying a backoff to the timeout. - * @param error The error code of the last attempt. - * @throws VolleyError In the event that the retry could not be performed (for example if we - * ran out of attempts), the passed in error is thrown. - */ - public void retry(VolleyError error) throws VolleyError; -} diff --git a/Clover/app/src/main/java/com/android/volley/ServerError.java b/Clover/app/src/main/java/com/android/volley/ServerError.java deleted file mode 100644 index b29a6c66..00000000 --- a/Clover/app/src/main/java/com/android/volley/ServerError.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley; - -import com.android.volley.NetworkResponse; -import com.android.volley.VolleyError; - -/** - * Indicates that the error responded with an error response. - */ -@SuppressWarnings("serial") -public class ServerError extends VolleyError { - public ServerError(NetworkResponse networkResponse) { - super(networkResponse); - } - - public ServerError() { - super(); - } -} diff --git a/Clover/app/src/main/java/com/android/volley/TimeoutError.java b/Clover/app/src/main/java/com/android/volley/TimeoutError.java deleted file mode 100644 index 0b5d6acb..00000000 --- a/Clover/app/src/main/java/com/android/volley/TimeoutError.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley; - -/** - * Indicates that the connection or the socket timed out. - */ -@SuppressWarnings("serial") -public class TimeoutError extends VolleyError { } diff --git a/Clover/app/src/main/java/com/android/volley/VolleyError.java b/Clover/app/src/main/java/com/android/volley/VolleyError.java deleted file mode 100644 index 4f7b883d..00000000 --- a/Clover/app/src/main/java/com/android/volley/VolleyError.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley; - -/** - * Exception style class encapsulating Volley errors - */ -@SuppressWarnings("serial") -public class VolleyError extends Exception { - public final NetworkResponse networkResponse; - - public VolleyError() { - networkResponse = null; - } - - public VolleyError(NetworkResponse response) { - networkResponse = response; - } - - public VolleyError(String exceptionMessage) { - super(exceptionMessage); - networkResponse = null; - } - - public VolleyError(String exceptionMessage, Throwable reason) { - super(exceptionMessage, reason); - networkResponse = null; - } - - public VolleyError(Throwable cause) { - super(cause); - networkResponse = null; - } -} diff --git a/Clover/app/src/main/java/com/android/volley/VolleyLog.java b/Clover/app/src/main/java/com/android/volley/VolleyLog.java deleted file mode 100644 index ca1f9ee4..00000000 --- a/Clover/app/src/main/java/com/android/volley/VolleyLog.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley; - -import android.os.SystemClock; -import android.util.Log; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -/** Logging helper class. */ -public class VolleyLog { - public static String TAG = "Volley"; - - public static boolean DEBUG = Log.isLoggable(TAG, Log.VERBOSE); - - /** - * Customize the log tag for your application, so that other apps - * using Volley don't mix their logs with yours. - *
- * Enable the log property for your tag before starting your app: - *
- * {@code adb shell setprop log.tag.<tag>} - */ - public static void setTag(String tag) { - d("Changing log tag to %s", tag); - TAG = tag; - - // Reinitialize the DEBUG "constant" - DEBUG = Log.isLoggable(TAG, Log.VERBOSE); - } - - public static void v(String format, Object... args) { - if (DEBUG) { - Log.v(TAG, buildMessage(format, args)); - } - } - - public static void d(String format, Object... args) { - if (DEBUG) { - Log.d(TAG, buildMessage(format, args)); - } - } - - public static void e(String format, Object... args) { - Log.e(TAG, buildMessage(format, args)); - } - - public static void e(Throwable tr, String format, Object... args) { - Log.e(TAG, buildMessage(format, args), tr); - } - - public static void wtf(String format, Object... args) { - Log.wtf(TAG, buildMessage(format, args)); - } - - public static void wtf(Throwable tr, String format, Object... args) { - Log.wtf(TAG, buildMessage(format, args), tr); - } - - /** - * Formats the caller's provided message and prepends useful info like - * calling thread ID and method name. - */ - private static String buildMessage(String format, Object... args) { - String msg = (args == null) ? format : String.format(Locale.US, format, args); - StackTraceElement[] trace = new Throwable().fillInStackTrace().getStackTrace(); - - String caller = ""; - // Walk up the stack looking for the first caller outside of VolleyLog. - // It will be at least two frames up, so start there. - for (int i = 2; i < trace.length; i++) { - Class clazz = trace[i].getClass(); - if (!clazz.equals(VolleyLog.class)) { - String callingClass = trace[i].getClassName(); - callingClass = callingClass.substring(callingClass.lastIndexOf('.') + 1); - callingClass = callingClass.substring(callingClass.lastIndexOf('$') + 1); - - caller = callingClass + "." + trace[i].getMethodName(); - break; - } - } - return String.format(Locale.US, "[%d] %s: %s", - Thread.currentThread().getId(), caller, msg); - } - - /** - * A simple event log with records containing a name, thread ID, and timestamp. - */ - static class MarkerLog { - public static final boolean ENABLED = VolleyLog.DEBUG; - - /** Minimum duration from first marker to last in an marker log to warrant logging. */ - private static final long MIN_DURATION_FOR_LOGGING_MS = 0; - - private static class Marker { - public final String name; - public final long thread; - public final long time; - - public Marker(String name, long thread, long time) { - this.name = name; - this.thread = thread; - this.time = time; - } - } - - private final List mMarkers = new ArrayList(); - private boolean mFinished = false; - - /** Adds a marker to this log with the specified name. */ - public synchronized void add(String name, long threadId) { - if (mFinished) { - throw new IllegalStateException("Marker added to finished log"); - } - - mMarkers.add(new Marker(name, threadId, SystemClock.elapsedRealtime())); - } - - /** - * Closes the log, dumping it to logcat if the time difference between - * the first and last markers is greater than {@link #MIN_DURATION_FOR_LOGGING_MS}. - * @param header Header string to print above the marker log. - */ - public synchronized void finish(String header) { - mFinished = true; - - long duration = getTotalDuration(); - if (duration <= MIN_DURATION_FOR_LOGGING_MS) { - return; - } - - long prevTime = mMarkers.get(0).time; - d("(%-4d ms) %s", duration, header); - for (Marker marker : mMarkers) { - long thisTime = marker.time; - d("(+%-4d) [%2d] %s", (thisTime - prevTime), marker.thread, marker.name); - prevTime = thisTime; - } - } - - @Override - protected void finalize() throws Throwable { - // Catch requests that have been collected (and hence end-of-lifed) - // but had no debugging output printed for them. - if (!mFinished) { - finish("Request on the loose"); - e("Marker log finalized without finish() - uncaught exit point for request"); - } - super.finalize(); - } - - /** Returns the time difference between the first and last events in this log. */ - private long getTotalDuration() { - if (mMarkers.size() == 0) { - return 0; - } - - long first = mMarkers.get(0).time; - long last = mMarkers.get(mMarkers.size() - 1).time; - return last - first; - } - } -} diff --git a/Clover/app/src/main/java/com/android/volley/compat/DelegateSSLSocket.java b/Clover/app/src/main/java/com/android/volley/compat/DelegateSSLSocket.java deleted file mode 100644 index 728934d8..00000000 --- a/Clover/app/src/main/java/com/android/volley/compat/DelegateSSLSocket.java +++ /dev/null @@ -1,331 +0,0 @@ -package com.android.volley.compat; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.InetAddress; -import java.net.SocketAddress; -import java.net.SocketException; -import java.nio.channels.SocketChannel; - -import javax.net.ssl.HandshakeCompletedListener; -import javax.net.ssl.SSLParameters; -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSocket; - -/** - * Created by robUx4 on 25/10/2014. - */ -public class DelegateSSLSocket extends SSLSocket { - - protected final SSLSocket delegate; - - DelegateSSLSocket(SSLSocket delegate) { - this.delegate = delegate; - } - - @Override - public String[] getSupportedCipherSuites() { - return delegate.getSupportedCipherSuites(); - } - - @Override - public String[] getEnabledCipherSuites() { - return delegate.getEnabledCipherSuites(); - } - - @Override - public void setEnabledCipherSuites(String[] suites) { - delegate.setEnabledCipherSuites(suites); - } - - @Override - public String[] getSupportedProtocols() { - return delegate.getSupportedProtocols(); - } - - @Override - public String[] getEnabledProtocols() { - return delegate.getEnabledProtocols(); - } - - @Override - public void setEnabledProtocols(String[] protocols) { - delegate.setEnabledProtocols(protocols); - } - - @Override - public SSLSession getSession() { - return delegate.getSession(); - } - - @Override - public void addHandshakeCompletedListener(HandshakeCompletedListener listener) { - delegate.addHandshakeCompletedListener(listener); - } - - @Override - public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) { - delegate.removeHandshakeCompletedListener(listener); - } - - @Override - public void startHandshake() throws IOException { - delegate.startHandshake(); - } - - @Override - public void setUseClientMode(boolean mode) { - delegate.setUseClientMode(mode); - } - - @Override - public boolean getUseClientMode() { - return delegate.getUseClientMode(); - } - - @Override - public void setNeedClientAuth(boolean need) { - delegate.setNeedClientAuth(need); - } - - @Override - public void setWantClientAuth(boolean want) { - delegate.setWantClientAuth(want); - } - - @Override - public boolean getNeedClientAuth() { - return delegate.getNeedClientAuth(); - } - - @Override - public boolean getWantClientAuth() { - return delegate.getWantClientAuth(); - } - - @Override - public void setEnableSessionCreation(boolean flag) { - delegate.setEnableSessionCreation(flag); - } - - @Override - public boolean getEnableSessionCreation() { - return delegate.getEnableSessionCreation(); - } - - @Override - public void bind(SocketAddress localAddr) throws IOException { - delegate.bind(localAddr); - } - - @Override - public synchronized void close() throws IOException { - delegate.close(); - } - - @Override - public void connect(SocketAddress remoteAddr) throws IOException { - delegate.connect(remoteAddr); - } - - @Override - public void connect(SocketAddress remoteAddr, int timeout) throws IOException { - delegate.connect(remoteAddr, timeout); - } - - @Override - public SocketChannel getChannel() { - return delegate.getChannel(); - } - - @Override - public InetAddress getInetAddress() { - return delegate.getInetAddress(); - } - - @Override - public InputStream getInputStream() throws IOException { - return delegate.getInputStream(); - } - - @Override - public boolean getKeepAlive() throws SocketException { - return delegate.getKeepAlive(); - } - - @Override - public InetAddress getLocalAddress() { - return delegate.getLocalAddress(); - } - - @Override - public int getLocalPort() { - return delegate.getLocalPort(); - } - - @Override - public SocketAddress getLocalSocketAddress() { - return delegate.getLocalSocketAddress(); - } - - @Override - public boolean getOOBInline() throws SocketException { - return delegate.getOOBInline(); - } - - @Override - public OutputStream getOutputStream() throws IOException { - return delegate.getOutputStream(); - } - - @Override - public int getPort() { - return delegate.getPort(); - } - - @Override - public synchronized int getReceiveBufferSize() throws SocketException { - return delegate.getReceiveBufferSize(); - } - - @Override - public SocketAddress getRemoteSocketAddress() { - return delegate.getRemoteSocketAddress(); - } - - @Override - public boolean getReuseAddress() throws SocketException { - return delegate.getReuseAddress(); - } - - @Override - public synchronized int getSendBufferSize() throws SocketException { - return delegate.getSendBufferSize(); - } - - @Override - public int getSoLinger() throws SocketException { - return delegate.getSoLinger(); - } - - @Override - public synchronized int getSoTimeout() throws SocketException { - return delegate.getSoTimeout(); - } - - @Override - public boolean getTcpNoDelay() throws SocketException { - return delegate.getTcpNoDelay(); - } - - @Override - public int getTrafficClass() throws SocketException { - return delegate.getTrafficClass(); - } - - @Override - public boolean isBound() { - return delegate.isBound(); - } - - @Override - public boolean isClosed() { - return delegate.isClosed(); - } - - @Override - public boolean isConnected() { - return delegate.isConnected(); - } - - @Override - public boolean isInputShutdown() { - return delegate.isInputShutdown(); - } - - @Override - public boolean isOutputShutdown() { - return delegate.isOutputShutdown(); - } - - @Override - public void sendUrgentData(int value) throws IOException { - delegate.sendUrgentData(value); - } - - @Override - public void setKeepAlive(boolean keepAlive) throws SocketException { - delegate.setKeepAlive(keepAlive); - } - - @Override - public void setOOBInline(boolean oobinline) throws SocketException { - delegate.setOOBInline(oobinline); - } - - @Override - public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { - delegate.setPerformancePreferences(connectionTime, latency, bandwidth); - } - - @Override - public synchronized void setReceiveBufferSize(int size) throws SocketException { - delegate.setReceiveBufferSize(size); - } - - @Override - public void setReuseAddress(boolean reuse) throws SocketException { - delegate.setReuseAddress(reuse); - } - - @Override - public synchronized void setSendBufferSize(int size) throws SocketException { - delegate.setSendBufferSize(size); - } - - @Override - public void setSoLinger(boolean on, int timeout) throws SocketException { - delegate.setSoLinger(on, timeout); - } - - @Override - public synchronized void setSoTimeout(int timeout) throws SocketException { - delegate.setSoTimeout(timeout); - } - - @Override - public void setSSLParameters(SSLParameters p) { - delegate.setSSLParameters(p); - } - - @Override - public void setTcpNoDelay(boolean on) throws SocketException { - delegate.setTcpNoDelay(on); - } - - @Override - public void setTrafficClass(int value) throws SocketException { - delegate.setTrafficClass(value); - } - - @Override - public void shutdownInput() throws IOException { - delegate.shutdownInput(); - } - - @Override - public void shutdownOutput() throws IOException { - delegate.shutdownOutput(); - } - - @Override - public String toString() { - return delegate.toString(); - } - - @Override - public boolean equals(Object o) { - return delegate.equals(o); - } -} diff --git a/Clover/app/src/main/java/com/android/volley/compat/NoSSLv3Compat.java b/Clover/app/src/main/java/com/android/volley/compat/NoSSLv3Compat.java deleted file mode 100644 index f8d82fe0..00000000 --- a/Clover/app/src/main/java/com/android/volley/compat/NoSSLv3Compat.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.android.volley.compat; - -import com.android.volley.VolleyLog; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.Socket; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.SSLSocketFactory; - -public class NoSSLv3Compat { - /** - * An {@link javax.net.ssl.SSLSocket} that doesn't allow {@code SSLv3} only connections - *

fixes https://github.com/koush/ion/issues/386

- *

Android bug report: https://code.google.com/p/android/issues/detail?id=78187

- */ - private static class NoSSLv3SSLSocket extends DelegateSSLSocket { - - private NoSSLv3SSLSocket(SSLSocket delegate) { - super(delegate); - - /*String canonicalName = delegate.getClass().getCanonicalName(); - if (!canonicalName.equals("org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl")) { - // try replicate the code from HttpConnection.setupSecureSocket() - try { - Method msetUseSessionTickets = delegate.getClass().getMethod("setUseSessionTickets", boolean.class); - if (null != msetUseSessionTickets) { - msetUseSessionTickets.invoke(delegate, true); - } - } catch (NoSuchMethodException ignored) { - } catch (InvocationTargetException ignored) { - } catch (IllegalAccessException ignored) { - } - }*/ - } - - @Override - public void setEnabledProtocols(String[] protocols) { - if (protocols != null && protocols.length == 1 && "SSLv3".equals(protocols[0])) { - // no way jose - // see issue https://code.google.com/p/android/issues/detail?id=78187 - List enabledProtocols = new ArrayList<>(Arrays.asList(delegate.getEnabledProtocols())); - if (enabledProtocols.size() > 1) { - enabledProtocols.remove("SSLv3"); - VolleyLog.d("Removed SSLv3 from enabled protocols"); - } else { - VolleyLog.d("SSL stuck with protocol available for " + String.valueOf(enabledProtocols)); - } - protocols = enabledProtocols.toArray(new String[enabledProtocols.size()]); - } - - super.setEnabledProtocols(protocols); - } - } - - /** - * {@link javax.net.ssl.SSLSocketFactory} that doesn't allow {@code SSLv3} only connections - */ - public static class NoSSLv3Factory extends SSLSocketFactory { - private final SSLSocketFactory delegate; - - public NoSSLv3Factory() { - this.delegate = HttpsURLConnection.getDefaultSSLSocketFactory(); - } - - @Override - public String[] getDefaultCipherSuites() { - return delegate.getDefaultCipherSuites(); - } - - @Override - public String[] getSupportedCipherSuites() { - return delegate.getSupportedCipherSuites(); - } - - private static Socket makeSocketSafe(Socket socket) { - if (socket instanceof SSLSocket) { - socket = new NoSSLv3SSLSocket((SSLSocket) socket); - } - return socket; - } - - @Override - public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { - return makeSocketSafe(delegate.createSocket(s, host, port, autoClose)); - } - - @Override - public Socket createSocket(String host, int port) throws IOException { - return makeSocketSafe(delegate.createSocket(host, port)); - } - - @Override - public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException { - return makeSocketSafe(delegate.createSocket(host, port, localHost, localPort)); - } - - @Override - public Socket createSocket(InetAddress host, int port) throws IOException { - return makeSocketSafe(delegate.createSocket(host, port)); - } - - @Override - public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { - return makeSocketSafe(delegate.createSocket(address, port, localAddress, localPort)); - } - } -} diff --git a/Clover/app/src/main/java/com/android/volley/toolbox/AndroidAuthenticator.java b/Clover/app/src/main/java/com/android/volley/toolbox/AndroidAuthenticator.java deleted file mode 100644 index 371fd83d..00000000 --- a/Clover/app/src/main/java/com/android/volley/toolbox/AndroidAuthenticator.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley.toolbox; - -import com.android.volley.AuthFailureError; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.accounts.AccountManagerFuture; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; - -/** - * An Authenticator that uses {@link AccountManager} to get auth - * tokens of a specified type for a specified account. - */ -public class AndroidAuthenticator implements Authenticator { - private final Context mContext; - private final Account mAccount; - private final String mAuthTokenType; - private final boolean mNotifyAuthFailure; - - /** - * Creates a new authenticator. - * @param context Context for accessing AccountManager - * @param account Account to authenticate as - * @param authTokenType Auth token type passed to AccountManager - */ - public AndroidAuthenticator(Context context, Account account, String authTokenType) { - this(context, account, authTokenType, false); - } - - /** - * Creates a new authenticator. - * @param context Context for accessing AccountManager - * @param account Account to authenticate as - * @param authTokenType Auth token type passed to AccountManager - * @param notifyAuthFailure Whether to raise a notification upon auth failure - */ - public AndroidAuthenticator(Context context, Account account, String authTokenType, - boolean notifyAuthFailure) { - mContext = context; - mAccount = account; - mAuthTokenType = authTokenType; - mNotifyAuthFailure = notifyAuthFailure; - } - - /** - * Returns the Account being used by this authenticator. - */ - public Account getAccount() { - return mAccount; - } - - // TODO: Figure out what to do about notifyAuthFailure - @SuppressWarnings("deprecation") - @Override - public String getAuthToken() throws AuthFailureError { - final AccountManager accountManager = AccountManager.get(mContext); - AccountManagerFuture future = accountManager.getAuthToken(mAccount, - mAuthTokenType, mNotifyAuthFailure, null, null); - Bundle result; - try { - result = future.getResult(); - } catch (Exception e) { - throw new AuthFailureError("Error while retrieving auth token", e); - } - String authToken = null; - if (future.isDone() && !future.isCancelled()) { - if (result.containsKey(AccountManager.KEY_INTENT)) { - Intent intent = result.getParcelable(AccountManager.KEY_INTENT); - throw new AuthFailureError(intent); - } - authToken = result.getString(AccountManager.KEY_AUTHTOKEN); - } - if (authToken == null) { - throw new AuthFailureError("Got null auth token for type: " + mAuthTokenType); - } - - return authToken; - } - - @Override - public void invalidateAuthToken(String authToken) { - AccountManager.get(mContext).invalidateAuthToken(mAccount.type, authToken); - } -} diff --git a/Clover/app/src/main/java/com/android/volley/toolbox/Authenticator.java b/Clover/app/src/main/java/com/android/volley/toolbox/Authenticator.java deleted file mode 100644 index d9f5e3c2..00000000 --- a/Clover/app/src/main/java/com/android/volley/toolbox/Authenticator.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley.toolbox; - -import com.android.volley.AuthFailureError; - -/** - * An interface for interacting with auth tokens. - */ -public interface Authenticator { - /** - * Synchronously retrieves an auth token. - * - * @throws AuthFailureError If authentication did not succeed - */ - public String getAuthToken() throws AuthFailureError; - - /** - * Invalidates the provided auth token. - */ - public void invalidateAuthToken(String authToken); -} diff --git a/Clover/app/src/main/java/com/android/volley/toolbox/BasicNetwork.java b/Clover/app/src/main/java/com/android/volley/toolbox/BasicNetwork.java deleted file mode 100644 index c76ef6ab..00000000 --- a/Clover/app/src/main/java/com/android/volley/toolbox/BasicNetwork.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley.toolbox; - -import android.os.SystemClock; - -import com.android.volley.AuthFailureError; -import com.android.volley.Cache; -import com.android.volley.Cache.Entry; -import com.android.volley.Network; -import com.android.volley.NetworkError; -import com.android.volley.NetworkResponse; -import com.android.volley.NoConnectionError; -import com.android.volley.Request; -import com.android.volley.RetryPolicy; -import com.android.volley.ServerError; -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.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.TreeMap; - -/** - * A network performing Volley requests over an {@link HttpStack}. - */ -public class BasicNetwork implements Network { - protected static final boolean DEBUG = VolleyLog.DEBUG; - - private static int SLOW_REQUEST_THRESHOLD_MS = 3000; - - private static int DEFAULT_POOL_SIZE = 4096; - - protected final HttpStack mHttpStack; - - protected final ByteArrayPool mPool; - - /** - * @param httpStack HTTP stack to be used - */ - public BasicNetwork(HttpStack httpStack) { - // If a pool isn't passed in, then build a small default pool that will give us a lot of - // benefit and not use too much memory. - this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE)); - } - - /** - * @param httpStack HTTP stack to be used - * @param pool a buffer pool that improves GC performance in copy operations - */ - public BasicNetwork(HttpStack httpStack, ByteArrayPool pool) { - mHttpStack = httpStack; - mPool = pool; - } - - @Override - public NetworkResponse performRequest(Request request) throws VolleyError { - long requestStart = SystemClock.elapsedRealtime(); - while (true) { - HttpResponse httpResponse = null; - byte[] responseContents = null; - Map responseHeaders = Collections.emptyMap(); - try { - // Gather headers. - Map headers = new HashMap(); - addCacheHeaders(headers, request.getCacheEntry()); - httpResponse = mHttpStack.performRequest(request, headers); - StatusLine statusLine = httpResponse.getStatusLine(); - int statusCode = statusLine.getStatusCode(); - - responseHeaders = convertHeaders(httpResponse.getAllHeaders()); - // Handle cache validation. - if (statusCode == HttpStatus.SC_NOT_MODIFIED) { - - Entry entry = request.getCacheEntry(); - if (entry == null) { - return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null, - responseHeaders, true); - } - - // A HTTP 304 response does not have all header fields. We - // have to use the header fields from the cache entry plus - // the new ones from the response. - // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5 - entry.responseHeaders.putAll(responseHeaders); - return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data, - entry.responseHeaders, true); - } - - // Some responses such as 204s do not have content. We must check. - if (httpResponse.getEntity() != null) { - responseContents = entityToBytes(httpResponse.getEntity()); - } else { - // Add 0 byte response as a way of honestly representing a - // no-content request. - responseContents = new byte[0]; - } - - // if the request is slow, log it. - long requestLifetime = SystemClock.elapsedRealtime() - requestStart; - logSlowRequests(requestLifetime, request, responseContents, statusLine); - - if (statusCode < 200 || statusCode > 299) { - throw new IOException(); - } - return new NetworkResponse(statusCode, responseContents, responseHeaders, false); - } catch (SocketTimeoutException e) { - attemptRetryOnException("socket", request, new TimeoutError()); - } catch (ConnectTimeoutException e) { - attemptRetryOnException("connection", request, new TimeoutError()); - } catch (MalformedURLException e) { - throw new RuntimeException("Bad URL " + request.getUrl(), e); - } catch (IOException e) { - int statusCode = 0; - NetworkResponse networkResponse = null; - if (httpResponse != null) { - statusCode = httpResponse.getStatusLine().getStatusCode(); - } else { - throw new NoConnectionError(e); - } - VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl()); - if (responseContents != null) { - networkResponse = new NetworkResponse(statusCode, responseContents, - responseHeaders, false); - if (statusCode == HttpStatus.SC_UNAUTHORIZED || - statusCode == HttpStatus.SC_FORBIDDEN) { - attemptRetryOnException("auth", - request, new AuthFailureError(networkResponse)); - } else { - // TODO: Only throw ServerError for 5xx status codes. - throw new ServerError(networkResponse); - } - } else { - throw new NetworkError(networkResponse); - } - } - } - } - - /** - * Logs requests that took over SLOW_REQUEST_THRESHOLD_MS to complete. - */ - private void logSlowRequests(long requestLifetime, Request request, - byte[] responseContents, StatusLine statusLine) { - if (DEBUG || requestLifetime > SLOW_REQUEST_THRESHOLD_MS) { - VolleyLog.d("HTTP response for request=<%s> [lifetime=%d], [size=%s], " + - "[rc=%d], [retryCount=%s]", request, requestLifetime, - responseContents != null ? responseContents.length : "null", - statusLine.getStatusCode(), request.getRetryPolicy().getCurrentRetryCount()); - } - } - - /** - * Attempts to prepare the request for a retry. If there are no more attempts remaining in the - * request's retry policy, a timeout exception is thrown. - * @param request The request to use. - */ - private static void attemptRetryOnException(String logPrefix, Request request, - VolleyError exception) throws VolleyError { - RetryPolicy retryPolicy = request.getRetryPolicy(); - int oldTimeout = request.getTimeoutMs(); - - try { - retryPolicy.retry(exception); - } catch (VolleyError e) { - request.addMarker( - String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout)); - throw e; - } - request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout)); - } - - private void addCacheHeaders(Map headers, Cache.Entry entry) { - // If there's no cache entry, we're done. - if (entry == null) { - return; - } - - if (entry.etag != null) { - headers.put("If-None-Match", entry.etag); - } - - if (entry.serverDate > 0) { - Date refTime = new Date(entry.serverDate); - headers.put("If-Modified-Since", DateUtils.formatDate(refTime)); - } - } - - protected void logError(String what, String url, long start) { - long now = SystemClock.elapsedRealtime(); - VolleyLog.v("HTTP ERROR(%s) %d ms to fetch %s", what, (now - start), url); - } - - /** Reads the contents of HttpEntity into a byte[]. */ - private byte[] entityToBytes(HttpEntity entity) throws IOException, ServerError { - PoolingByteArrayOutputStream bytes = - new PoolingByteArrayOutputStream(mPool, (int) entity.getContentLength()); - byte[] buffer = null; - try { - InputStream in = entity.getContent(); - if (in == null) { - throw new ServerError(); - } - buffer = mPool.getBuf(1024); - int count; - while ((count = in.read(buffer)) != -1) { - bytes.write(buffer, 0, count); - } - return bytes.toByteArray(); - } finally { - try { - // Close the InputStream and release the resources by "consuming the content". - entity.consumeContent(); - } catch (IOException e) { - // This can happen if there was an exception above that left the entity in - // an invalid state. - VolleyLog.v("Error occured when calling consumingContent"); - } - mPool.returnBuf(buffer); - bytes.close(); - } - } - - /** - * Converts Headers[] to Map. - */ - protected static Map convertHeaders(Header[] headers) { - Map result = new TreeMap(String.CASE_INSENSITIVE_ORDER); - for (int i = 0; i < headers.length; i++) { - result.put(headers[i].getName(), headers[i].getValue()); - } - return result; - } -} diff --git a/Clover/app/src/main/java/com/android/volley/toolbox/ByteArrayPool.java b/Clover/app/src/main/java/com/android/volley/toolbox/ByteArrayPool.java deleted file mode 100644 index af95076a..00000000 --- a/Clover/app/src/main/java/com/android/volley/toolbox/ByteArrayPool.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2012 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 com.android.volley.toolbox; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.LinkedList; -import java.util.List; - -/** - * ByteArrayPool is a source and repository of byte[] objects. Its purpose is to - * supply those buffers to consumers who need to use them for a short period of time and then - * dispose of them. Simply creating and disposing such buffers in the conventional manner can - * considerable heap churn and garbage collection delays on Android, which lacks good management of - * short-lived heap objects. It may be advantageous to trade off some memory in the form of a - * permanently allocated pool of buffers in order to gain heap performance improvements; that is - * what this class does. - *

- * A good candidate user for this class is something like an I/O system that uses large temporary - * byte[] buffers to copy data around. In these use cases, often the consumer wants - * the buffer to be a certain minimum size to ensure good performance (e.g. when copying data chunks - * off of a stream), but doesn't mind if the buffer is larger than the minimum. Taking this into - * account and also to maximize the odds of being able to reuse a recycled buffer, this class is - * free to return buffers larger than the requested size. The caller needs to be able to gracefully - * deal with getting buffers any size over the minimum. - *

- * If there is not a suitably-sized buffer in its recycling pool when a buffer is requested, this - * class will allocate a new buffer and return it. - *

- * This class has no special ownership of buffers it creates; the caller is free to take a buffer - * it receives from this pool, use it permanently, and never return it to the pool; additionally, - * it is not harmful to return to this pool a buffer that was allocated elsewhere, provided there - * are no other lingering references to it. - *

- * This class ensures that the total size of the buffers in its recycling pool never exceeds a - * certain byte limit. When a buffer is returned that would cause the pool to exceed the limit, - * least-recently-used buffers are disposed. - */ -public class ByteArrayPool { - /** The buffer pool, arranged both by last use and by buffer size */ - private List mBuffersByLastUse = new LinkedList(); - private List mBuffersBySize = new ArrayList(64); - - /** The total size of the buffers in the pool */ - private int mCurrentSize = 0; - - /** - * The maximum aggregate size of the buffers in the pool. Old buffers are discarded to stay - * under this limit. - */ - private final int mSizeLimit; - - /** Compares buffers by size */ - protected static final Comparator BUF_COMPARATOR = new Comparator() { - @Override - public int compare(byte[] lhs, byte[] rhs) { - return lhs.length - rhs.length; - } - }; - - /** - * @param sizeLimit the maximum size of the pool, in bytes - */ - public ByteArrayPool(int sizeLimit) { - mSizeLimit = sizeLimit; - } - - /** - * Returns a buffer from the pool if one is available in the requested size, or allocates a new - * one if a pooled one is not available. - * - * @param len the minimum size, in bytes, of the requested buffer. The returned buffer may be - * larger. - * @return a byte[] buffer is always returned. - */ - public synchronized byte[] getBuf(int len) { - for (int i = 0; i < mBuffersBySize.size(); i++) { - byte[] buf = mBuffersBySize.get(i); - if (buf.length >= len) { - mCurrentSize -= buf.length; - mBuffersBySize.remove(i); - mBuffersByLastUse.remove(buf); - return buf; - } - } - return new byte[len]; - } - - /** - * Returns a buffer to the pool, throwing away old buffers if the pool would exceed its allotted - * size. - * - * @param buf the buffer to return to the pool. - */ - public synchronized void returnBuf(byte[] buf) { - if (buf == null || buf.length > mSizeLimit) { - return; - } - mBuffersByLastUse.add(buf); - int pos = Collections.binarySearch(mBuffersBySize, buf, BUF_COMPARATOR); - if (pos < 0) { - pos = -pos - 1; - } - mBuffersBySize.add(pos, buf); - mCurrentSize += buf.length; - trim(); - } - - /** - * Removes buffers from the pool until it is under its size limit. - */ - private synchronized void trim() { - while (mCurrentSize > mSizeLimit) { - byte[] buf = mBuffersByLastUse.remove(0); - mBuffersBySize.remove(buf); - mCurrentSize -= buf.length; - } - } - -} diff --git a/Clover/app/src/main/java/com/android/volley/toolbox/ClearCacheRequest.java b/Clover/app/src/main/java/com/android/volley/toolbox/ClearCacheRequest.java deleted file mode 100644 index a3478bf1..00000000 --- a/Clover/app/src/main/java/com/android/volley/toolbox/ClearCacheRequest.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley.toolbox; - -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. - */ -public class ClearCacheRequest extends Request { - private final Cache mCache; - private final Runnable mCallback; - - /** - * Creates a synthetic request for clearing the cache. - * @param cache Cache to clear - * @param callback Callback to make on the main thread once the cache is clear, - * or null for none - */ - public ClearCacheRequest(Cache cache, Runnable callback) { - super(Method.GET, null, null); - mCache = cache; - mCallback = callback; - } - - @Override - public boolean isCanceled() { - // This is a little bit of a hack, but hey, why not. - mCache.clear(); - if (mCallback != null) { - Handler handler = new Handler(Looper.getMainLooper()); - handler.postAtFrontOfQueue(mCallback); - } - return true; - } - - @Override - public Priority getPriority() { - return Priority.IMMEDIATE; - } - - @Override - protected Response parseNetworkResponse(NetworkResponse response) { - return null; - } - - @Override - protected void deliverResponse(Object response) { - } -} diff --git a/Clover/app/src/main/java/com/android/volley/toolbox/DiskBasedCache.java b/Clover/app/src/main/java/com/android/volley/toolbox/DiskBasedCache.java deleted file mode 100644 index a65a8f4d..00000000 --- a/Clover/app/src/main/java/com/android/volley/toolbox/DiskBasedCache.java +++ /dev/null @@ -1,569 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley.toolbox; - -import android.os.SystemClock; - -import com.android.volley.Cache; -import com.android.volley.VolleyLog; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.EOFException; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * Cache implementation that caches files directly onto the hard disk in the specified - * directory. The default disk usage size is 5MB, but is configurable. - */ -public class DiskBasedCache implements Cache { - - /** Map of the Key, CacheHeader pairs */ - private final Map mEntries = - new LinkedHashMap(16, .75f, true); - - /** Total amount of space currently used by the cache in bytes. */ - private long mTotalSize = 0; - - /** The root directory to use for the cache. */ - private final File mRootDirectory; - - /** The maximum size of the cache in bytes. */ - private final int mMaxCacheSizeInBytes; - - /** Default maximum disk usage in bytes. */ - private static final int DEFAULT_DISK_USAGE_BYTES = 5 * 1024 * 1024; - - /** High water mark percentage for the cache */ - private static final float HYSTERESIS_FACTOR = 0.9f; - - /** Magic number for current version of cache file format. */ - private static final int CACHE_MAGIC = 0x20140623; - - private static final int DEFAULT_DISK_FILES_MAX = 250; - - /** - * Constructs an instance of the DiskBasedCache at the specified directory. - * @param rootDirectory The root directory of the cache. - * @param maxCacheSizeInBytes The maximum size of the cache in bytes. - */ - public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes) { - mRootDirectory = rootDirectory; - mMaxCacheSizeInBytes = maxCacheSizeInBytes; - } - - /** - * Constructs an instance of the DiskBasedCache at the specified directory using - * the default maximum cache size of 5MB. - * @param rootDirectory The root directory of the cache. - */ - public DiskBasedCache(File rootDirectory) { - this(rootDirectory, DEFAULT_DISK_USAGE_BYTES); - } - - /** - * Clears the cache. Deletes all cached files from disk. - */ - @Override - public synchronized void clear() { - File[] files = mRootDirectory.listFiles(); - if (files != null) { - for (File file : files) { - file.delete(); - } - } - mEntries.clear(); - mTotalSize = 0; - VolleyLog.d("Cache cleared."); - } - - /** - * Returns the cache entry with the specified key if it exists, null otherwise. - */ - @Override - public synchronized Entry get(String key) { - CacheHeader entry = mEntries.get(key); - // if the entry does not exist, return. - if (entry == null) { - return null; - } - - File file = getFileForKey(key); - CountingInputStream cis = null; - try { - cis = new CountingInputStream(new BufferedInputStream(new FileInputStream(file))); - CacheHeader.readHeader(cis); // eat header - byte[] data = streamToBytes(cis, (int) (file.length() - cis.bytesRead)); - return entry.toCacheEntry(data); - } catch (IOException e) { - VolleyLog.d("%s: %s", file.getAbsolutePath(), e.toString()); - remove(key); - return null; - } finally { - if (cis != null) { - try { - cis.close(); - } catch (IOException ioe) { - return null; - } - } - } - } - - /** - * Initializes the DiskBasedCache by scanning for all files currently in the - * specified root directory. Creates the root directory if necessary. - */ - @Override - public synchronized void initialize() { - if (!mRootDirectory.exists()) { - if (!mRootDirectory.mkdirs()) { - VolleyLog.e("Unable to create cache dir %s", mRootDirectory.getAbsolutePath()); - } - return; - } - - File[] files = mRootDirectory.listFiles(); - if (files == null) { - return; - } - for (File file : files) { - BufferedInputStream fis = null; - try { - fis = new BufferedInputStream(new FileInputStream(file)); - CacheHeader entry = CacheHeader.readHeader(fis); - entry.size = file.length(); - putEntry(entry.key, entry); - } catch (IOException e) { - if (file != null) { - file.delete(); - } - } finally { - try { - if (fis != null) { - fis.close(); - } - } catch (IOException ignored) { } - } - } - } - - /** - * Invalidates an entry in the cache. - * @param key Cache key - * @param fullExpire True to fully expire the entry, false to soft expire - */ - @Override - public synchronized void invalidate(String key, boolean fullExpire) { - Entry entry = get(key); - if (entry != null) { - entry.softTtl = 0; - if (fullExpire) { - entry.ttl = 0; - } - put(key, entry); - } - - } - - /** - * Puts the entry with the specified key into the cache. - */ - @Override - public synchronized void put(String key, Entry entry) { - pruneIfNeeded(entry.data.length); - File file = getFileForKey(key); - try { - BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(file)); - CacheHeader e = new CacheHeader(key, entry); - boolean success = e.writeHeader(fos); - if (!success) { - fos.close(); - VolleyLog.d("Failed to write header for %s", file.getAbsolutePath()); - throw new IOException(); - } - fos.write(entry.data); - fos.close(); - putEntry(key, e); - return; - } catch (IOException e) { - } - boolean deleted = file.delete(); - if (!deleted) { - VolleyLog.d("Could not clean up file %s", file.getAbsolutePath()); - } - } - - /** - * Removes the specified key from the cache if it exists. - */ - @Override - public synchronized void remove(String key) { - boolean deleted = getFileForKey(key).delete(); - removeEntry(key); - if (!deleted) { - VolleyLog.d("Could not delete cache entry for key=%s, filename=%s", - key, getFilenameForKey(key)); - } - } - - /** - * Creates a pseudo-unique filename for the specified cache key. - * @param key The key to generate a file name for. - * @return A pseudo-unique filename. - */ - private String getFilenameForKey(String key) { - int firstHalfLength = key.length() / 2; - String localFilename = String.valueOf(key.substring(0, firstHalfLength).hashCode()); - localFilename += String.valueOf(key.substring(firstHalfLength).hashCode()); - return localFilename; - } - - /** - * Returns a file object for the given cache key. - */ - public File getFileForKey(String key) { - return new File(mRootDirectory, getFilenameForKey(key)); - } - - /** - * Prunes the cache to fit the amount of bytes specified. - * @param neededSpace The amount of bytes we are trying to fit into the cache. - */ - private void pruneIfNeeded(int neededSpace) { - if (mEntries.size() <= DEFAULT_DISK_FILES_MAX && (mTotalSize + neededSpace) < mMaxCacheSizeInBytes) { - return; - } - if (VolleyLog.DEBUG) { - VolleyLog.v("Pruning old cache entries."); - } - - long before = mTotalSize; - int prunedFiles = 0; - long startTime = SystemClock.elapsedRealtime(); - - Iterator> iterator = mEntries.entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); - CacheHeader e = entry.getValue(); - boolean deleted = getFileForKey(e.key).delete(); - if (deleted) { - mTotalSize -= e.size; - } else { - VolleyLog.d("Could not delete cache entry for key=%s, filename=%s", - e.key, getFilenameForKey(e.key)); - } - iterator.remove(); - prunedFiles++; - - if (mEntries.size() < DEFAULT_DISK_FILES_MAX * HYSTERESIS_FACTOR && (mTotalSize + neededSpace) < mMaxCacheSizeInBytes * HYSTERESIS_FACTOR) { - break; - } - } - - if (VolleyLog.DEBUG) { - VolleyLog.v("pruned %d files, %d bytes, %d ms", - prunedFiles, (mTotalSize - before), SystemClock.elapsedRealtime() - startTime); - } - } - - /** - * Puts the entry with the specified key into the cache. - * @param key The key to identify the entry by. - * @param entry The entry to cache. - */ - private void putEntry(String key, CacheHeader entry) { - if (!mEntries.containsKey(key)) { - mTotalSize += entry.size; - } else { - CacheHeader oldEntry = mEntries.get(key); - mTotalSize += (entry.size - oldEntry.size); - } - mEntries.put(key, entry); - } - - /** - * Removes the entry identified by 'key' from the cache. - */ - private void removeEntry(String key) { - CacheHeader entry = mEntries.get(key); - if (entry != null) { - mTotalSize -= entry.size; - mEntries.remove(key); - } - } - - /** - * Reads the contents of an InputStream into a byte[]. - * */ - private static byte[] streamToBytes(InputStream in, int length) throws IOException { - if (length < 0) { - throw new IOException("length < 0"); - } - - byte[] bytes = new byte[length]; - int count; - int pos = 0; - while (pos < length && ((count = in.read(bytes, pos, length - pos)) != -1)) { - pos += count; - } - if (pos != length) { - throw new IOException("Expected " + length + " bytes, read " + pos + " bytes"); - } - return bytes; - } - - /** - * Handles holding onto the cache headers for an entry. - */ - // Visible for testing. - static class CacheHeader { - /** The size of the data identified by this CacheHeader. (This is not - * serialized to disk. */ - public long size; - - /** The key that identifies the cache entry. */ - public String key; - - /** ETag for cache coherence. */ - public String etag; - - /** Date of this response as reported by the server. */ - public long serverDate; - - /** TTL for this record. */ - public long ttl; - - /** Soft TTL for this record. */ - public long softTtl; - - /** Headers from the response resulting in this cache entry. */ - public Map responseHeaders; - - private CacheHeader() { } - - /** - * Instantiates a new CacheHeader object - * @param key The key that identifies the cache entry - * @param entry The cache entry. - */ - 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; - } - - /** - * Reads the header off of an InputStream and returns a CacheHeader object. - * @param is The InputStream to read from. - * @throws IOException - */ - public static CacheHeader readHeader(InputStream is) throws IOException { - CacheHeader entry = new CacheHeader(); - int magic = readInt(is); - if (magic != CACHE_MAGIC) { - // don't bother deleting, it'll get pruned eventually - throw new IOException(); - } - entry.key = readString(is); - entry.etag = readString(is); - if (entry.etag.equals("")) { - entry.etag = null; - } - entry.serverDate = readLong(is); - entry.ttl = readLong(is); - entry.softTtl = readLong(is); - entry.responseHeaders = readStringStringMap(is); - return entry; - } - - /** - * Creates a cache entry for the specified data. - */ - public Entry toCacheEntry(byte[] data) { - Entry e = new Entry(); - e.data = data; - e.etag = etag; - e.serverDate = serverDate; - e.ttl = ttl; - e.softTtl = softTtl; - e.responseHeaders = responseHeaders; - return e; - } - - - /** - * Writes the contents of this CacheHeader to the specified OutputStream. - */ - public boolean writeHeader(OutputStream os) { - try { - writeInt(os, CACHE_MAGIC); - writeString(os, key); - writeString(os, etag == null ? "" : etag); - writeLong(os, serverDate); - writeLong(os, ttl); - writeLong(os, softTtl); - writeStringStringMap(responseHeaders, os); - os.flush(); - return true; - } catch (IOException e) { - VolleyLog.d("%s", e.toString()); - return false; - } - } - - } - - private static class CountingInputStream extends FilterInputStream { - private int bytesRead = 0; - - private CountingInputStream(InputStream in) { - super(in); - } - - @Override - public int read() throws IOException { - int result = super.read(); - if (result != -1) { - bytesRead++; - } - return result; - } - - @Override - public int read(byte[] buffer, int offset, int count) throws IOException { - int result = super.read(buffer, offset, count); - if (result != -1) { - bytesRead += result; - } - return result; - } - } - - /* - * Homebrewed simple serialization system used for reading and writing cache - * headers on disk. Once upon a time, this used the standard Java - * Object{Input,Output}Stream, but the default implementation relies heavily - * on reflection (even for standard types) and generates a ton of garbage. - */ - - /** - * Simple wrapper around {@link InputStream#read()} that throws EOFException - * instead of returning -1. - */ - private static int read(InputStream is) throws IOException { - int b = is.read(); - if (b == -1) { - throw new EOFException(); - } - return b; - } - - static void writeInt(OutputStream os, int n) throws IOException { - os.write((n >> 0) & 0xff); - os.write((n >> 8) & 0xff); - os.write((n >> 16) & 0xff); - os.write((n >> 24) & 0xff); - } - - static int readInt(InputStream is) throws IOException { - int n = 0; - n |= (read(is) << 0); - n |= (read(is) << 8); - n |= (read(is) << 16); - n |= (read(is) << 24); - return n; - } - - static void writeLong(OutputStream os, long n) throws IOException { - os.write((byte)(n >>> 0)); - os.write((byte)(n >>> 8)); - os.write((byte)(n >>> 16)); - os.write((byte)(n >>> 24)); - os.write((byte)(n >>> 32)); - os.write((byte)(n >>> 40)); - os.write((byte)(n >>> 48)); - os.write((byte)(n >>> 56)); - } - - static long readLong(InputStream is) throws IOException { - long n = 0; - n |= ((read(is) & 0xFFL) << 0); - n |= ((read(is) & 0xFFL) << 8); - n |= ((read(is) & 0xFFL) << 16); - n |= ((read(is) & 0xFFL) << 24); - n |= ((read(is) & 0xFFL) << 32); - n |= ((read(is) & 0xFFL) << 40); - n |= ((read(is) & 0xFFL) << 48); - n |= ((read(is) & 0xFFL) << 56); - return n; - } - - static void writeString(OutputStream os, String s) throws IOException { - byte[] b = s.getBytes("UTF-8"); - writeLong(os, b.length); - os.write(b, 0, b.length); - } - - static String readString(InputStream is) throws IOException { - int n = (int) readLong(is); - byte[] b = streamToBytes(is, n); - return new String(b, "UTF-8"); - } - - static void writeStringStringMap(Map map, OutputStream os) throws IOException { - if (map != null) { - writeInt(os, map.size()); - for (Map.Entry entry : map.entrySet()) { - writeString(os, entry.getKey()); - writeString(os, entry.getValue()); - } - } else { - writeInt(os, 0); - } - } - - static Map readStringStringMap(InputStream is) throws IOException { - int size = readInt(is); - Map result = (size == 0) - ? Collections.emptyMap() - : new HashMap(size); - for (int i = 0; i < size; i++) { - String key = readString(is).intern(); - String value = readString(is).intern(); - result.put(key, value); - } - return result; - } - - -} diff --git a/Clover/app/src/main/java/com/android/volley/toolbox/HttpClientStack.java b/Clover/app/src/main/java/com/android/volley/toolbox/HttpClientStack.java deleted file mode 100644 index 377110ef..00000000 --- a/Clover/app/src/main/java/com/android/volley/toolbox/HttpClientStack.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley.toolbox; - -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; -import org.apache.http.NameValuePair; -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 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}. - */ -public class HttpClientStack implements HttpStack { - protected final HttpClient mClient; - - private final static String HEADER_CONTENT_TYPE = "Content-Type"; - - public HttpClientStack(HttpClient client) { - mClient = client; - } - - private static void addHeaders(HttpUriRequest httpRequest, Map headers) { - for (String key : headers.keySet()) { - httpRequest.setHeader(key, headers.get(key)); - } - } - - @SuppressWarnings("unused") - private static List getPostParameterPairs(Map postParams) { - List result = new ArrayList(postParams.size()); - for (String key : postParams.keySet()) { - result.add(new BasicNameValuePair(key, postParams.get(key))); - } - return result; - } - - @Override - public HttpResponse performRequest(Request request, Map additionalHeaders) - throws IOException, AuthFailureError { - HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders); - addHeaders(httpRequest, additionalHeaders); - addHeaders(httpRequest, request.getHeaders()); - onPrepareRequest(httpRequest); - HttpParams httpParams = httpRequest.getParams(); - int timeoutMs = request.getTimeoutMs(); - // TODO: Reevaluate this connection timeout based on more wide-scale - // data collection and possibly different for wifi vs. 3G. - HttpConnectionParams.setConnectionTimeout(httpParams, 5000); - HttpConnectionParams.setSoTimeout(httpParams, timeoutMs); - return mClient.execute(httpRequest); - } - - /** - * 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()) { - case Method.DEPRECATED_GET_OR_POST: { - // This is the deprecated way that needs to be handled for backwards compatibility. - // If the request's post body is null, then the assumption is that the request is - // GET. Otherwise, it is assumed that the request is a POST. - byte[] postBody = request.getPostBody(); - if (postBody != null) { - HttpPost postRequest = new HttpPost(request.getUrl()); - postRequest.addHeader(HEADER_CONTENT_TYPE, request.getPostBodyContentType()); - HttpEntity entity; - entity = new ByteArrayEntity(postBody); - postRequest.setEntity(entity); - return postRequest; - } else { - return new HttpGet(request.getUrl()); - } - } - case Method.GET: - return new HttpGet(request.getUrl()); - case Method.DELETE: - return new HttpDelete(request.getUrl()); - case Method.POST: { - HttpPost postRequest = new HttpPost(request.getUrl()); - postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType()); - setEntityIfNonEmptyBody(postRequest, request); - return postRequest; - } - case Method.PUT: { - HttpPut putRequest = new HttpPut(request.getUrl()); - putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType()); - 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."); - } - } - - private static void setEntityIfNonEmptyBody(HttpEntityEnclosingRequestBase httpRequest, - Request request) throws AuthFailureError { - byte[] body = request.getBody(); - if (body != null) { - HttpEntity entity = new ByteArrayEntity(body); - httpRequest.setEntity(entity); - } - } - - /** - * Called before the request is executed using the underlying HttpClient. - * - *

Overwrite in subclasses to augment the request.

- */ - 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/Clover/app/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java b/Clover/app/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java deleted file mode 100644 index cb084322..00000000 --- a/Clover/app/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley.toolbox; - -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 java.util.Map; - -/** - * Utility methods for parsing HTTP headers. - */ -public class HttpHeaderParser { - - /** - * Extracts a {@link Cache.Entry} from a {@link NetworkResponse}. - * - * @param response The network response to parse headers from - * @return a cache entry for the given response, or null if the response is not cacheable. - */ - public static Cache.Entry parseCacheHeaders(NetworkResponse response) { - long now = System.currentTimeMillis(); - - Map headers = response.headers; - - long serverDate = 0; - long serverExpires = 0; - long softExpire = 0; - long maxAge = 0; - boolean hasCacheControl = false; - - String serverEtag = null; - String headerValue; - - headerValue = headers.get("Date"); - if (headerValue != null) { - serverDate = parseDateAsEpoch(headerValue); - } - - headerValue = headers.get("Cache-Control"); - if (headerValue != null) { - hasCacheControl = true; - String[] tokens = headerValue.split(","); - for (int i = 0; i < tokens.length; i++) { - String token = tokens[i].trim(); - if (token.equals("no-cache") || token.equals("no-store")) { - return null; - } else if (token.startsWith("max-age=")) { - try { - maxAge = Long.parseLong(token.substring(8)); - } catch (Exception e) { - } - } else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) { - maxAge = 0; - } - } - } - - headerValue = headers.get("Expires"); - if (headerValue != null) { - serverExpires = 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; - } else if (serverDate > 0 && serverExpires >= serverDate) { - // Default semantic for Expire header in HTTP specification is softExpire. - softExpire = now + (serverExpires - serverDate); - } - - Cache.Entry entry = new Cache.Entry(); - entry.data = response.data; - entry.etag = serverEtag; - entry.softTtl = softExpire; - entry.ttl = entry.softTtl; - entry.serverDate = serverDate; - entry.responseHeaders = headers; - - return entry; - } - - /** - * Parse date in RFC1123 format, and return its value as epoch - */ - public static long parseDateAsEpoch(String dateStr) { - try { - // Parse date in RFC1123 format if this header contains one - return DateUtils.parseDate(dateStr).getTime(); - } catch (DateParseException e) { - // Date in invalid format, fallback to 0 - return 0; - } - } - - /** - * Returns the charset specified in the Content-Type of this header, - * or the HTTP default (ISO-8859-1) if none can be found. - */ - public static String parseCharset(Map headers) { - String contentType = headers.get(HTTP.CONTENT_TYPE); - if (contentType != null) { - String[] params = contentType.split(";"); - for (int i = 1; i < params.length; i++) { - String[] pair = params[i].trim().split("="); - if (pair.length == 2) { - if (pair[0].equals("charset")) { - return pair[1]; - } - } - } - } - - return HTTP.DEFAULT_CONTENT_CHARSET; - } -} diff --git a/Clover/app/src/main/java/com/android/volley/toolbox/HttpStack.java b/Clover/app/src/main/java/com/android/volley/toolbox/HttpStack.java deleted file mode 100644 index a52fd06c..00000000 --- a/Clover/app/src/main/java/com/android/volley/toolbox/HttpStack.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley.toolbox; - -import com.android.volley.AuthFailureError; -import com.android.volley.Request; - -import org.apache.http.HttpResponse; - -import java.io.IOException; -import java.util.Map; - -/** - * An HTTP stack abstraction. - */ -public interface HttpStack { - /** - * Performs an HTTP request with the given parameters. - * - *

A GET request is sent if request.getPostBody() == null. A POST request is sent otherwise, - * and the Content-Type header is set to request.getPostBodyContentType().

- * - * @param request the request to perform - * @param additionalHeaders additional headers to be sent together with - * {@link Request#getHeaders()} - * @return the HTTP response - */ - public HttpResponse performRequest(Request request, Map additionalHeaders) - throws IOException, AuthFailureError; - -} diff --git a/Clover/app/src/main/java/com/android/volley/toolbox/HurlStack.java b/Clover/app/src/main/java/com/android/volley/toolbox/HurlStack.java deleted file mode 100644 index 7bd5bf7a..00000000 --- a/Clover/app/src/main/java/com/android/volley/toolbox/HurlStack.java +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright (C) 2011 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 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; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLSocketFactory; - -/** - * An {@link HttpStack} based on {@link HttpURLConnection}. - */ -public class HurlStack implements HttpStack { - - private static final String HEADER_CONTENT_TYPE = "Content-Type"; - private final String mUserAgent; - - /** - * An interface for transforming URLs before use. - */ - public interface UrlRewriter { - /** - * Returns a URL to use instead of the provided one, or null to indicate - * this URL should not be used at all. - */ - public String rewriteUrl(String originalUrl); - } - - private final UrlRewriter mUrlRewriter; - private final SSLSocketFactory mSslSocketFactory; - - public HurlStack(String userAgent) { - this(userAgent, null); - } - - /** - * @param urlRewriter Rewriter to use for request URLs - */ - public HurlStack(String userAgent, UrlRewriter urlRewriter) { - this(userAgent, urlRewriter, null); - } - - /** - * @param urlRewriter Rewriter to use for request URLs - * @param sslSocketFactory SSL factory to use for HTTPS connections - */ - public HurlStack(String userAgent, UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) { - mUserAgent = userAgent; - mUrlRewriter = urlRewriter; - mSslSocketFactory = sslSocketFactory; - } - - @Override - public HttpResponse performRequest(Request request, Map additionalHeaders) - throws IOException, AuthFailureError { - String url = request.getUrl(); - HashMap map = new HashMap(); - map.putAll(request.getHeaders()); - map.putAll(additionalHeaders); - map.put("User-Agent", mUserAgent); - if (mUrlRewriter != null) { - String rewritten = mUrlRewriter.rewriteUrl(url); - if (rewritten == null) { - throw new IOException("URL blocked by rewriter: " + url); - } - url = rewritten; - } - URL parsedUrl = new URL(url); - HttpURLConnection connection = openConnection(parsedUrl, request); - for (String headerName : map.keySet()) { - connection.addRequestProperty(headerName, map.get(headerName)); - } - setConnectionParametersForRequest(connection, request); - // Initialize HttpResponse with data from the HttpURLConnection. - ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1); - int responseCode = connection.getResponseCode(); - if (responseCode == -1) { - // -1 is returned by getResponseCode() if the response code could not be retrieved. - // Signal to the caller that something was wrong with the connection. - throw new IOException("Could not retrieve response code from HttpUrlConnection."); - } - StatusLine responseStatus = new BasicStatusLine(protocolVersion, - connection.getResponseCode(), connection.getResponseMessage()); - BasicHttpResponse response = new BasicHttpResponse(responseStatus); - response.setEntity(entityFromConnection(connection)); - for (Entry> header : connection.getHeaderFields().entrySet()) { - if (header.getKey() != null) { - Header h = new BasicHeader(header.getKey(), header.getValue().get(0)); - response.addHeader(h); - } - } - return response; - } - - /** - * Initializes an {@link HttpEntity} from the given {@link HttpURLConnection}. - * @param connection - * @return an HttpEntity populated with data from connection. - */ - private static HttpEntity entityFromConnection(HttpURLConnection connection) { - BasicHttpEntity entity = new BasicHttpEntity(); - InputStream inputStream; - try { - inputStream = connection.getInputStream(); - } catch (IOException ioe) { - inputStream = connection.getErrorStream(); - } - entity.setContent(inputStream); - entity.setContentLength(connection.getContentLength()); - entity.setContentEncoding(connection.getContentEncoding()); - entity.setContentType(connection.getContentType()); - return entity; - } - - /** - * Create an {@link HttpURLConnection} for the specified {@code url}. - */ - protected HttpURLConnection createConnection(URL url) throws IOException { - return (HttpURLConnection) url.openConnection(); - } - - /** - * Opens an {@link HttpURLConnection} with parameters. - * @param url - * @return an open connection - * @throws IOException - */ - private HttpURLConnection openConnection(URL url, Request request) throws IOException { - HttpURLConnection connection = createConnection(url); - - int timeoutMs = request.getTimeoutMs(); - connection.setConnectTimeout(timeoutMs); - connection.setReadTimeout(timeoutMs); - connection.setUseCaches(false); - connection.setDoInput(true); - - // use caller-provided custom SslSocketFactory, if any, for HTTPS - if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) { - ((HttpsURLConnection)connection).setSSLSocketFactory(mSslSocketFactory); - } - - return connection; - } - - @SuppressWarnings("deprecation") - /* package */ static void setConnectionParametersForRequest(HttpURLConnection connection, - Request request) throws IOException, AuthFailureError { - switch (request.getMethod()) { - case Method.DEPRECATED_GET_OR_POST: - // This is the deprecated way that needs to be handled for backwards compatibility. - // If the request's post body is null, then the assumption is that the request is - // GET. Otherwise, it is assumed that the request is a POST. - byte[] postBody = request.getPostBody(); - if (postBody != null) { - // Prepare output. There is no need to set Content-Length explicitly, - // since this is handled by HttpURLConnection using the size of the prepared - // output stream. - connection.setDoOutput(true); - connection.setRequestMethod("POST"); - connection.addRequestProperty(HEADER_CONTENT_TYPE, - request.getPostBodyContentType()); - DataOutputStream out = new DataOutputStream(connection.getOutputStream()); - out.write(postBody); - out.close(); - } - break; - case Method.GET: - // Not necessary to set the request method because connection defaults to GET but - // being explicit here. - connection.setRequestMethod("GET"); - break; - case Method.DELETE: - connection.setRequestMethod("DELETE"); - break; - case Method.POST: - connection.setRequestMethod("POST"); - addBodyIfExists(connection, request); - break; - case Method.PUT: - 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: - connection.setRequestMethod("PATCH"); - addBodyIfExists(connection, request); - break; - default: - throw new IllegalStateException("Unknown method type."); - } - } - - private static void addBodyIfExists(HttpURLConnection connection, Request request) - throws IOException, AuthFailureError { - byte[] body = request.getBody(); - if (body != null) { - connection.setDoOutput(true); - connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType()); - DataOutputStream out = new DataOutputStream(connection.getOutputStream()); - out.write(body); - out.close(); - } - } -} diff --git a/Clover/app/src/main/java/com/android/volley/toolbox/ImageLoader.java b/Clover/app/src/main/java/com/android/volley/toolbox/ImageLoader.java deleted file mode 100644 index 5348dc69..00000000 --- a/Clover/app/src/main/java/com/android/volley/toolbox/ImageLoader.java +++ /dev/null @@ -1,482 +0,0 @@ -/** - * 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 com.android.volley.toolbox; - -import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.os.Handler; -import android.os.Looper; -import android.widget.ImageView; - -import com.android.volley.Request; -import com.android.volley.RequestQueue; -import com.android.volley.Response.ErrorListener; -import com.android.volley.Response.Listener; -import com.android.volley.VolleyError; - -import java.util.HashMap; -import java.util.LinkedList; - -/** - * Helper that handles loading and caching images from remote URLs. - * - * The simple way to use this class is to call {@link ImageLoader#get(String, ImageListener)} - * and to pass in the default image listener provided by - * {@link ImageLoader#getImageListener(ImageView, int, int)}. Note that all function calls to - * this class must be made from the main thead, and all responses will be delivered to the main - * thread as well. - */ -public class ImageLoader { - /** RequestQueue for dispatching ImageRequests onto. */ - private final RequestQueue mRequestQueue; - - /** Amount of time to wait after first response arrives before delivering all responses. */ - private int mBatchResponseDelayMs = 100; - - /** The cache implementation to be used as an L1 cache before calling into volley. */ - private final ImageCache mCache; - - /** - * HashMap of Cache keys -> BatchedImageRequest used to track in-flight requests so - * that we can coalesce multiple requests to the same URL into a single network request. - */ - private final HashMap mInFlightRequests = - new HashMap(); - - /** HashMap of the currently pending responses (waiting to be delivered). */ - private final HashMap mBatchedResponses = - new HashMap(); - - /** Handler to the main thread. */ - private final Handler mHandler = new Handler(Looper.getMainLooper()); - - /** Runnable for in-flight response delivery. */ - private Runnable mRunnable; - - /** - * Simple cache adapter interface. If provided to the ImageLoader, it - * will be used as an L1 cache before dispatch to Volley. Implementations - * must not block. Implementation with an LruCache is recommended. - */ - public interface ImageCache { - public Bitmap getBitmap(String url); - public void putBitmap(String url, Bitmap bitmap); - } - - /** - * Constructs a new ImageLoader. - * @param queue The RequestQueue to use for making image requests. - * @param imageCache The cache to use as an L1 cache. - */ - public ImageLoader(RequestQueue queue, ImageCache imageCache) { - mRequestQueue = queue; - mCache = imageCache; - } - - /** - * The default implementation of ImageListener which handles basic functionality - * of showing a default image until the network response is received, at which point - * it will switch to either the actual image or the error image. - * @param imageView The imageView that the listener is associated with. - * @param defaultImageResId Default image resource ID to use, or 0 if it doesn't exist. - * @param errorImageResId Error image resource ID to use, or 0 if it doesn't exist. - */ - public static ImageListener getImageListener(final ImageView view, - final int defaultImageResId, final int errorImageResId) { - return new ImageListener() { - @Override - public void onErrorResponse(VolleyError error) { - if (errorImageResId != 0) { - view.setImageResource(errorImageResId); - } - } - - @Override - public void onResponse(ImageContainer response, boolean isImmediate) { - if (response.getBitmap() != null) { - view.setImageBitmap(response.getBitmap()); - } else if (defaultImageResId != 0) { - view.setImageResource(defaultImageResId); - } - } - }; - } - - /** - * Interface for the response handlers on image requests. - * - * The call flow is this: - * 1. Upon being attached to a request, onResponse(response, true) will - * be invoked to reflect any cached data that was already available. If the - * data was available, response.getBitmap() will be non-null. - * - * 2. After a network response returns, only one of the following cases will happen: - * - onResponse(response, false) will be called if the image was loaded. - * or - * - onErrorResponse will be called if there was an error loading the image. - */ - public interface ImageListener extends ErrorListener { - /** - * Listens for non-error changes to the loading of the image request. - * - * @param response Holds all information pertaining to the request, as well - * as the bitmap (if it is loaded). - * @param isImmediate True if this was called during ImageLoader.get() variants. - * This can be used to differentiate between a cached image loading and a network - * image loading in order to, for example, run an animation to fade in network loaded - * images. - */ - public void onResponse(ImageContainer response, boolean isImmediate); - } - - /** - * Checks if the item is available in the cache. - * @param requestUrl The url of the remote image - * @param maxWidth The maximum width of the returned image. - * @param maxHeight The maximum height of the returned image. - * @return True if the item exists in cache, false otherwise. - */ - public boolean isCached(String requestUrl, int maxWidth, int maxHeight) { - throwIfNotOnMainThread(); - - String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight); - return mCache.getBitmap(cacheKey) != null; - } - - /** - * Returns an ImageContainer for the requested URL. - * - * The ImageContainer will contain either the specified default bitmap or the loaded bitmap. - * If the default was returned, the {@link ImageLoader} will be invoked when the - * request is fulfilled. - * - * @param requestUrl The URL of the image to be loaded. - * @param defaultImage Optional default image to return until the actual image is loaded. - */ - public ImageContainer get(String requestUrl, final ImageListener listener) { - return get(requestUrl, listener, 0, 0); - } - - /** - * 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 - * image is not available). - * @param requestUrl The url of the remote image - * @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. - * @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) { - // only fulfill requests that were initiated from the main thread. - throwIfNotOnMainThread(); - - final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight); - - // Try to look up the request in the cache of remote images. - Bitmap cachedBitmap = mCache.getBitmap(cacheKey); - if (cachedBitmap != null) { - // Return the cached bitmap. - ImageContainer container = new ImageContainer(cachedBitmap, requestUrl, null, null); - imageListener.onResponse(container, true); - return container; - } - - // The bitmap did not exist in the cache, fetch it! - ImageContainer imageContainer = - new ImageContainer(null, requestUrl, cacheKey, imageListener); - - // Update the caller to let them know that they should use the default bitmap. - imageListener.onResponse(imageContainer, true); - - // Check to see if a request is already in-flight. - BatchedImageRequest request = mInFlightRequests.get(cacheKey); - if (request != null) { - // If it is, add this request to the list of listeners. - request.addContainer(imageContainer); - return imageContainer; - } - - // The request is not already in flight. Send the new request to the network and - // track it. - Request newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight, cacheKey); - - mRequestQueue.add(newRequest); - mInFlightRequests.put(cacheKey, - new BatchedImageRequest(newRequest, imageContainer)); - return imageContainer; - } - - protected Request makeImageRequest(String requestUrl, int maxWidth, int maxHeight, final String cacheKey) { - return new ImageRequest(requestUrl, new Listener() { - @Override - public void onResponse(Bitmap response) { - onGetImageSuccess(cacheKey, response); - } - }, maxWidth, maxHeight, - Config.RGB_565, new ErrorListener() { - @Override - public void onErrorResponse(VolleyError error) { - onGetImageError(cacheKey, error); - } - }); - } - - /** - * Sets the amount of time to wait after the first response arrives before delivering all - * responses. Batching can be disabled entirely by passing in 0. - * @param newBatchedResponseDelayMs The time in milliseconds to wait. - */ - public void setBatchedResponseDelay(int newBatchedResponseDelayMs) { - mBatchResponseDelayMs = newBatchedResponseDelayMs; - } - - /** - * Handler for when an image was successfully loaded. - * @param cacheKey The cache key that is associated with the image request. - * @param response The bitmap that was returned from the network. - */ - protected void onGetImageSuccess(String cacheKey, Bitmap response) { - // cache the image that was fetched. - mCache.putBitmap(cacheKey, response); - - // remove the request from the list of in-flight requests. - BatchedImageRequest request = mInFlightRequests.remove(cacheKey); - - if (request != null) { - // Update the response bitmap. - request.mResponseBitmap = response; - - // Send the batched response - batchResponse(cacheKey, request); - } - } - - /** - * Handler for when an image failed to load. - * @param cacheKey The cache key that is associated with the image request. - */ - protected void onGetImageError(String cacheKey, VolleyError error) { - // Notify the requesters that something failed via a null result. - // Remove this request from the list of in-flight requests. - BatchedImageRequest request = mInFlightRequests.remove(cacheKey); - - if (request != null) { - // Set the error for this request - request.setError(error); - - // Send the batched response - batchResponse(cacheKey, request); - } - } - - /** - * Container object for all of the data surrounding an image request. - */ - public class ImageContainer { - /** - * The most relevant bitmap for the container. If the image was in cache, the - * Holder to use for the final bitmap (the one that pairs to the requested URL). - */ - private Bitmap mBitmap; - - private final ImageListener mListener; - - /** The cache key that was associated with the request */ - private final String mCacheKey; - - /** The request URL that was specified */ - private final String mRequestUrl; - - /** - * Constructs a BitmapContainer object. - * @param bitmap The final bitmap (if it exists). - * @param requestUrl The requested URL for this container. - * @param cacheKey The cache key that identifies the requested URL for this container. - */ - public ImageContainer(Bitmap bitmap, String requestUrl, - String cacheKey, ImageListener listener) { - mBitmap = bitmap; - mRequestUrl = requestUrl; - mCacheKey = cacheKey; - mListener = listener; - } - - /** - * Releases interest in the in-flight request (and cancels it if no one else is listening). - */ - public void cancelRequest() { - if (mListener == null) { - return; - } - - BatchedImageRequest request = mInFlightRequests.get(mCacheKey); - if (request != null) { - boolean canceled = request.removeContainerAndCancelIfNecessary(this); - if (canceled) { - mInFlightRequests.remove(mCacheKey); - } - } else { - // check to see if it is already batched for delivery. - request = mBatchedResponses.get(mCacheKey); - if (request != null) { - request.removeContainerAndCancelIfNecessary(this); - if (request.mContainers.size() == 0) { - mBatchedResponses.remove(mCacheKey); - } - } - } - } - - /** - * Returns the bitmap associated with the request URL if it has been loaded, null otherwise. - */ - public Bitmap getBitmap() { - return mBitmap; - } - - /** - * Returns the requested URL for this container. - */ - public String getRequestUrl() { - return mRequestUrl; - } - } - - /** - * Wrapper class used to map a Request to the set of active ImageContainer objects that are - * interested in its results. - */ - private class BatchedImageRequest { - /** The request being tracked */ - private final Request mRequest; - - /** The result of the request being tracked by this item */ - private Bitmap mResponseBitmap; - - /** Error if one occurred for this response */ - private VolleyError mError; - - /** List of all of the active ImageContainers that are interested in the request */ - private final LinkedList mContainers = new LinkedList(); - - /** - * Constructs a new BatchedImageRequest object - * @param request The request being tracked - * @param container The ImageContainer of the person who initiated the request. - */ - public BatchedImageRequest(Request request, ImageContainer container) { - mRequest = request; - mContainers.add(container); - } - - /** - * Set the error for this response - */ - public void setError(VolleyError error) { - mError = error; - } - - /** - * Get the error for this response - */ - public VolleyError getError() { - return mError; - } - - /** - * Adds another ImageContainer to the list of those interested in the results of - * the request. - */ - public void addContainer(ImageContainer container) { - mContainers.add(container); - } - - /** - * Detatches the bitmap container from the request and cancels the request if no one is - * left listening. - * @param container The container to remove from the list - * @return True if the request was canceled, false otherwise. - */ - public boolean removeContainerAndCancelIfNecessary(ImageContainer container) { - mContainers.remove(container); - if (mContainers.size() == 0) { - mRequest.cancel(); - return true; - } - return false; - } - } - - /** - * Starts the runnable for batched delivery of responses if it is not already started. - * @param cacheKey The cacheKey of the response being delivered. - * @param request The BatchedImageRequest to be delivered. - * @param error The volley error associated with the request (if applicable). - */ - private void batchResponse(String cacheKey, BatchedImageRequest request) { - mBatchedResponses.put(cacheKey, request); - // If we don't already have a batch delivery runnable in flight, make a new one. - // Note that this will be used to deliver responses to all callers in mBatchedResponses. - if (mRunnable == null) { - mRunnable = new Runnable() { - @Override - public void run() { - for (BatchedImageRequest bir : mBatchedResponses.values()) { - for (ImageContainer container : bir.mContainers) { - // If one of the callers in the batched request canceled the request - // after the response was received but before it was delivered, - // skip them. - if (container.mListener == null) { - continue; - } - if (bir.getError() == null) { - container.mBitmap = bir.mResponseBitmap; - container.mListener.onResponse(container, false); - } else { - container.mListener.onErrorResponse(bir.getError()); - } - } - } - mBatchedResponses.clear(); - mRunnable = null; - } - - }; - // Post the runnable. - mHandler.postDelayed(mRunnable, mBatchResponseDelayMs); - } - } - - private void throwIfNotOnMainThread() { - if (Looper.myLooper() != Looper.getMainLooper()) { - throw new IllegalStateException("ImageLoader must be invoked from the main thread."); - } - } - /** - * Creates a cache key for use with the L1 cache. - * @param url The URL of the request. - * @param maxWidth The max-width of the output. - * @param maxHeight The max-height of the output. - */ - private static String getCacheKey(String url, int maxWidth, int maxHeight) { - return new StringBuilder(url.length() + 12).append("#W").append(maxWidth) - .append("#H").append(maxHeight).append(url).toString(); - } -} diff --git a/Clover/app/src/main/java/com/android/volley/toolbox/ImageRequest.java b/Clover/app/src/main/java/com/android/volley/toolbox/ImageRequest.java deleted file mode 100644 index dd613e8d..00000000 --- a/Clover/app/src/main/java/com/android/volley/toolbox/ImageRequest.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright (C) 2011 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 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; -import com.android.volley.Request; -import com.android.volley.Response; -import com.android.volley.VolleyLog; - -/** - * A canned request for getting an image at a given URL and calling - * back with a decoded Bitmap. - */ -public class ImageRequest extends Request { - /** Socket timeout in milliseconds for image requests */ - private static final int IMAGE_TIMEOUT_MS = 1000; - - /** Default number of retries for image requests */ - private static final int IMAGE_MAX_RETRIES = 2; - - /** Default backoff multiplier for image requests */ - private static final float IMAGE_BACKOFF_MULT = 2f; - - private final Response.Listener mListener; - private final Config mDecodeConfig; - private final int mMaxWidth; - private final int mMaxHeight; - - /** 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(); - - /** - * Creates a new image request, decoding to a maximum specified width and - * height. If both width and height are zero, the image will be decoded to - * its natural size. If one of the two is nonzero, that dimension will be - * clamped and the other one will be set to preserve the image's aspect - * ratio. If both width and height are nonzero, the image will be decoded to - * be fit in the rectangle of dimensions width x height while keeping its - * aspect ratio. - * - * @param url URL of the image - * @param listener Listener to receive the decoded 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 decodeConfig Format to decode the bitmap to - * @param errorListener Error listener, or null to ignore errors - */ - public ImageRequest(String url, Response.Listener listener, int maxWidth, int maxHeight, - 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; - } - - @Override - public Priority getPriority() { - return Priority.LOW; - } - - /** - * Scales one side of a rectangle to fit aspect ratio. - * - * @param maxPrimary Maximum size of the primary dimension (i.e. width for - * max width), or zero to maintain aspect ratio with secondary - * dimension - * @param maxSecondary Maximum size of the secondary dimension, or zero to - * maintain aspect ratio with primary dimension - * @param actualPrimary Actual size of the primary dimension - * @param actualSecondary Actual size of the secondary dimension - */ - private static int getResizedDimension(int maxPrimary, int maxSecondary, int actualPrimary, - int actualSecondary) { - // If no dominant value at all, just return the actual. - if (maxPrimary == 0 && maxSecondary == 0) { - return actualPrimary; - } - - // If primary is unspecified, scale primary to match secondary's scaling ratio. - if (maxPrimary == 0) { - double ratio = (double) maxSecondary / (double) actualSecondary; - return (int) (actualPrimary * ratio); - } - - if (maxSecondary == 0) { - return maxPrimary; - } - - double ratio = (double) actualSecondary / (double) actualPrimary; - int resized = maxPrimary; - if (resized * ratio > maxSecondary) { - resized = (int) (maxSecondary / ratio); - } - return resized; - } - - @Override - protected Response parseNetworkResponse(NetworkResponse response) { - // Serialize all decode on a global lock to reduce concurrent heap usage. - synchronized (sDecodeLock) { - try { - return doParse(response); - } catch (OutOfMemoryError e) { - VolleyLog.e("Caught OOM for %d byte image, url=%s", response.data.length, getUrl()); - return Response.error(new ParseError(e)); - } - } - } - - /** - * The real guts of parseNetworkResponse. Broken out for readability. - */ - private Response doParse(NetworkResponse response) { - byte[] data = response.data; - BitmapFactory.Options decodeOptions = new BitmapFactory.Options(); - Bitmap bitmap = null; - if (mMaxWidth == 0 && mMaxHeight == 0) { - decodeOptions.inPreferredConfig = mDecodeConfig; - bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions); - } else { - // If we have to resize this image, first get the natural bounds. - decodeOptions.inJustDecodeBounds = true; - BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions); - int actualWidth = decodeOptions.outWidth; - int actualHeight = decodeOptions.outHeight; - - // Then compute the dimensions we would ideally like to decode to. - int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight, - actualWidth, actualHeight); - int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth, - actualHeight, actualWidth); - - // Decode to the nearest power of two scaling factor. - decodeOptions.inJustDecodeBounds = false; - // TODO(ficus): Do we need this or is it okay since API 8 doesn't support it? - // decodeOptions.inPreferQualityOverSpeed = PREFER_QUALITY_OVER_SPEED; - decodeOptions.inSampleSize = - findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight); - Bitmap tempBitmap = - BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions); - - // Disallow dos by checking the size of the tempBitmap, otherwise the bitmap - // constructor will throw an IllegalArgumentException. - if (tempBitmap != null && - (tempBitmap.getWidth() == 0 || tempBitmap.getHeight() == 0)) { - bitmap = null; - tempBitmap.recycle(); - } else if (tempBitmap != null && (tempBitmap.getWidth() > desiredWidth || - tempBitmap.getHeight() > desiredHeight)) { - // If necessary, scale down to the maximal acceptable size. - bitmap = Bitmap.createScaledBitmap(tempBitmap, - desiredWidth, desiredHeight, true); - tempBitmap.recycle(); - } else { - bitmap = tempBitmap; - } - } - - if (bitmap == null) { - return Response.error(new ParseError(response)); - } else { - return Response.success(bitmap, HttpHeaderParser.parseCacheHeaders(response)); - } - } - - @Override - protected void deliverResponse(Bitmap response) { - mListener.onResponse(response); - } - - /** - * Returns the largest power-of-two divisor for use in downscaling a bitmap - * that will not result in the scaling past the desired dimensions. - * - * @param actualWidth Actual width of the bitmap - * @param actualHeight Actual height of the bitmap - * @param desiredWidth Desired width of the bitmap - * @param desiredHeight Desired height of the bitmap - */ - // Visible for testing. - static int findBestSampleSize( - int actualWidth, int actualHeight, int desiredWidth, int desiredHeight) { - double wr = (double) actualWidth / desiredWidth; - double hr = (double) actualHeight / desiredHeight; - double ratio = Math.min(wr, hr); - float n = 1.0f; - while ((n * 2) <= ratio) { - n *= 2; - } - - return (int) n; - } -} diff --git a/Clover/app/src/main/java/com/android/volley/toolbox/JsonArrayRequest.java b/Clover/app/src/main/java/com/android/volley/toolbox/JsonArrayRequest.java deleted file mode 100644 index b1eae805..00000000 --- a/Clover/app/src/main/java/com/android/volley/toolbox/JsonArrayRequest.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley.toolbox; - -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. - */ -public class JsonArrayRequest extends JsonRequest { - - /** - * Creates a new request. - * @param url URL to fetch the JSON from - * @param listener Listener to receive the JSON response - * @param errorListener Error listener, or null to ignore errors. - */ - public JsonArrayRequest(String url, Listener listener, ErrorListener errorListener) { - super(Method.GET, url, null, listener, errorListener); - } - - @Override - protected Response parseNetworkResponse(NetworkResponse response) { - try { - String jsonString = - new String(response.data, HttpHeaderParser.parseCharset(response.headers)); - return Response.success(new JSONArray(jsonString), - HttpHeaderParser.parseCacheHeaders(response)); - } catch (UnsupportedEncodingException e) { - return Response.error(new ParseError(e)); - } catch (JSONException je) { - return Response.error(new ParseError(je)); - } - } -} diff --git a/Clover/app/src/main/java/com/android/volley/toolbox/JsonObjectRequest.java b/Clover/app/src/main/java/com/android/volley/toolbox/JsonObjectRequest.java deleted file mode 100644 index 74821cba..00000000 --- a/Clover/app/src/main/java/com/android/volley/toolbox/JsonObjectRequest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley.toolbox; - -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. - */ -public class JsonObjectRequest extends JsonRequest { - - /** - * Creates a new request. - * @param method the HTTP method to use - * @param url URL to fetch the JSON from - * @param jsonRequest A {@link JSONObject} to post with the request. Null is allowed and - * indicates no parameters will be posted along with request. - * @param listener Listener to receive the JSON response - * @param errorListener Error listener, or null to ignore errors. - */ - public JsonObjectRequest(int method, String url, JSONObject jsonRequest, - Listener listener, ErrorListener errorListener) { - super(method, url, (jsonRequest == null) ? null : jsonRequest.toString(), listener, - errorListener); - } - - /** - * Constructor which defaults to GET if jsonRequest is - * null, POST otherwise. - * - * @see #JsonObjectRequest(int, String, JSONObject, Listener, ErrorListener) - */ - public JsonObjectRequest(String url, JSONObject jsonRequest, Listener listener, - ErrorListener errorListener) { - this(jsonRequest == null ? Method.GET : Method.POST, url, jsonRequest, - listener, errorListener); - } - - @Override - protected Response parseNetworkResponse(NetworkResponse response) { - try { - String jsonString = - new String(response.data, HttpHeaderParser.parseCharset(response.headers)); - return Response.success(new JSONObject(jsonString), - HttpHeaderParser.parseCacheHeaders(response)); - } catch (UnsupportedEncodingException e) { - return Response.error(new ParseError(e)); - } catch (JSONException je) { - return Response.error(new ParseError(je)); - } - } -} diff --git a/Clover/app/src/main/java/com/android/volley/toolbox/JsonRequest.java b/Clover/app/src/main/java/com/android/volley/toolbox/JsonRequest.java deleted file mode 100644 index f11ac14e..00000000 --- a/Clover/app/src/main/java/com/android/volley/toolbox/JsonRequest.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley.toolbox; - -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 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. - * - * @param JSON type of response expected - */ -public abstract class JsonRequest extends Request { - /** Charset for request. */ - private static final String PROTOCOL_CHARSET = "utf-8"; - - /** Content type for request. */ - private static final String PROTOCOL_CONTENT_TYPE = - String.format("application/json; charset=%s", PROTOCOL_CHARSET); - - private final Listener mListener; - private final String mRequestBody; - - /** - * Deprecated constructor for a JsonRequest which defaults to GET unless {@link #getPostBody()} - * or {@link #getPostParams()} is overridden (which defaults to POST). - * - * @deprecated Use {@link #JsonRequest(int, String, String, Listener, ErrorListener)}. - */ - public JsonRequest(String url, String requestBody, Listener listener, - ErrorListener errorListener) { - this(Method.DEPRECATED_GET_OR_POST, url, requestBody, listener, errorListener); - } - - public JsonRequest(int method, String url, String requestBody, Listener listener, - ErrorListener errorListener) { - super(method, url, errorListener); - mListener = listener; - mRequestBody = requestBody; - } - - @Override - protected void deliverResponse(T response) { - mListener.onResponse(response); - } - - @Override - abstract protected Response parseNetworkResponse(NetworkResponse response); - - /** - * @deprecated Use {@link #getBodyContentType()}. - */ - @Override - public String getPostBodyContentType() { - return getBodyContentType(); - } - - /** - * @deprecated Use {@link #getBody()}. - */ - @Override - public byte[] getPostBody() { - return getBody(); - } - - @Override - public String getBodyContentType() { - return PROTOCOL_CONTENT_TYPE; - } - - @Override - public byte[] getBody() { - try { - return mRequestBody == null ? null : mRequestBody.getBytes(PROTOCOL_CHARSET); - } catch (UnsupportedEncodingException uee) { - VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s", - mRequestBody, PROTOCOL_CHARSET); - return null; - } - } -} diff --git a/Clover/app/src/main/java/com/android/volley/toolbox/NetworkImageView.java b/Clover/app/src/main/java/com/android/volley/toolbox/NetworkImageView.java deleted file mode 100644 index 692e9885..00000000 --- a/Clover/app/src/main/java/com/android/volley/toolbox/NetworkImageView.java +++ /dev/null @@ -1,219 +0,0 @@ -/** - * 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 com.android.volley.toolbox; - -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.ImageContainer; -import com.android.volley.toolbox.ImageLoader.ImageListener; - -/** - * Handles fetching an image from a URL as well as the life-cycle of the - * associated request. - */ -public class NetworkImageView 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; - - public NetworkImageView(Context context) { - this(context, null); - } - - public NetworkImageView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public NetworkImageView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - /** - * 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 NetworkImageView#setDefaultImageResId(int)} on the view. - * - * NOTE: If applicable, {@link NetworkImageView#setDefaultImageResId(int)} and - * {@link NetworkImageView#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; - } - - /** - * 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); - } - } - - @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()); - } else if (mDefaultImageId != 0) { - setImageResource(mDefaultImageId); - } - } - }, maxWidth, maxHeight); - - // 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/Clover/app/src/main/java/com/android/volley/toolbox/NoCache.java b/Clover/app/src/main/java/com/android/volley/toolbox/NoCache.java deleted file mode 100644 index ab662543..00000000 --- a/Clover/app/src/main/java/com/android/volley/toolbox/NoCache.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley.toolbox; - -import com.android.volley.Cache; - -/** - * A cache that doesn't. - */ -public class NoCache implements Cache { - @Override - public void clear() { - } - - @Override - public Entry get(String key) { - return null; - } - - @Override - public void put(String key, Entry entry) { - } - - @Override - public void invalidate(String key, boolean fullExpire) { - } - - @Override - public void remove(String key) { - } - - @Override - public void initialize() { - } -} diff --git a/Clover/app/src/main/java/com/android/volley/toolbox/PoolingByteArrayOutputStream.java b/Clover/app/src/main/java/com/android/volley/toolbox/PoolingByteArrayOutputStream.java deleted file mode 100644 index d082e84a..00000000 --- a/Clover/app/src/main/java/com/android/volley/toolbox/PoolingByteArrayOutputStream.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2012 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 com.android.volley.toolbox; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -/** - * A variation of {@link java.io.ByteArrayOutputStream} that uses a pool of byte[] buffers instead - * of always allocating them fresh, saving on heap churn. - */ -public class PoolingByteArrayOutputStream extends ByteArrayOutputStream { - /** - * If the {@link #PoolingByteArrayOutputStream(ByteArrayPool)} constructor is called, this is - * the default size to which the underlying byte array is initialized. - */ - private static final int DEFAULT_SIZE = 256; - - private final ByteArrayPool mPool; - - /** - * Constructs a new PoolingByteArrayOutputStream with a default size. If more bytes are written - * to this instance, the underlying byte array will expand. - */ - public PoolingByteArrayOutputStream(ByteArrayPool pool) { - this(pool, DEFAULT_SIZE); - } - - /** - * Constructs a new {@code ByteArrayOutputStream} with a default size of {@code size} bytes. If - * more than {@code size} bytes are written to this instance, the underlying byte array will - * expand. - * - * @param size initial size for the underlying byte array. The value will be pinned to a default - * minimum size. - */ - public PoolingByteArrayOutputStream(ByteArrayPool pool, int size) { - mPool = pool; - buf = mPool.getBuf(Math.max(size, DEFAULT_SIZE)); - } - - @Override - public void close() throws IOException { - mPool.returnBuf(buf); - buf = null; - super.close(); - } - - @Override - public void finalize() throws Throwable { - mPool.returnBuf(buf); - super.finalize(); - } - - /** - * Ensures there is enough space in the buffer for the given number of additional bytes. - */ - private void expand(int i) { - /* Can the buffer handle @i more bytes, if not expand it */ - if (count + i <= buf.length) { - return; - } - byte[] newbuf = mPool.getBuf((count + i) * 2); - System.arraycopy(buf, 0, newbuf, 0, count); - mPool.returnBuf(buf); - buf = newbuf; - } - - @Override - public synchronized void write(byte[] buffer, int offset, int len) { - expand(len); - super.write(buffer, offset, len); - } - - @Override - public synchronized void write(int oneByte) { - expand(1); - super.write(oneByte); - } -} diff --git a/Clover/app/src/main/java/com/android/volley/toolbox/RequestFuture.java b/Clover/app/src/main/java/com/android/volley/toolbox/RequestFuture.java deleted file mode 100644 index 173c44cc..00000000 --- a/Clover/app/src/main/java/com/android/volley/toolbox/RequestFuture.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) 2011 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 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; - -/** - * A Future that represents a Volley request. - * - * Used by providing as your response and error listeners. For example: - *
- * RequestFuture<JSONObject> future = RequestFuture.newFuture();
- * MyRequest request = new MyRequest(URL, future, future);
- *
- * // If you want to be able to cancel the request:
- * future.setRequest(requestQueue.add(request));
- *
- * // Otherwise:
- * requestQueue.add(request);
- *
- * try {
- *   JSONObject response = future.get();
- *   // do something with response
- * } catch (InterruptedException e) {
- *   // handle the error
- * } catch (ExecutionException e) {
- *   // handle the error
- * }
- * 
- * - * @param The type of parsed response this future expects. - */ -public class RequestFuture implements Future, Response.Listener, - Response.ErrorListener { - private Request mRequest; - private boolean mResultReceived = false; - private T mResult; - private VolleyError mException; - - public static RequestFuture newFuture() { - return new RequestFuture(); - } - - private RequestFuture() {} - - public void setRequest(Request request) { - mRequest = request; - } - - @Override - public synchronized boolean cancel(boolean mayInterruptIfRunning) { - if (mRequest == null) { - return false; - } - - if (!isDone()) { - mRequest.cancel(); - return true; - } else { - return false; - } - } - - @Override - public T get() throws InterruptedException, ExecutionException { - try { - return doGet(null); - } catch (TimeoutException e) { - throw new AssertionError(e); - } - } - - @Override - public T get(long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, TimeoutException { - return doGet(TimeUnit.MILLISECONDS.convert(timeout, unit)); - } - - private synchronized T doGet(Long timeoutMs) - throws InterruptedException, ExecutionException, TimeoutException { - if (mException != null) { - throw new ExecutionException(mException); - } - - if (mResultReceived) { - return mResult; - } - - if (timeoutMs == null) { - wait(0); - } else if (timeoutMs > 0) { - wait(timeoutMs); - } - - if (mException != null) { - throw new ExecutionException(mException); - } - - if (!mResultReceived) { - throw new TimeoutException(); - } - - return mResult; - } - - @Override - public boolean isCancelled() { - if (mRequest == null) { - return false; - } - return mRequest.isCanceled(); - } - - @Override - public synchronized boolean isDone() { - return mResultReceived || mException != null || isCancelled(); - } - - @Override - public synchronized void onResponse(T response) { - mResultReceived = true; - mResult = response; - notifyAll(); - } - - @Override - public synchronized void onErrorResponse(VolleyError error) { - mException = error; - notifyAll(); - } -} - diff --git a/Clover/app/src/main/java/com/android/volley/toolbox/StringRequest.java b/Clover/app/src/main/java/com/android/volley/toolbox/StringRequest.java deleted file mode 100644 index 6b3dfcf8..00000000 --- a/Clover/app/src/main/java/com/android/volley/toolbox/StringRequest.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.volley.toolbox; - -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. - */ -public class StringRequest extends Request { - private final Listener mListener; - - /** - * Creates a new request with the given method. - * - * @param method the request {@link Method} to use - * @param url URL to fetch the string at - * @param listener Listener to receive the String response - * @param errorListener Error listener, or null to ignore errors - */ - public StringRequest(int method, String url, Listener listener, - ErrorListener errorListener) { - super(method, url, errorListener); - mListener = listener; - } - - /** - * Creates a new GET request. - * - * @param url URL to fetch the string at - * @param listener Listener to receive the String response - * @param errorListener Error listener, or null to ignore errors - */ - public StringRequest(String url, Listener listener, ErrorListener errorListener) { - this(Method.GET, url, listener, errorListener); - } - - @Override - protected void deliverResponse(String response) { - mListener.onResponse(response); - } - - @Override - protected Response parseNetworkResponse(NetworkResponse response) { - String parsed; - try { - parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers)); - } catch (UnsupportedEncodingException e) { - parsed = new String(response.data); - } - return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response)); - } -} diff --git a/Clover/app/src/main/java/com/android/volley/toolbox/Volley.java b/Clover/app/src/main/java/com/android/volley/toolbox/Volley.java deleted file mode 100644 index b840c6d1..00000000 --- a/Clover/app/src/main/java/com/android/volley/toolbox/Volley.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2012 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 com.android.volley.toolbox; - -import android.content.Context; -import android.net.http.AndroidHttpClient; -import android.os.Build; - -import com.android.volley.Network; -import com.android.volley.RequestQueue; -import com.android.volley.compat.NoSSLv3Compat; - -import java.io.File; - -public class Volley { - - /** Default on-disk cache directory. */ - public static final String DEFAULT_CACHE_DIR = "volley"; - - public static RequestQueue newRequestQueue(Context context, String userAgent, HttpStack stack, File cacheDir, int diskCacheSize) { - if (stack == null) { - if (Build.VERSION.SDK_INT >= 9) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - // Use a socket factory that removes sslv3 - stack = new HurlStack(userAgent, null, new NoSSLv3Compat.NoSSLv3Factory()); - } else { - stack = new HurlStack(userAgent); - } - } else { - // Prior to Gingerbread, HttpUrlConnection was unreliable. - // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html - stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent)); - } - } - - Network network = new BasicNetwork(stack); - - DiskBasedCache diskCache = diskCacheSize < 0 ? new DiskBasedCache(cacheDir) : new DiskBasedCache(cacheDir, diskCacheSize); - RequestQueue queue = new RequestQueue(diskCache, network); - queue.start(); - - return queue; - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/core/di/NetModule.java b/Clover/app/src/main/java/org/floens/chan/core/di/NetModule.java index 42fa7273..bb815628 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/di/NetModule.java +++ b/Clover/app/src/main/java/org/floens/chan/core/di/NetModule.java @@ -14,20 +14,14 @@ import java.io.File; import javax.inject.Singleton; public class NetModule { - private static final int VOLLEY_CACHE_SIZE = 10 * 1024 * 1024; private static final long FILE_CACHE_DISK_SIZE = 50 * 1024 * 1024; private static final String FILE_CACHE_NAME = "filecache"; @Provides @Singleton public RequestQueue provideRequestQueue(Context applicationContext, UserAgentProvider userAgentProvider) { - File cacheDir = getCacheDir(applicationContext); - String userAgent = userAgentProvider.getUserAgent(); - return Volley.newRequestQueue(applicationContext, - userAgent, - new ProxiedHurlStack(userAgent), - new File(cacheDir, Volley.DEFAULT_CACHE_DIR), VOLLEY_CACHE_SIZE); + return Volley.newRequestQueue(applicationContext, new ProxiedHurlStack(userAgent)); } @Provides diff --git a/Clover/app/src/main/java/org/floens/chan/core/net/ProxiedHurlStack.java b/Clover/app/src/main/java/org/floens/chan/core/net/ProxiedHurlStack.java index 7d57c2e3..1feb8b25 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/net/ProxiedHurlStack.java +++ b/Clover/app/src/main/java/org/floens/chan/core/net/ProxiedHurlStack.java @@ -27,18 +27,32 @@ import java.net.Proxy; import java.net.URL; public class ProxiedHurlStack extends HurlStack { + private final String userAgent; + public ProxiedHurlStack(String userAgent) { - super(userAgent, null); + super(); + this.userAgent = userAgent; } @Override protected HttpURLConnection createConnection(URL url) throws IOException { // Start the connection by specifying a proxy server Proxy proxy = ChanSettings.getProxy(); + HttpURLConnection connection; if (proxy != null) { - return (HttpURLConnection) url.openConnection(proxy); + connection = (HttpURLConnection) url.openConnection(proxy); } else { - return (HttpURLConnection) url.openConnection(); + connection = (HttpURLConnection) url.openConnection(); } + + // Use the same workaround as described in Volley's HurlStack: + // Workaround for the M release HttpURLConnection not observing the + // HttpURLConnection.setFollowRedirects() property. + // https://code.google.com/p/android/issues/detail?id=194495 + connection.setInstanceFollowRedirects(HttpURLConnection.getFollowRedirects()); + + connection.setRequestProperty("User-Agent", userAgent); + + return connection; } } From 327489f844874c1a84a0a559154abcfa9319883b Mon Sep 17 00:00:00 2001 From: Floens Date: Mon, 22 Apr 2019 13:39:58 +0200 Subject: [PATCH 3/9] watcher: use MessagingStyle for notifications. Use notification channels. Stop using a foreground service. --- .../chan/core/manager/WatchManager.java | 108 +++++++++-- .../ui/notification/NotificationHelper.java | 16 ++ .../ThreadWatchNotifications.java | 180 ++++++++++++++++++ .../floens/chan/ui/service/WatchNotifier.java | 87 +++++---- 4 files changed, 332 insertions(+), 59 deletions(-) create mode 100644 Clover/app/src/main/java/org/floens/chan/ui/notification/NotificationHelper.java create mode 100644 Clover/app/src/main/java/org/floens/chan/ui/notification/ThreadWatchNotifications.java diff --git a/Clover/app/src/main/java/org/floens/chan/core/manager/WatchManager.java b/Clover/app/src/main/java/org/floens/chan/core/manager/WatchManager.java index 9ee4a05b..d3104f13 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/manager/WatchManager.java +++ b/Clover/app/src/main/java/org/floens/chan/core/manager/WatchManager.java @@ -21,11 +21,16 @@ import android.app.AlarmManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.graphics.Bitmap; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.PowerManager; import android.support.annotation.Nullable; +import android.text.TextUtils; + +import com.android.volley.VolleyError; +import com.android.volley.toolbox.ImageLoader; import org.floens.chan.Chan; import org.floens.chan.core.database.DatabaseManager; @@ -40,7 +45,7 @@ import org.floens.chan.core.pool.ChanLoaderFactory; import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.core.site.loader.ChanThreadLoader; import org.floens.chan.ui.helper.PostHelper; -import org.floens.chan.ui.service.WatchNotifier; +import org.floens.chan.ui.notification.ThreadWatchNotifications; import org.floens.chan.utils.Logger; import java.util.ArrayList; @@ -59,6 +64,7 @@ import javax.inject.Singleton; import de.greenrobot.event.EventBus; import static org.floens.chan.Chan.inject; +import static org.floens.chan.Chan.injector; import static org.floens.chan.utils.AndroidUtils.getAppContext; /** @@ -103,7 +109,7 @@ public class WatchManager { private static final int MESSAGE_UPDATE = 1; private static final int REQUEST_CODE_WATCH_UPDATE = 2; private static final String WATCHER_UPDATE_ACTION = "org.floens.chan.intent.action.WATCHER_UPDATE"; - private static final String WAKELOCK_TAG = "WatchManagerUpdateLock"; + private static final String WAKELOCK_TAG = "org.floens.chan:watch_manager_update_lock"; private static final long WAKELOCK_MAX_TIME = 60 * 1000; private static final long BACKGROUND_UPDATE_MIN_DELAY = 90 * 1000; @@ -133,13 +139,18 @@ public class WatchManager { private PowerManager.WakeLock wakeLock; private long lastBackgroundUpdateTime; + private ThreadWatchNotifications threadWatchNotifications; + @Inject - public WatchManager(DatabaseManager databaseManager, ChanLoaderFactory chanLoaderFactory) { - alarmManager = (AlarmManager) getAppContext().getSystemService(Context.ALARM_SERVICE); - powerManager = (PowerManager) getAppContext().getSystemService(Context.POWER_SERVICE); + public WatchManager(Context applicationContext, + DatabaseManager databaseManager, ChanLoaderFactory chanLoaderFactory, + ThreadWatchNotifications threadWatchNotifications) { + alarmManager = (AlarmManager) applicationContext.getSystemService(Context.ALARM_SERVICE); + powerManager = (PowerManager) applicationContext.getSystemService(Context.POWER_SERVICE); this.databaseManager = databaseManager; this.chanLoaderFactory = chanLoaderFactory; + this.threadWatchNotifications = threadWatchNotifications; databasePinManager = databaseManager.getDatabasePinManager(); pins = databaseManager.runTask(databasePinManager.getPins()); @@ -543,10 +554,18 @@ public class WatchManager { // Update notification state if (watchEnabled && backgroundEnabled) { - // Also calls onStartCommand, which updates the notification with new info - getAppContext().startService(new Intent(getAppContext(), WatchNotifier.class)); + // Show/update notification + List pinWatchers = new ArrayList<>(); + for (Pin watchingPin : getWatchingPins()) { + PinWatcher pinWatcher = getPinWatcher(watchingPin); + if (pinWatcher != null && !watchingPin.isError) { + pinWatchers.add(pinWatcher); + } + } + + threadWatchNotifications.showForWatchers(pinWatchers); } else { - getAppContext().stopService(new Intent(getAppContext(), WatchNotifier.class)); + threadWatchNotifications.hide(); } } @@ -668,9 +687,12 @@ public class WatchManager { } } - public class PinWatcher implements ChanThreadLoader.ChanLoaderCallback { + public class PinWatcher implements ChanThreadLoader.ChanLoaderCallback, ImageLoader.ImageListener { private static final String TAG = "PinWatcher"; + // Width and height of the bitmap for the notification image. + private static final int THUMBNAIL_SIZE = 128; + private final Pin pin; private ChanThreadLoader chanLoader; @@ -679,6 +701,11 @@ public class WatchManager { private boolean wereNewQuotes = false; private boolean wereNewPosts = false; + private boolean requireNotificationUpdate = true; + + private Bitmap thumbnailBitmap = null; + private ImageLoader.ImageContainer thumbnailContainer; + public PinWatcher(Pin pin) { this.pin = pin; inject(this); @@ -687,6 +714,27 @@ public class WatchManager { chanLoader = chanLoaderFactory.obtain(pin.loadable, this); } + public int getPinId() { + return pin.id; + } + + public String getTitle() { + return pin.loadable.title; + } + + public boolean requiresNotificationUpdate() { + return requireNotificationUpdate; + } + + public void hadNotificationUpdate() { + requireNotificationUpdate = false; + } + + @Nullable + public Bitmap getThumbnailBitmap() { + return thumbnailBitmap; + } + public List getUnviewedPosts() { if (posts.size() == 0) { return posts; @@ -728,10 +776,13 @@ public class WatchManager { private void onViewed() { wereNewPosts = false; wereNewQuotes = false; + requireNotificationUpdate = true; } private boolean update(boolean fromBackground) { if (!pin.isError && pin.watching) { + loadThumbnailBitmapIfNeeded(); + if (fromBackground) { // Always load regardless of timer, since the time left is not accurate for 15min+ intervals chanLoader.clearTimer(); @@ -774,20 +825,20 @@ public class WatchManager { quotes.clear(); // Get list of saved replies from this thread - List savedReplies = new ArrayList<>(); + Set savedReplies = new HashSet<>(); for (Post item : thread.posts) { - // saved.title = pin.loadable.title; - if (item.isSavedReply) { - savedReplies.add(item); + savedReplies.add(item.no); } } // Now get a list of posts that have a quote to a saved reply + out: for (Post post : thread.posts) { - for (Post saved : savedReplies) { - if (post.repliesTo.contains(saved.no)) { + for (Integer no : post.repliesTo) { + if (savedReplies.contains(no)) { quotes.add(post); + continue out; } } } @@ -801,6 +852,7 @@ public class WatchManager { if (isFirstLoad) { pin.watchLastCount = posts.size(); pin.quoteLastCount = quotes.size(); + requireNotificationUpdate = true; } pin.watchNewCount = posts.size(); @@ -810,11 +862,13 @@ public class WatchManager { // There were new posts after processing if (pin.watchNewCount > lastWatchNewCount) { wereNewPosts = true; + requireNotificationUpdate = true; } // There were new quotes after processing if (pin.quoteNewCount > lastQuoteNewCount) { wereNewQuotes = true; + requireNotificationUpdate = true; } } @@ -828,9 +882,33 @@ public class WatchManager { if (thread.archived || thread.closed) { pin.archived = true; pin.watching = false; + requireNotificationUpdate = true; } pinWatcherUpdated(this); + + loadThumbnailBitmapIfNeeded(); + } + + private void loadThumbnailBitmapIfNeeded() { + if (TextUtils.isEmpty(pin.thumbnailUrl) || thumbnailContainer != null) { + return; + } + + thumbnailContainer = injector().instance(ImageLoader.class) + .get(pin.thumbnailUrl, this, + THUMBNAIL_SIZE, THUMBNAIL_SIZE); + } + + @Override + public void onResponse(ImageLoader.ImageContainer response, boolean isImmediate) { + if (response.getBitmap() != null) { + thumbnailBitmap = response.getBitmap(); + } + } + + @Override + public void onErrorResponse(VolleyError error) { } } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/notification/NotificationHelper.java b/Clover/app/src/main/java/org/floens/chan/ui/notification/NotificationHelper.java new file mode 100644 index 00000000..3d2c198d --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/ui/notification/NotificationHelper.java @@ -0,0 +1,16 @@ +package org.floens.chan.ui.notification; + +import android.app.NotificationManager; +import android.content.Context; + +public class NotificationHelper { + protected final Context applicationContext; + protected final NotificationManager notificationManager; + + public NotificationHelper(Context applicationContext) { + this.applicationContext = applicationContext; + + notificationManager = (NotificationManager) applicationContext. + getSystemService(Context.NOTIFICATION_SERVICE); + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/notification/ThreadWatchNotifications.java b/Clover/app/src/main/java/org/floens/chan/ui/notification/ThreadWatchNotifications.java new file mode 100644 index 00000000..c4f12cfb --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/ui/notification/ThreadWatchNotifications.java @@ -0,0 +1,180 @@ +package org.floens.chan.ui.notification; + +import android.annotation.TargetApi; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.support.v4.app.NotificationCompat; + +import org.floens.chan.R; +import org.floens.chan.core.manager.WatchManager; +import org.floens.chan.core.model.Post; +import org.floens.chan.ui.activity.BoardActivity; + +import java.util.List; +import java.util.regex.Pattern; + +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton +public class ThreadWatchNotifications extends NotificationHelper { + private static final String CHANNEL_ID_WATCH_NORMAL = "watch:normal"; + private static final String CHANNEL_ID_WATCH_MENTION = "watch:mention"; + private static final int NOTIFICATION_ID_WATCH_NORMAL = 0x10000; + private static final int NOTIFICATION_ID_WATCH_NORMAL_MASK = 0xffff; + private static final int NOTIFICATION_ID_WATCH_MENTION = 0x20000; + private static final int NOTIFICATION_ID_WATCH_MENTION_MASK = 0xffff; + + private static final String POST_COMMENT_IMAGE_PREFIX = "(img) "; + private static final Pattern POST_COMMENT_SHORTEN_NO_PATTERN = + Pattern.compile(">>\\d+(?=\\d{4})(\\d{4})"); + + @Inject + public ThreadWatchNotifications(Context applicationContext) { + super(applicationContext); + } + + public void showForWatchers(List pinWatchers) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + showPinSummaries(pinWatchers); + } else { + // legacy notification + } + } + + public void hide() { + + } + + @TargetApi(Build.VERSION_CODES.O) + private void showPinSummaries(List pinWatchers) { + ensureChannels(); + + for (WatchManager.PinWatcher pinWatcher : pinWatchers) { + if (!pinWatcher.requiresNotificationUpdate()) { + continue; + } + + // Normal thread posts. + if (!pinWatcher.getUnviewedPosts().isEmpty()) { + NotificationCompat.Builder builder = + new NotificationCompat.Builder(applicationContext, + CHANNEL_ID_WATCH_NORMAL); + + NotificationCompat.MessagingStyle messagingStyle = + new NotificationCompat.MessagingStyle(""); + + builder.setSmallIcon(R.drawable.ic_stat_notify); + if (pinWatcher.getThumbnailBitmap() != null) { + builder.setLargeIcon(pinWatcher.getThumbnailBitmap()); + } + + String subTitle = "(" + pinWatcher.getUnviewedPosts().size() + ")"; + messagingStyle.setConversationTitle(pinWatcher.getTitle() + " " + subTitle); + messagingStyle.setGroupConversation(true); + addPostsToMessagingStyle(messagingStyle, pinWatcher.getUnviewedPosts()); + builder.setStyle(messagingStyle); + + setNotificationIntent(builder); + + int id = NOTIFICATION_ID_WATCH_NORMAL + + (pinWatcher.getPinId() & NOTIFICATION_ID_WATCH_NORMAL_MASK); + notificationManager.notify(id, builder.build()); + } + + // Posts that mention you. + if (!pinWatcher.getUnviewedQuotes().isEmpty()) { + NotificationCompat.Builder builder = + new NotificationCompat.Builder(applicationContext, + CHANNEL_ID_WATCH_MENTION); + + NotificationCompat.MessagingStyle messagingStyle = + new NotificationCompat.MessagingStyle(""); + + builder.setSmallIcon(R.drawable.ic_stat_notify_alert); + builder.setSubText("Mentions"); + if (pinWatcher.getThumbnailBitmap() != null) { + builder.setLargeIcon(pinWatcher.getThumbnailBitmap()); + } + + String subTitle = "(" + pinWatcher.getUnviewedQuotes().size() + " mentions)"; + messagingStyle.setConversationTitle(pinWatcher.getTitle() + " " + subTitle); + messagingStyle.setGroupConversation(true); + addPostsToMessagingStyle(messagingStyle, pinWatcher.getUnviewedQuotes()); + builder.setStyle(messagingStyle); + + setNotificationIntent(builder); + + int id = NOTIFICATION_ID_WATCH_MENTION + + (pinWatcher.getPinId() & NOTIFICATION_ID_WATCH_MENTION_MASK); + notificationManager.notify(id, builder.build()); + } + + pinWatcher.hadNotificationUpdate(); + } + } + + private void addPostsToMessagingStyle(NotificationCompat.MessagingStyle messagingStyle, + List unviewedPosts) { + final int maxLines = 25; + + if (unviewedPosts.size() > maxLines) { + unviewedPosts = unviewedPosts.subList( + unviewedPosts.size() - maxLines, unviewedPosts.size()); + } + + for (Post post : unviewedPosts) { + String comment = post.image() != null ? POST_COMMENT_IMAGE_PREFIX : ""; + if (post.comment.length() > 0) { + comment += post.comment; + } + + // Replace >>132456798 with >6789 to shorten the notification + comment = POST_COMMENT_SHORTEN_NO_PATTERN.matcher(comment) + .replaceAll(">$1"); + + CharSequence name = post.nameTripcodeIdCapcodeSpan; +// if (name.length() == 0) { +// name = "Anonymous"; +// } + messagingStyle.addMessage(comment, post.time, name); + } + } + + @TargetApi(Build.VERSION_CODES.O) + private void setNotificationIntent(NotificationCompat.Builder builder) { + Intent intent = new Intent(applicationContext, BoardActivity.class); + intent.setAction(Intent.ACTION_MAIN); + intent.addCategory(Intent.CATEGORY_LAUNCHER); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | + Intent.FLAG_ACTIVITY_SINGLE_TOP | + Intent.FLAG_ACTIVITY_NEW_TASK | + Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + + PendingIntent pendingIntent = PendingIntent.getActivity( + applicationContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + + builder.setContentIntent(pendingIntent); + } + + @TargetApi(Build.VERSION_CODES.O) + private void ensureChannels() { + NotificationChannel normalChannel = new NotificationChannel( + CHANNEL_ID_WATCH_NORMAL, "Thread updates", + NotificationManager.IMPORTANCE_DEFAULT); + normalChannel.setDescription("Normal posts for threads"); + notificationManager.createNotificationChannel(normalChannel); + + NotificationChannel mentionChannel = new NotificationChannel( + CHANNEL_ID_WATCH_MENTION, "Thread mentions", + NotificationManager.IMPORTANCE_HIGH); + mentionChannel.setDescription("Posts were you were mentioned"); + mentionChannel.enableVibration(true); + mentionChannel.enableLights(true); + notificationManager.createNotificationChannel(mentionChannel); + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/service/WatchNotifier.java b/Clover/app/src/main/java/org/floens/chan/ui/service/WatchNotifier.java index dbc3eb7c..a52e8514 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/service/WatchNotifier.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/service/WatchNotifier.java @@ -24,6 +24,7 @@ import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.IBinder; +import android.support.annotation.Nullable; import android.support.v4.app.NotificationCompat; import android.text.TextUtils; @@ -69,8 +70,6 @@ public class WatchNotifier extends Service { inject(this); nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - - startForeground(NOTIFICATION_ID, createNotification()); } @Override @@ -91,13 +90,17 @@ public class WatchNotifier extends Service { } public void updateNotification() { - nm.notify(NOTIFICATION_ID, createNotification()); + Notification notification = createNotification(); + if (notification != null) { + nm.notify(NOTIFICATION_ID, notification); + } } public void pausePins() { watchManager.pauseAll(); } + @Nullable private Notification createNotification() { boolean notifyQuotesOnly = ChanSettings.watchNotifyMode.get().equals("quotes"); boolean soundQuotesOnly = ChanSettings.watchSound.get().equals("quotes"); @@ -162,59 +165,55 @@ public class WatchNotifier extends Service { return notifyAboutPosts(pins, subjectPins, unviewedPosts, listQuoting, notifyQuotesOnly, light, sound, peek); } + @Nullable private Notification notifyAboutPosts(List pins, List subjectPins, List unviewedPosts, List listQuoting, boolean notifyQuotesOnly, boolean light, boolean sound, boolean peek) { - String title = getResources().getQuantityString(R.plurals.watch_title, pins.size(), pins.size()); - if (unviewedPosts.isEmpty()) { - // Idle notification - String message = getString(R.string.watch_idle); - return get(title, message, null, false, false, false, false, null); + return null; + } + // New posts notification + String message; + List postsForExpandedLines; + if (notifyQuotesOnly) { + message = getResources().getQuantityString(R.plurals.watch_new_quotes, listQuoting.size(), listQuoting.size()); + postsForExpandedLines = listQuoting; } else { - // New posts notification - String message; - List postsForExpandedLines; - if (notifyQuotesOnly) { - message = getResources().getQuantityString(R.plurals.watch_new_quotes, listQuoting.size(), listQuoting.size()); - postsForExpandedLines = listQuoting; + postsForExpandedLines = unviewedPosts; + if (listQuoting.size() > 0) { + message = getResources().getQuantityString(R.plurals.watch_new_quoting, unviewedPosts.size(), unviewedPosts.size(), listQuoting.size()); } else { - postsForExpandedLines = unviewedPosts; - if (listQuoting.size() > 0) { - message = getResources().getQuantityString(R.plurals.watch_new_quoting, unviewedPosts.size(), unviewedPosts.size(), listQuoting.size()); - } else { - message = getResources().getQuantityString(R.plurals.watch_new, unviewedPosts.size(), unviewedPosts.size()); - } + message = getResources().getQuantityString(R.plurals.watch_new, unviewedPosts.size(), unviewedPosts.size()); } + } - Collections.sort(postsForExpandedLines, POST_AGE_COMPARATOR); - List expandedLines = new ArrayList<>(); - for (Post postForExpandedLine : postsForExpandedLines) { - CharSequence prefix; - if (postForExpandedLine.getTitle().length() <= SUBJECT_LENGTH) { - prefix = postForExpandedLine.getTitle(); - } else { - prefix = postForExpandedLine.getTitle().subSequence(0, SUBJECT_LENGTH); - } - - String comment = postForExpandedLine.image() != null ? IMAGE_TEXT : ""; - if (postForExpandedLine.comment.length() > 0) { - comment += postForExpandedLine.comment; - } - - // Replace >>132456798 with >789 to shorten the notification - comment = SHORTEN_NO_PATTERN.matcher(comment).replaceAll(">$1"); - - expandedLines.add(prefix + ": " + comment); + Collections.sort(postsForExpandedLines, POST_AGE_COMPARATOR); + List expandedLines = new ArrayList<>(); + for (Post postForExpandedLine : postsForExpandedLines) { + CharSequence prefix; + if (postForExpandedLine.getTitle().length() <= SUBJECT_LENGTH) { + prefix = postForExpandedLine.getTitle(); + } else { + prefix = postForExpandedLine.getTitle().subSequence(0, SUBJECT_LENGTH); } - Pin targetPin = null; - if (subjectPins.size() == 1) { - targetPin = subjectPins.get(0); + String comment = postForExpandedLine.image() != null ? IMAGE_TEXT : ""; + if (postForExpandedLine.comment.length() > 0) { + comment += postForExpandedLine.comment; } - String smallText = TextUtils.join(", ", expandedLines); - return get(message, smallText, expandedLines, light, sound, peek, true, targetPin); + // Replace >>132456798 with >789 to shorten the notification + comment = SHORTEN_NO_PATTERN.matcher(comment).replaceAll(">$1"); + + expandedLines.add(prefix + ": " + comment); + } + + Pin targetPin = null; + if (subjectPins.size() == 1) { + targetPin = subjectPins.get(0); } + + String smallText = TextUtils.join(", ", expandedLines); + return get(message, smallText, expandedLines, light, sound, peek, true, targetPin); } /** From 8e08f0fa5680b653702da507c931a5f7fa4427e9 Mon Sep 17 00:00:00 2001 From: Floens Date: Mon, 22 Apr 2019 14:21:02 +0200 Subject: [PATCH 4/9] watcher: remove old settings for watch settings controller. implement notifications for = Build.VERSION_CODES.O) { + setSettingViewVisibility(normalChannel, false, false); + setSettingViewVisibility(mentionChannel, false, false); + } } } @@ -90,10 +96,10 @@ public class WatchSettingsController extends SettingsController implements Compo if (item == enableBackground) { boolean enabled = ChanSettings.watchBackground.get(); setSettingViewVisibility(backgroundTimeout, enabled, true); - setSettingViewVisibility(notifyMode, enabled, true); - setSettingViewVisibility(soundMode, enabled, true); - setSettingViewVisibility(peekMode, enabled, true); - setSettingViewVisibility(ledMode, enabled, true); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + setSettingViewVisibility(normalChannel, enabled, true); + setSettingViewVisibility(mentionChannel, enabled, true); + } } } @@ -104,6 +110,7 @@ public class WatchSettingsController extends SettingsController implements Compo enableBackground = settings.add(new BooleanSettingView(this, ChanSettings.watchBackground, R.string.setting_watch_enable_background, R.string.setting_watch_enable_background_description)); int[] timeouts = new int[]{ + 60 * 1000, 10 * 60 * 1000, 15 * 60 * 1000, 30 * 60 * 1000, @@ -124,17 +131,23 @@ public class WatchSettingsController extends SettingsController implements Compo } }); - notifyMode = settings.add(new ListSettingView<>(this, ChanSettings.watchNotifyMode, R.string.setting_watch_notify_mode, - context.getResources().getStringArray(R.array.setting_watch_notify_modes), new String[]{"all", "quotes"})); - - soundMode = settings.add(new ListSettingView<>(this, ChanSettings.watchSound, R.string.setting_watch_sound, - context.getResources().getStringArray(R.array.setting_watch_sounds), new String[]{"all", "quotes"})); - - peekMode = settings.add(new BooleanSettingView(this, ChanSettings.watchPeek, R.string.setting_watch_peek, R.string.setting_watch_peek_description)); - - ledMode = settings.add(new ListSettingView<>(this, ChanSettings.watchLed, R.string.setting_watch_led, - context.getResources().getStringArray(R.array.setting_watch_leds), - new String[]{"-1", "ffffffff", "ffff0000", "ffffff00", "ff00ff00", "ff00ffff", "ff0000ff", "ffff00ff"})); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + injector().instance(ThreadWatchNotifications.class).ensureChannels(); + + normalChannel = settings.add(new LinkSettingView(this, R.string.setting_watch_channel_normal, 0, v -> { + Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS); + intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName()); + intent.putExtra(Settings.EXTRA_CHANNEL_ID, ThreadWatchNotifications.CHANNEL_ID_WATCH_NORMAL); + AndroidUtils.openIntent(intent); + })); + + mentionChannel = settings.add(new LinkSettingView(this, R.string.setting_watch_channel_mention, 0, v -> { + Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS); + intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName()); + intent.putExtra(Settings.EXTRA_CHANNEL_ID, ThreadWatchNotifications.CHANNEL_ID_WATCH_MENTION); + AndroidUtils.openIntent(intent); + })); + } groups.add(settings); } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/notification/ThreadWatchNotifications.java b/Clover/app/src/main/java/org/floens/chan/ui/notification/ThreadWatchNotifications.java index c4f12cfb..f4b627c5 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/notification/ThreadWatchNotifications.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/notification/ThreadWatchNotifications.java @@ -22,8 +22,8 @@ import javax.inject.Singleton; @Singleton public class ThreadWatchNotifications extends NotificationHelper { - private static final String CHANNEL_ID_WATCH_NORMAL = "watch:normal"; - private static final String CHANNEL_ID_WATCH_MENTION = "watch:mention"; + public static final String CHANNEL_ID_WATCH_NORMAL = "watch:normal"; + public static final String CHANNEL_ID_WATCH_MENTION = "watch:mention"; private static final int NOTIFICATION_ID_WATCH_NORMAL = 0x10000; private static final int NOTIFICATION_ID_WATCH_NORMAL_MASK = 0xffff; private static final int NOTIFICATION_ID_WATCH_MENTION = 0x20000; @@ -39,20 +39,18 @@ public class ThreadWatchNotifications extends NotificationHelper { } public void showForWatchers(List pinWatchers) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - showPinSummaries(pinWatchers); - } else { - // legacy notification - } + showPinSummaries(pinWatchers); } - public void hide() { - + public void hideAll() { + notificationManager.cancelAll(); } @TargetApi(Build.VERSION_CODES.O) private void showPinSummaries(List pinWatchers) { - ensureChannels(); + if (isOreo()) { + ensureChannels(); + } for (WatchManager.PinWatcher pinWatcher : pinWatchers) { if (!pinWatcher.requiresNotificationUpdate()) { @@ -60,62 +58,70 @@ public class ThreadWatchNotifications extends NotificationHelper { } // Normal thread posts. + int normalId = NOTIFICATION_ID_WATCH_NORMAL + + (pinWatcher.getPinId() & NOTIFICATION_ID_WATCH_NORMAL_MASK); if (!pinWatcher.getUnviewedPosts().isEmpty()) { NotificationCompat.Builder builder = - new NotificationCompat.Builder(applicationContext, - CHANNEL_ID_WATCH_NORMAL); - - NotificationCompat.MessagingStyle messagingStyle = - new NotificationCompat.MessagingStyle(""); - - builder.setSmallIcon(R.drawable.ic_stat_notify); - if (pinWatcher.getThumbnailBitmap() != null) { - builder.setLargeIcon(pinWatcher.getThumbnailBitmap()); - } - - String subTitle = "(" + pinWatcher.getUnviewedPosts().size() + ")"; - messagingStyle.setConversationTitle(pinWatcher.getTitle() + " " + subTitle); - messagingStyle.setGroupConversation(true); - addPostsToMessagingStyle(messagingStyle, pinWatcher.getUnviewedPosts()); - builder.setStyle(messagingStyle); - - setNotificationIntent(builder); - - int id = NOTIFICATION_ID_WATCH_NORMAL + - (pinWatcher.getPinId() & NOTIFICATION_ID_WATCH_NORMAL_MASK); - notificationManager.notify(id, builder.build()); + buildMessagingStyleNotification(pinWatcher, pinWatcher.getUnviewedPosts(), + false, CHANNEL_ID_WATCH_NORMAL); + notificationManager.notify(normalId, builder.build()); + } else { + notificationManager.cancel(normalId); } // Posts that mention you. + int mentionId = NOTIFICATION_ID_WATCH_MENTION + + (pinWatcher.getPinId() & NOTIFICATION_ID_WATCH_MENTION_MASK); if (!pinWatcher.getUnviewedQuotes().isEmpty()) { NotificationCompat.Builder builder = - new NotificationCompat.Builder(applicationContext, - CHANNEL_ID_WATCH_MENTION); + buildMessagingStyleNotification(pinWatcher, pinWatcher.getUnviewedQuotes(), + true, CHANNEL_ID_WATCH_MENTION); - NotificationCompat.MessagingStyle messagingStyle = - new NotificationCompat.MessagingStyle(""); + notificationManager.notify(mentionId, builder.build()); + } else { + notificationManager.cancel(mentionId); + } - builder.setSmallIcon(R.drawable.ic_stat_notify_alert); - builder.setSubText("Mentions"); - if (pinWatcher.getThumbnailBitmap() != null) { - builder.setLargeIcon(pinWatcher.getThumbnailBitmap()); - } + pinWatcher.hadNotificationUpdate(); + } + } - String subTitle = "(" + pinWatcher.getUnviewedQuotes().size() + " mentions)"; - messagingStyle.setConversationTitle(pinWatcher.getTitle() + " " + subTitle); - messagingStyle.setGroupConversation(true); - addPostsToMessagingStyle(messagingStyle, pinWatcher.getUnviewedQuotes()); - builder.setStyle(messagingStyle); + private NotificationCompat.Builder buildMessagingStyleNotification( + WatchManager.PinWatcher pinWatcher, List posts, boolean mentions, + String channelId) { + NotificationCompat.Builder builder = + new NotificationCompat.Builder(applicationContext, channelId); - setNotificationIntent(builder); + NotificationCompat.MessagingStyle messagingStyle = + new NotificationCompat.MessagingStyle(""); - int id = NOTIFICATION_ID_WATCH_MENTION + - (pinWatcher.getPinId() & NOTIFICATION_ID_WATCH_MENTION_MASK); - notificationManager.notify(id, builder.build()); - } + builder.setSmallIcon(!mentions ? + R.drawable.ic_stat_notify : R.drawable.ic_stat_notify_alert); + if (mentions) { + builder.setSubText("Mentions"); + } + if (pinWatcher.getThumbnailBitmap() != null) { + builder.setLargeIcon(pinWatcher.getThumbnailBitmap()); + } + if (mentions && !isOreo()) { + builder.setDefaults(NotificationCompat.DEFAULT_SOUND | + NotificationCompat.DEFAULT_VIBRATE); + } - pinWatcher.hadNotificationUpdate(); + String subTitle; + if (!mentions) { + subTitle = "(" + posts.size() + ")"; + } else { + subTitle = "(" + posts.size() + " mentions)"; } + messagingStyle.setConversationTitle(pinWatcher.getTitle() + " " + subTitle); + messagingStyle.setGroupConversation(true); + addPostsToMessagingStyle(messagingStyle, posts); + builder.setStyle(messagingStyle); + + setNotificationIntent(builder); + + return builder; } private void addPostsToMessagingStyle(NotificationCompat.MessagingStyle messagingStyle, @@ -145,7 +151,6 @@ public class ThreadWatchNotifications extends NotificationHelper { } } - @TargetApi(Build.VERSION_CODES.O) private void setNotificationIntent(NotificationCompat.Builder builder) { Intent intent = new Intent(applicationContext, BoardActivity.class); intent.setAction(Intent.ACTION_MAIN); @@ -162,7 +167,7 @@ public class ThreadWatchNotifications extends NotificationHelper { } @TargetApi(Build.VERSION_CODES.O) - private void ensureChannels() { + public void ensureChannels() { NotificationChannel normalChannel = new NotificationChannel( CHANNEL_ID_WATCH_NORMAL, "Thread updates", NotificationManager.IMPORTANCE_DEFAULT); @@ -177,4 +182,8 @@ public class ThreadWatchNotifications extends NotificationHelper { mentionChannel.enableLights(true); notificationManager.createNotificationChannel(mentionChannel); } + + private boolean isOreo() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; + } } diff --git a/Clover/app/src/main/res/values/strings.xml b/Clover/app/src/main/res/values/strings.xml index 71002459..00123752 100644 --- a/Clover/app/src/main/res/values/strings.xml +++ b/Clover/app/src/main/res/values/strings.xml @@ -408,6 +408,8 @@ Enable \"Play videos with external player\" in the settings to play videos with Blue Purple + Notification settings for thread updates + Notification settings for thread mentions About From 0a8cc4751273752ebbe557b10745b7db1fb4ef7e Mon Sep 17 00:00:00 2001 From: Floens Date: Mon, 22 Apr 2019 14:24:28 +0200 Subject: [PATCH 5/9] watcher: delete WatchNotifier service. It is replaced by ThreadWatchNotificatons. The usage of a foreground service was already deprecated by using AlarmManager for background updates. --- Clover/app/src/main/AndroidManifest.xml | 4 - .../floens/chan/ui/service/WatchNotifier.java | 303 ------------------ 2 files changed, 307 deletions(-) delete mode 100644 Clover/app/src/main/java/org/floens/chan/ui/service/WatchNotifier.java diff --git a/Clover/app/src/main/AndroidManifest.xml b/Clover/app/src/main/AndroidManifest.xml index c0c9ddd2..3d85b924 100644 --- a/Clover/app/src/main/AndroidManifest.xml +++ b/Clover/app/src/main/AndroidManifest.xml @@ -80,10 +80,6 @@ along with this program. If not, see . - - diff --git a/Clover/app/src/main/java/org/floens/chan/ui/service/WatchNotifier.java b/Clover/app/src/main/java/org/floens/chan/ui/service/WatchNotifier.java deleted file mode 100644 index a52e8514..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/service/WatchNotifier.java +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.service; - -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.os.IBinder; -import android.support.annotation.Nullable; -import android.support.v4.app.NotificationCompat; -import android.text.TextUtils; - -import org.floens.chan.Chan; -import org.floens.chan.R; -import org.floens.chan.core.manager.WatchManager; -import org.floens.chan.core.model.Post; -import org.floens.chan.core.model.orm.Pin; -import org.floens.chan.core.settings.ChanSettings; -import org.floens.chan.ui.activity.BoardActivity; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.regex.Pattern; - -import javax.inject.Inject; - -import static org.floens.chan.Chan.inject; - -public class WatchNotifier extends Service { - private static final String TAG = "WatchNotifier"; - private static final int NOTIFICATION_ID = 1; - private static final PostAgeComparator POST_AGE_COMPARATOR = new PostAgeComparator(); - private static final int SUBJECT_LENGTH = 6; - private static final String IMAGE_TEXT = "(img) "; - private static final Pattern SHORTEN_NO_PATTERN = Pattern.compile(">>\\d+(?=\\d{3})(\\d{3})"); - - private NotificationManager nm; - - @Inject - WatchManager watchManager; - - @Override - public IBinder onBind(final Intent intent) { - return null; - } - - @Override - public void onCreate() { - super.onCreate(); - inject(this); - - nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - } - - @Override - public void onDestroy() { - super.onDestroy(); - nm.cancel(NOTIFICATION_ID); - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - if (intent != null && intent.getExtras() != null && intent.getExtras().getBoolean("pause_pins", false)) { - pausePins(); - } else { - updateNotification(); - } - - return START_STICKY; - } - - public void updateNotification() { - Notification notification = createNotification(); - if (notification != null) { - nm.notify(NOTIFICATION_ID, notification); - } - } - - public void pausePins() { - watchManager.pauseAll(); - } - - @Nullable - private Notification createNotification() { - boolean notifyQuotesOnly = ChanSettings.watchNotifyMode.get().equals("quotes"); - boolean soundQuotesOnly = ChanSettings.watchSound.get().equals("quotes"); - - List unviewedPosts = new ArrayList<>(); - List listQuoting = new ArrayList<>(); - List pins = new ArrayList<>(); - List subjectPins = new ArrayList<>(); - - boolean light = false; - boolean sound = false; - boolean peek = false; - - for (Pin pin : watchManager.getWatchingPins()) { - WatchManager.PinWatcher watcher = watchManager.getPinWatcher(pin); - if (watcher == null || pin.isError) { - continue; - } - - pins.add(pin); - - if (notifyQuotesOnly) { - unviewedPosts.addAll(watcher.getUnviewedQuotes()); - listQuoting.addAll(watcher.getUnviewedQuotes()); - if (watcher.getWereNewQuotes()) { - light = true; - sound = true; - peek = true; - } - if (pin.getNewQuoteCount() > 0) { - subjectPins.add(pin); - } - } else { - unviewedPosts.addAll(watcher.getUnviewedPosts()); - listQuoting.addAll(watcher.getUnviewedQuotes()); - if (watcher.getWereNewPosts()) { - light = true; - if (!soundQuotesOnly) { - sound = true; - peek = true; - } - } - if (watcher.getWereNewQuotes()) { - sound = true; - peek = true; - } - if (pin.getNewPostCount() > 0) { - subjectPins.add(pin); - } - } - } - - if (Chan.getInstance().getApplicationInForeground()) { - light = false; - sound = false; - } - - if (!ChanSettings.watchPeek.get()) { - peek = false; - } - - return notifyAboutPosts(pins, subjectPins, unviewedPosts, listQuoting, notifyQuotesOnly, light, sound, peek); - } - - @Nullable - private Notification notifyAboutPosts(List pins, List subjectPins, List unviewedPosts, List listQuoting, - boolean notifyQuotesOnly, boolean light, boolean sound, boolean peek) { - if (unviewedPosts.isEmpty()) { - return null; - } - // New posts notification - String message; - List postsForExpandedLines; - if (notifyQuotesOnly) { - message = getResources().getQuantityString(R.plurals.watch_new_quotes, listQuoting.size(), listQuoting.size()); - postsForExpandedLines = listQuoting; - } else { - postsForExpandedLines = unviewedPosts; - if (listQuoting.size() > 0) { - message = getResources().getQuantityString(R.plurals.watch_new_quoting, unviewedPosts.size(), unviewedPosts.size(), listQuoting.size()); - } else { - message = getResources().getQuantityString(R.plurals.watch_new, unviewedPosts.size(), unviewedPosts.size()); - } - } - - Collections.sort(postsForExpandedLines, POST_AGE_COMPARATOR); - List expandedLines = new ArrayList<>(); - for (Post postForExpandedLine : postsForExpandedLines) { - CharSequence prefix; - if (postForExpandedLine.getTitle().length() <= SUBJECT_LENGTH) { - prefix = postForExpandedLine.getTitle(); - } else { - prefix = postForExpandedLine.getTitle().subSequence(0, SUBJECT_LENGTH); - } - - String comment = postForExpandedLine.image() != null ? IMAGE_TEXT : ""; - if (postForExpandedLine.comment.length() > 0) { - comment += postForExpandedLine.comment; - } - - // Replace >>132456798 with >789 to shorten the notification - comment = SHORTEN_NO_PATTERN.matcher(comment).replaceAll(">$1"); - - expandedLines.add(prefix + ": " + comment); - } - - Pin targetPin = null; - if (subjectPins.size() == 1) { - targetPin = subjectPins.get(0); - } - - String smallText = TextUtils.join(", ", expandedLines); - return get(message, smallText, expandedLines, light, sound, peek, true, targetPin); - } - - /** - * Create a notification with the supplied parameters. - * The style of the big notification is InboxStyle, a list of text. - * - * @param title The title of the notification - * @param smallText The content of the small notification - * @param expandedLines A list of lines for the big notification, or null if not shown - * @param sound Should the notification make a sound - * @param peek Peek the notification into the screen - * @param alertIcon Show the alert version of the icon - * @param target The target pin, or null to open the pinned pane on tap - */ - private Notification get(String title, String smallText, List expandedLines, - boolean light, boolean sound, boolean peek, boolean alertIcon, Pin target) { - Intent intent = new Intent(this, BoardActivity.class); - intent.setAction(Intent.ACTION_MAIN); - intent.addCategory(Intent.CATEGORY_LAUNCHER); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); - - intent.putExtra("pin_id", target == null ? -1 : target.id); - - PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); - - NotificationCompat.Builder builder = new NotificationCompat.Builder(this); - if (sound || peek) { - builder.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE); - } - - if (light) { - long watchLed = Long.parseLong(ChanSettings.watchLed.get(), 16); - if (watchLed >= 0) { - builder.setLights((int) watchLed, 1000, 1000); - } - } - - builder.setContentIntent(pendingIntent); - - builder.setContentTitle(title); - if (smallText != null) { - builder.setContentText(smallText); - } - - if (alertIcon || peek) { - builder.setSmallIcon(R.drawable.ic_stat_notify_alert); - builder.setPriority(NotificationCompat.PRIORITY_HIGH); - } else { - builder.setSmallIcon(R.drawable.ic_stat_notify); - builder.setPriority(NotificationCompat.PRIORITY_MIN); - } - - Intent pauseWatching = new Intent(this, WatchNotifier.class); - pauseWatching.putExtra("pause_pins", true); - - PendingIntent pauseWatchingPending = PendingIntent.getService(this, 0, pauseWatching, - PendingIntent.FLAG_UPDATE_CURRENT); - - builder.addAction(R.drawable.ic_action_pause, getString(R.string.watch_pause_pins), - pauseWatchingPending); - - if (expandedLines != null) { - NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle(); - for (CharSequence line : expandedLines.subList(Math.max(0, expandedLines.size() - 10), expandedLines.size())) { - style.addLine(line); - } - style.setBigContentTitle(title); - builder.setStyle(style); - } - - return builder.build(); - } - - private static class PostAgeComparator implements Comparator { - @Override - public int compare(Post lhs, Post rhs) { - if (lhs.time < rhs.time) { - return 1; - } else if (lhs.time > rhs.time) { - return -1; - } else { - return 0; - } - } - } -} From 316dafb0baad20af635362b8750ab340c26d1345 Mon Sep 17 00:00:00 2001 From: Floens Date: Tue, 16 Jul 2019 22:17:27 +0200 Subject: [PATCH 6/9] watcher: enable clicking notification to go to thread --- .../java/org/floens/chan/ui/activity/StartActivity.java | 6 +++--- .../org/floens/chan/ui/controller/DrawerController.java | 4 ++++ .../chan/ui/notification/ThreadWatchNotifications.java | 6 ++++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java index c26f04a5..03dd2ea5 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java @@ -308,16 +308,16 @@ public class StartActivity extends AppCompatActivity implements NfcAdapter.Creat protected void onNewIntent(Intent intent) { super.onNewIntent(intent); - // Handle WatchNotifier clicks + // Handle notification clicks if (intent.getExtras() != null) { int pinId = intent.getExtras().getInt("pin_id", -2); - if (pinId != -2 && mainNavigationController.getTop() instanceof BrowseController) { + if (pinId != -2) { if (pinId == -1) { drawerController.onMenuClicked(); } else { Pin pin = watchManager.findPinById(pinId); if (pin != null) { - browseController.showThread(pin.loadable, false); + drawerController.openPin(pin); } } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/DrawerController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/DrawerController.java index c0758194..99f0cb9e 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/DrawerController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/DrawerController.java @@ -142,6 +142,10 @@ public class DrawerController extends Controller implements DrawerAdapter.Callba // TODO: probably twice because of some force redraw, fix that. drawerLayout.post(() -> drawerLayout.post(() -> drawerLayout.closeDrawer(drawer))); + openPin(pin); + } + + public void openPin(Pin pin) { ThreadController threadController = getTopThreadController(); if (threadController != null) { threadController.openPin(pin); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/notification/ThreadWatchNotifications.java b/Clover/app/src/main/java/org/floens/chan/ui/notification/ThreadWatchNotifications.java index f4b627c5..f6c505d9 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/notification/ThreadWatchNotifications.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/notification/ThreadWatchNotifications.java @@ -119,7 +119,7 @@ public class ThreadWatchNotifications extends NotificationHelper { addPostsToMessagingStyle(messagingStyle, posts); builder.setStyle(messagingStyle); - setNotificationIntent(builder); + setNotificationIntent(builder, pinWatcher.getPinId()); return builder; } @@ -151,7 +151,7 @@ public class ThreadWatchNotifications extends NotificationHelper { } } - private void setNotificationIntent(NotificationCompat.Builder builder) { + private void setNotificationIntent(NotificationCompat.Builder builder, int pinId) { Intent intent = new Intent(applicationContext, BoardActivity.class); intent.setAction(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); @@ -160,6 +160,8 @@ public class ThreadWatchNotifications extends NotificationHelper { Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + intent.putExtra("pin_id", pinId); + PendingIntent pendingIntent = PendingIntent.getActivity( applicationContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); From 52aa24e9e84f3b78c92d6453c3fcd1020ce733af Mon Sep 17 00:00:00 2001 From: Floens Date: Tue, 16 Jul 2019 22:19:21 +0200 Subject: [PATCH 7/9] build: update gradle to 3.4.2 --- Clover/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Clover/build.gradle b/Clover/build.gradle index 6ac36dd8..dcf3372b 100644 --- a/Clover/build.gradle +++ b/Clover/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.4.0' + classpath 'com.android.tools.build:gradle:3.4.2' } } From 787ef0d27c5e2cc064f86f9ea165e57c779191b2 Mon Sep 17 00:00:00 2001 From: Floens Date: Tue, 16 Jul 2019 22:49:06 +0200 Subject: [PATCH 8/9] watcher: avoid notification pending intent collapsing caused to only open one thread clicking any notification. now it creates unique pending intents by using new request codes. --- .../chan/ui/notification/ThreadWatchNotifications.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Clover/app/src/main/java/org/floens/chan/ui/notification/ThreadWatchNotifications.java b/Clover/app/src/main/java/org/floens/chan/ui/notification/ThreadWatchNotifications.java index f6c505d9..5d5ed32e 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/notification/ThreadWatchNotifications.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/notification/ThreadWatchNotifications.java @@ -12,6 +12,7 @@ import android.support.v4.app.NotificationCompat; import org.floens.chan.R; import org.floens.chan.core.manager.WatchManager; import org.floens.chan.core.model.Post; +import org.floens.chan.core.model.orm.Pin; import org.floens.chan.ui.activity.BoardActivity; import java.util.List; @@ -33,6 +34,8 @@ public class ThreadWatchNotifications extends NotificationHelper { private static final Pattern POST_COMMENT_SHORTEN_NO_PATTERN = Pattern.compile(">>\\d+(?=\\d{4})(\\d{4})"); + private int pendingIntentCounter = 0; + @Inject public ThreadWatchNotifications(Context applicationContext) { super(applicationContext); @@ -163,7 +166,8 @@ public class ThreadWatchNotifications extends NotificationHelper { intent.putExtra("pin_id", pinId); PendingIntent pendingIntent = PendingIntent.getActivity( - applicationContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + applicationContext, ++pendingIntentCounter, + intent, PendingIntent.FLAG_UPDATE_CURRENT); builder.setContentIntent(pendingIntent); } From ea0c272382e5bb33e8068776865bc4d637e6f600 Mon Sep 17 00:00:00 2001 From: Floens Date: Tue, 16 Jul 2019 23:03:59 +0200 Subject: [PATCH 9/9] build: update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0f21f21a..3b41070a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ android: - tools - extra-android-m2repository - build-tools-28.0.3 - - android-27 + - android-28 script: cd Clover && ./gradlew build --console plain -x lint