160 lines
5.9 KiB
Java
160 lines
5.9 KiB
Java
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;
|
|
}
|
|
};
|
|
}
|
|
}
|
|
} |