From 109e86b3885dd2564aae94cb9803fe364b95a358 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Mon, 5 Jun 2017 11:48:26 +0300 Subject: move glide-related stuff to a separate package --- org.fox.ttrss/src/main/AndroidManifest.xml | 2 +- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 3 +- .../fox/ttrss/glide/OkHttpProgressGlideModule.java | 160 +++++++++++++++++++++ .../java/org/fox/ttrss/glide/ProgressTarget.java | 110 ++++++++++++++ .../java/org/fox/ttrss/glide/WrappingTarget.java | 52 +++++++ .../fox/ttrss/util/OkHttpProgressGlideModule.java | 160 --------------------- .../java/org/fox/ttrss/util/ProgressTarget.java | 110 -------------- .../java/org/fox/ttrss/util/WrappingTarget.java | 52 ------- 8 files changed, 324 insertions(+), 325 deletions(-) create mode 100644 org.fox.ttrss/src/main/java/org/fox/ttrss/glide/OkHttpProgressGlideModule.java create mode 100644 org.fox.ttrss/src/main/java/org/fox/ttrss/glide/ProgressTarget.java create mode 100644 org.fox.ttrss/src/main/java/org/fox/ttrss/glide/WrappingTarget.java delete mode 100644 org.fox.ttrss/src/main/java/org/fox/ttrss/util/OkHttpProgressGlideModule.java delete mode 100644 org.fox.ttrss/src/main/java/org/fox/ttrss/util/ProgressTarget.java delete mode 100644 org.fox.ttrss/src/main/java/org/fox/ttrss/util/WrappingTarget.java (limited to 'org.fox.ttrss') diff --git a/org.fox.ttrss/src/main/AndroidManifest.xml b/org.fox.ttrss/src/main/AndroidManifest.xml index 8036550a..d7b91e98 100755 --- a/org.fox.ttrss/src/main/AndroidManifest.xml +++ b/org.fox.ttrss/src/main/AndroidManifest.xml @@ -22,7 +22,7 @@ - LISTENERS = new HashMap<>(); + private static final Map 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 diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/ProgressTarget.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/ProgressTarget.java new file mode 100644 index 00000000..977d1954 --- /dev/null +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/ProgressTarget.java @@ -0,0 +1,110 @@ +package org.fox.ttrss.glide; + + +import android.graphics.drawable.Drawable; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.animation.GlideAnimation; +import com.bumptech.glide.request.target.Target; + +public abstract class ProgressTarget extends WrappingTarget implements OkHttpProgressGlideModule.UIProgressListener { + private T model; + private boolean ignoreProgress = true; + public ProgressTarget(Target target) { + this(null, target); + } + public ProgressTarget(T model, Target target) { + super(target); + this.model = model; + } + + public final T getModel() { + return model; + } + public final void setModel(T model) { + Glide.clear(this); // indirectly calls cleanup + this.model = model; + } + /** + * Convert a model into an Url string that is used to match up the OkHttp requests. For explicit + * {@link com.bumptech.glide.load.model.GlideUrl GlideUrl} loads this needs to return + * {@link com.bumptech.glide.load.model.GlideUrl#toStringUrl toStringUrl}. For custom models do the same as your + * {@link com.bumptech.glide.load.model.stream.BaseGlideUrlLoader BaseGlideUrlLoader} does. + * @param model return the representation of the given model, DO NOT use {@link #getModel()} inside this method. + * @return a stable Url representation of the model, otherwise the progress reporting won't work + */ + protected String toUrlString(T model) { + return String.valueOf(model); + } + + @Override public float getGranualityPercentage() { + return 1.0f; + } + + @Override public void onProgress(long bytesRead, long expectedLength) { + if (ignoreProgress) { + return; + } + if (expectedLength == Long.MAX_VALUE) { + onConnecting(); + } else if (bytesRead == expectedLength) { + onDownloaded(); + } else { + onDownloading(bytesRead, expectedLength); + } + } + + /** + * Called when the Glide load has started. + * At this time it is not known if the Glide will even go and use the network to fetch the image. + */ + protected abstract void onConnecting(); + /** + * Called when there's any progress on the download; not called when loading from cache. + * At this time we know how many bytes have been transferred through the wire. + */ + protected abstract void onDownloading(long bytesRead, long expectedLength); + /** + * Called when the bytes downloaded reach the length reported by the server; not called when loading from cache. + * At this time it is fairly certain, that Glide either finished reading the stream. + * This means that the image was either already decoded or saved the network stream to cache. + * In the latter case there's more work to do: decode the image from cache and transform. + * These cannot be listened to for progress so it's unsure how fast they'll be, best to show indeterminate progress. + */ + protected abstract void onDownloaded(); + /** + * Called when the Glide load has finished either by successfully loading the image or failing to load or cancelled. + * In any case the best is to hide/reset any progress displays. + */ + protected abstract void onDelivered(); + + private void start() { + OkHttpProgressGlideModule.expect(toUrlString(model), this); + ignoreProgress = false; + onProgress(0, Long.MAX_VALUE); + } + private void cleanup() { + ignoreProgress = true; + T model = this.model; // save in case it gets modified + onDelivered(); + OkHttpProgressGlideModule.forget(toUrlString(model)); + this.model = null; + } + + @Override public void onLoadStarted(Drawable placeholder) { + super.onLoadStarted(placeholder); + start(); + } + @Override public void onResourceReady(Z resource, GlideAnimation animation) { + cleanup(); + super.onResourceReady(resource, animation); + } + @Override public void onLoadFailed(Exception e, Drawable errorDrawable) { + cleanup(); + super.onLoadFailed(e, errorDrawable); + } + @Override public void onLoadCleared(Drawable placeholder) { + cleanup(); + super.onLoadCleared(placeholder); + } +} \ No newline at end of file diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/WrappingTarget.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/WrappingTarget.java new file mode 100644 index 00000000..90990e16 --- /dev/null +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/WrappingTarget.java @@ -0,0 +1,52 @@ +package org.fox.ttrss.glide; + +import android.graphics.drawable.Drawable; +import android.support.annotation.NonNull; + +import com.bumptech.glide.request.Request; +import com.bumptech.glide.request.animation.GlideAnimation; +import com.bumptech.glide.request.target.*; + +public class WrappingTarget implements Target { + protected final @NonNull Target target; + public WrappingTarget(@NonNull Target target) { + this.target = target; + } + public @NonNull Target getWrappedTarget() { + return target; + } + @Override public void getSize(SizeReadyCallback cb) { + target.getSize(cb); + } + + @Override public void onLoadStarted(Drawable placeholder) { + target.onLoadStarted(placeholder); + } + @Override public void onLoadFailed(Exception e, Drawable errorDrawable) { + target.onLoadFailed(e, errorDrawable); + } + @SuppressWarnings("unchecked") + @Override public void onResourceReady(Z resource, GlideAnimation glideAnimation) { + target.onResourceReady(resource, (GlideAnimation)glideAnimation); + } + @Override public void onLoadCleared(Drawable placeholder) { + target.onLoadCleared(placeholder); + } + + @Override public Request getRequest() { + return target.getRequest(); + } + @Override public void setRequest(Request request) { + target.setRequest(request); + } + + @Override public void onStart() { + target.onStart(); + } + @Override public void onStop() { + target.onStop(); + } + @Override public void onDestroy() { + target.onDestroy(); + } +} \ No newline at end of file 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 deleted file mode 100644 index b333807e..00000000 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/OkHttpProgressGlideModule.java +++ /dev/null @@ -1,160 +0,0 @@ -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 LISTENERS = new HashMap<>(); - private static final Map 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 diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/ProgressTarget.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/ProgressTarget.java deleted file mode 100644 index df02865c..00000000 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/ProgressTarget.java +++ /dev/null @@ -1,110 +0,0 @@ -package org.fox.ttrss.util; - - -import android.graphics.drawable.Drawable; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.request.animation.GlideAnimation; -import com.bumptech.glide.request.target.Target; - -public abstract class ProgressTarget extends WrappingTarget implements OkHttpProgressGlideModule.UIProgressListener { - private T model; - private boolean ignoreProgress = true; - public ProgressTarget(Target target) { - this(null, target); - } - public ProgressTarget(T model, Target target) { - super(target); - this.model = model; - } - - public final T getModel() { - return model; - } - public final void setModel(T model) { - Glide.clear(this); // indirectly calls cleanup - this.model = model; - } - /** - * Convert a model into an Url string that is used to match up the OkHttp requests. For explicit - * {@link com.bumptech.glide.load.model.GlideUrl GlideUrl} loads this needs to return - * {@link com.bumptech.glide.load.model.GlideUrl#toStringUrl toStringUrl}. For custom models do the same as your - * {@link com.bumptech.glide.load.model.stream.BaseGlideUrlLoader BaseGlideUrlLoader} does. - * @param model return the representation of the given model, DO NOT use {@link #getModel()} inside this method. - * @return a stable Url representation of the model, otherwise the progress reporting won't work - */ - protected String toUrlString(T model) { - return String.valueOf(model); - } - - @Override public float getGranualityPercentage() { - return 1.0f; - } - - @Override public void onProgress(long bytesRead, long expectedLength) { - if (ignoreProgress) { - return; - } - if (expectedLength == Long.MAX_VALUE) { - onConnecting(); - } else if (bytesRead == expectedLength) { - onDownloaded(); - } else { - onDownloading(bytesRead, expectedLength); - } - } - - /** - * Called when the Glide load has started. - * At this time it is not known if the Glide will even go and use the network to fetch the image. - */ - protected abstract void onConnecting(); - /** - * Called when there's any progress on the download; not called when loading from cache. - * At this time we know how many bytes have been transferred through the wire. - */ - protected abstract void onDownloading(long bytesRead, long expectedLength); - /** - * Called when the bytes downloaded reach the length reported by the server; not called when loading from cache. - * At this time it is fairly certain, that Glide either finished reading the stream. - * This means that the image was either already decoded or saved the network stream to cache. - * In the latter case there's more work to do: decode the image from cache and transform. - * These cannot be listened to for progress so it's unsure how fast they'll be, best to show indeterminate progress. - */ - protected abstract void onDownloaded(); - /** - * Called when the Glide load has finished either by successfully loading the image or failing to load or cancelled. - * In any case the best is to hide/reset any progress displays. - */ - protected abstract void onDelivered(); - - private void start() { - OkHttpProgressGlideModule.expect(toUrlString(model), this); - ignoreProgress = false; - onProgress(0, Long.MAX_VALUE); - } - private void cleanup() { - ignoreProgress = true; - T model = this.model; // save in case it gets modified - onDelivered(); - OkHttpProgressGlideModule.forget(toUrlString(model)); - this.model = null; - } - - @Override public void onLoadStarted(Drawable placeholder) { - super.onLoadStarted(placeholder); - start(); - } - @Override public void onResourceReady(Z resource, GlideAnimation animation) { - cleanup(); - super.onResourceReady(resource, animation); - } - @Override public void onLoadFailed(Exception e, Drawable errorDrawable) { - cleanup(); - super.onLoadFailed(e, errorDrawable); - } - @Override public void onLoadCleared(Drawable placeholder) { - cleanup(); - super.onLoadCleared(placeholder); - } -} \ No newline at end of file diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/WrappingTarget.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/WrappingTarget.java deleted file mode 100644 index 5dcfa7ec..00000000 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/WrappingTarget.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.fox.ttrss.util; - -import android.graphics.drawable.Drawable; -import android.support.annotation.NonNull; - -import com.bumptech.glide.request.Request; -import com.bumptech.glide.request.animation.GlideAnimation; -import com.bumptech.glide.request.target.*; - -public class WrappingTarget implements Target { - protected final @NonNull Target target; - public WrappingTarget(@NonNull Target target) { - this.target = target; - } - public @NonNull Target getWrappedTarget() { - return target; - } - @Override public void getSize(SizeReadyCallback cb) { - target.getSize(cb); - } - - @Override public void onLoadStarted(Drawable placeholder) { - target.onLoadStarted(placeholder); - } - @Override public void onLoadFailed(Exception e, Drawable errorDrawable) { - target.onLoadFailed(e, errorDrawable); - } - @SuppressWarnings("unchecked") - @Override public void onResourceReady(Z resource, GlideAnimation glideAnimation) { - target.onResourceReady(resource, (GlideAnimation)glideAnimation); - } - @Override public void onLoadCleared(Drawable placeholder) { - target.onLoadCleared(placeholder); - } - - @Override public Request getRequest() { - return target.getRequest(); - } - @Override public void setRequest(Request request) { - target.setRequest(request); - } - - @Override public void onStart() { - target.onStart(); - } - @Override public void onStop() { - target.onStop(); - } - @Override public void onDestroy() { - target.onDestroy(); - } -} \ No newline at end of file -- cgit v1.2.3