summaryrefslogtreecommitdiff
path: root/org.fox.ttrss/src/main/java/org/fox/ttrss/util/OkHttpProgressGlideModule.java
diff options
context:
space:
mode:
Diffstat (limited to 'org.fox.ttrss/src/main/java/org/fox/ttrss/util/OkHttpProgressGlideModule.java')
-rw-r--r--org.fox.ttrss/src/main/java/org/fox/ttrss/util/OkHttpProgressGlideModule.java160
1 files changed, 160 insertions, 0 deletions
diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/OkHttpProgressGlideModule.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/OkHttpProgressGlideModule.java
new file mode 100644
index 00000000..b333807e
--- /dev/null
+++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/OkHttpProgressGlideModule.java
@@ -0,0 +1,160 @@
+package org.fox.ttrss.util;
+
+import java.io.*;
+import java.util.*;
+
+import android.content.Context;
+import android.os.*;
+
+import com.bumptech.glide.*;
+import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader;
+import com.bumptech.glide.load.model.GlideUrl;
+import com.bumptech.glide.module.GlideModule;
+
+import okhttp3.*;
+import okio.*;
+
+public class OkHttpProgressGlideModule implements GlideModule {
+ @Override public void applyOptions(Context context, GlideBuilder builder) {
+
+ }
+ @Override public void registerComponents(Context context, Glide glide) {
+ OkHttpClient client = new OkHttpClient.Builder()
+ .addNetworkInterceptor(createInterceptor(new DispatchingProgressListener()))
+ .build();
+ glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(client));
+ }
+
+ private static Interceptor createInterceptor(final ResponseProgressListener listener) {
+ return new Interceptor() {
+ @Override public Response intercept(Chain chain) throws IOException {
+ Request request = chain.request();
+ Response response = chain.proceed(request);
+ return response.newBuilder()
+ .body(new OkHttpProgressResponseBody(request.url(), response.body(), listener))
+ .build();
+ }
+ };
+ }
+
+ public interface UIProgressListener {
+ void onProgress(long bytesRead, long expectedLength);
+ /**
+ * Control how often the listener needs an update. 0% and 100% will always be dispatched.
+ * @return in percentage (0.2 = call {@link #onProgress} around every 0.2 percent of progress)
+ */
+ float getGranualityPercentage();
+ }
+
+ public static void forget(String url) {
+ DispatchingProgressListener.forget(url);
+ }
+ public static void expect(String url, UIProgressListener listener) {
+ DispatchingProgressListener.expect(url, listener);
+ }
+
+ private interface ResponseProgressListener {
+ void update(HttpUrl url, long bytesRead, long contentLength);
+ }
+
+ private static class DispatchingProgressListener implements ResponseProgressListener {
+ private static final Map<String, UIProgressListener> LISTENERS = new HashMap<>();
+ private static final Map<String, Long> PROGRESSES = new HashMap<>();
+
+ private final Handler handler;
+ DispatchingProgressListener() {
+ this.handler = new Handler(Looper.getMainLooper());
+ }
+
+ static void forget(String url) {
+ LISTENERS.remove(url);
+ PROGRESSES.remove(url);
+ }
+ static void expect(String url, UIProgressListener listener) {
+ LISTENERS.put(url, listener);
+ }
+
+ @Override public void update(HttpUrl url, final long bytesRead, final long contentLength) {
+ //System.out.printf("%s: %d/%d = %.2f%%%n", url, bytesRead, contentLength, (100f * bytesRead) / contentLength);
+
+ String key = url.toString();
+ final UIProgressListener listener = LISTENERS.get(key);
+
+ if (listener == null) {
+ return;
+ }
+ if (contentLength <= bytesRead) {
+ forget(key);
+ }
+ if (needsDispatch(key, bytesRead, contentLength, listener.getGranualityPercentage())) {
+ handler.post(new Runnable() {
+ @Override public void run() {
+ listener.onProgress(bytesRead, contentLength);
+ }
+ });
+ }
+ }
+
+ private boolean needsDispatch(String key, long current, long total, float granularity) {
+ if (granularity == 0 || current == 0 || total == current) {
+ return true;
+ }
+ float percent = 100f * current / total;
+ long currentProgress = (long)(percent / granularity);
+ Long lastProgress = PROGRESSES.get(key);
+ if (lastProgress == null || currentProgress != lastProgress) {
+ PROGRESSES.put(key, currentProgress);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ private static class OkHttpProgressResponseBody extends ResponseBody {
+ private final HttpUrl url;
+ private final ResponseBody responseBody;
+ private final ResponseProgressListener progressListener;
+ private BufferedSource bufferedSource;
+
+ OkHttpProgressResponseBody(HttpUrl url, ResponseBody responseBody,
+ ResponseProgressListener progressListener) {
+
+ this.url = url;
+ this.responseBody = responseBody;
+ this.progressListener = progressListener;
+ }
+
+ @Override public MediaType contentType() {
+ return responseBody.contentType();
+ }
+
+ @Override public long contentLength() {
+ return responseBody.contentLength();
+ }
+
+ @Override public BufferedSource source() {
+ if (bufferedSource == null) {
+ bufferedSource = Okio.buffer(source(responseBody.source()));
+ }
+ return bufferedSource;
+ }
+
+ private Source source(Source source) {
+ return new ForwardingSource(source) {
+ long totalBytesRead = 0L;
+ @Override public long read(Buffer sink, long byteCount) throws IOException {
+ long bytesRead = super.read(sink, byteCount);
+ long fullLength = responseBody.contentLength();
+ if (bytesRead == -1) { // this source is exhausted
+ totalBytesRead = fullLength;
+ } else {
+ totalBytesRead += bytesRead;
+ }
+ progressListener.update(url, totalBytesRead, fullLength);
+ return bytesRead;
+ }
+ };
+ }
+ }
+} \ No newline at end of file