/* * The MIT License (MIT) * * Copyright (c) 2016 Piasy * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * credit: https://gist.github.com/TWiStErRob/08d5807e396740e52c90 */ package com.github.piasy.biv.loader.glide; import com.bumptech.glide.Glide; import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader; import com.bumptech.glide.load.model.GlideUrl; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import okhttp3.HttpUrl; import okhttp3.Interceptor; import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import okhttp3.ResponseBody; import okio.Buffer; import okio.BufferedSource; import okio.ForwardingSource; import okio.Okio; import okio.Source; /** * Created by Piasy{github.com/Piasy} on 12/11/2016. */ public class GlideProgressSupport { 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 static void init(Glide glide, OkHttpClient okHttpClient) { OkHttpClient.Builder builder; if (okHttpClient != null) { builder = okHttpClient.newBuilder(); } else { builder = new OkHttpClient.Builder(); } builder.addNetworkInterceptor(createInterceptor(new DispatchingProgressListener())); glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(builder.build())); } public static void forget(String url) { DispatchingProgressListener.forget(url); } public static void expect(String url, ProgressListener listener) { DispatchingProgressListener.expect(url, listener); } public interface ProgressListener { void onDownloadStart(); void onProgress(int progress); void onDownloadFinish(); } private interface ResponseProgressListener { void update(HttpUrl url, long bytesRead, long contentLength); } private static class DispatchingProgressListener implements ResponseProgressListener { private static final Map<String, ProgressListener> LISTENERS = new HashMap<>(); private static final Map<String, Integer> PROGRESSES = new HashMap<>(); static void forget(String url) { LISTENERS.remove(url); PROGRESSES.remove(url); } static void expect(String url, ProgressListener listener) { LISTENERS.put(url, listener); } @Override public void update(HttpUrl url, final long bytesRead, final long contentLength) { String key = url.toString(); final ProgressListener listener = LISTENERS.get(key); if (listener == null) { return; } Integer lastProgress = PROGRESSES.get(key); if (lastProgress == null) { // ensure `onStart` is called before `onProgress` and `onFinish` listener.onDownloadStart(); } if (contentLength <= bytesRead) { listener.onDownloadFinish(); forget(key); return; } int progress = (int) ((float) bytesRead / contentLength * 100); if (lastProgress == null || progress != lastProgress) { PROGRESSES.put(key, progress); listener.onProgress(progress); } } } private static class OkHttpProgressResponseBody extends ResponseBody { private final HttpUrl mUrl; private final ResponseBody mResponseBody; private final ResponseProgressListener mProgressListener; private BufferedSource mBufferedSource; OkHttpProgressResponseBody(HttpUrl url, ResponseBody responseBody, ResponseProgressListener progressListener) { this.mUrl = url; this.mResponseBody = responseBody; this.mProgressListener = progressListener; } @Override public MediaType contentType() { return mResponseBody.contentType(); } @Override public long contentLength() { return mResponseBody.contentLength(); } @Override public BufferedSource source() { if (mBufferedSource == null) { mBufferedSource = Okio.buffer(source(mResponseBody.source())); } return mBufferedSource; } private Source source(Source source) { return new ForwardingSource(source) { private long mTotalBytesRead = 0L; @Override public long read(Buffer sink, long byteCount) throws IOException { long bytesRead = super.read(sink, byteCount); long fullLength = mResponseBody.contentLength(); if (bytesRead == -1) { // this source is exhausted mTotalBytesRead = fullLength; } else { mTotalBytesRead += bytesRead; } mProgressListener.update(mUrl, mTotalBytesRead, fullLength); return bytesRead; } }; } } }