Created
April 5, 2018 19:56
-
-
Save KirkBushman/c9a912f63165827cd247691946c6bf1e to your computer and use it in GitHub Desktop.
Progress loader on Gifs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class GlideLoader(private val imageView: ImageView, private val progressBar: ProgressBar) { | |
fun load(url: String, options: RequestOptions) { | |
onConnecting() | |
ProgressAppGlideModule.expect(url, object : ProgressAppGlideModule.UIonProgressListener { | |
override fun onProgress(bytesRead: Long, expectedLength: Long) { | |
progressBar.setProgress((100 * bytesRead / expectedLength).toInt()) | |
} | |
override fun getGranularityPercentage(): Float { | |
return 1.0f | |
} | |
}) | |
Glide.with(imageView.context) | |
.asGif() | |
.load(url) | |
.apply(options.skipMemoryCache(true)) | |
.listener(object : RequestListener<GifDrawable> { | |
override fun onResourceReady(resource: GifDrawable?, model: Any?, target: Target<GifDrawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean { | |
ProgressAppGlideModule.forget(url) | |
onFinished() | |
Log.d("Glide gif loader", "onResourceReady($resource, $model, $target, $dataSource, $isFirstResource)") | |
return false | |
} | |
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<GifDrawable>?, isFirstResource: Boolean): Boolean { | |
ProgressAppGlideModule.forget(url) | |
onFinished() | |
Log.d("Glide gif loader", "onException($e, $model, $target, $isFirstResource)") | |
return false | |
} | |
}) | |
.into(imageView) | |
} | |
private fun onConnecting() { | |
progressBar.visibility = View.VISIBLE | |
} | |
private fun onFinished() { | |
progressBar.visibility = View.GONE | |
imageView.visibility = View.VISIBLE | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class MediaZoomGifActivity : AppCompatActivity() { | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
setContentView(R.layout.my_activity_layout) | |
... | |
val url = intent.getStringExtra(MEDIA_URL) | |
val options = RequestOptions().priority(Priority.HIGH) | |
GlideLoader(media_gif_imageview, progress_bar).load(newUrl, options) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@GlideModule | |
class ProgressAppGlideModule : LibraryGlideModule() { | |
companion object { | |
fun forget(url: String) { | |
ProgressAppGlideModule.DispatchingProgressListener.forget(url) | |
} | |
fun expect(url: String, listener: ProgressAppGlideModule.UIonProgressListener) { | |
ProgressAppGlideModule.DispatchingProgressListener.expect(url, listener) | |
} | |
} | |
override fun registerComponents(context: Context, glide: Glide, registry: Registry) { | |
super.registerComponents(context, glide, registry) | |
val client = OkHttpClient.Builder() | |
.addNetworkInterceptor(object : Interceptor { | |
override fun intercept(chain: Interceptor.Chain): Response { | |
val request = chain.request() | |
val response = chain.proceed(request) | |
val listener = DispatchingProgressListener() | |
return response.newBuilder() | |
.body(OkHttpProgressResponseBody(request.url(), response.body()!!, listener)) | |
.build() | |
} | |
}) | |
.build() | |
registry.replace(GlideUrl::class.java, InputStream::class.java, OkHttpUrlLoader.Factory(client)) | |
} | |
private interface ResponseProgressListener { | |
fun update(url: HttpUrl, bytesRead: Long, contentLength: Long) | |
} | |
interface UIonProgressListener { | |
fun onProgress(bytesRead: Long, expectedLength: Long) | |
fun getGranularityPercentage(): Float | |
} | |
private class DispatchingProgressListener : ProgressAppGlideModule.ResponseProgressListener { | |
companion object { | |
private val LISTENERS = WeakHashMap<String, UIonProgressListener>() | |
private val PROGRESSES = WeakHashMap<String, Long>() | |
fun forget(url: String) { | |
LISTENERS.remove(url) | |
PROGRESSES.remove(url) | |
} | |
fun expect(url: String, listener: UIonProgressListener) { | |
LISTENERS[url] = listener | |
} | |
} | |
private val handler = Handler(Looper.getMainLooper()) | |
override fun update(url: HttpUrl, bytesRead: Long, contentLength: Long) { | |
val key = url.toString() | |
val listener = LISTENERS[key] ?: return | |
if(contentLength <= bytesRead) { | |
forget(key) | |
} | |
if(needsDispatch(key, bytesRead, contentLength, listener.getGranularityPercentage())) { | |
handler.post { | |
listener.onProgress(bytesRead, contentLength) | |
} | |
} | |
} | |
private fun needsDispatch(key: String, current: Long, total: Long, granularity: Float): Boolean { | |
if(granularity == 0f || current == 0L || total == current) { | |
return true | |
} | |
val percent = 100f * current / total | |
val currentProgress = (percent / granularity).toLong() | |
val lastProgress = PROGRESSES[key] | |
return if(lastProgress == null || currentProgress != lastProgress) { | |
PROGRESSES[key] = currentProgress | |
true | |
} else { | |
false | |
} | |
} | |
} | |
private class OkHttpProgressResponseBody( | |
private val url: HttpUrl, | |
private val responseBody: ResponseBody, | |
private val progressListener: ResponseProgressListener) : ResponseBody() { | |
private var bufferedSource: BufferedSource? = null | |
override fun contentType(): MediaType? { | |
return responseBody.contentType() | |
} | |
override fun contentLength(): Long { | |
return responseBody.contentLength() | |
} | |
override fun source(): BufferedSource { | |
if(bufferedSource == null) { | |
bufferedSource = Okio.buffer(source(responseBody.source())) | |
} | |
return bufferedSource!! | |
} | |
private fun source(source: Source): Source { | |
return object : ForwardingSource(source) { | |
var totalBytesRead = 0L | |
override fun read(sink: Buffer?, byteCount: Long): Long { | |
val bytesRead = super.read(sink!!, byteCount) | |
val fullLength = responseBody.contentLength() | |
if(bytesRead == -1L) { | |
totalBytesRead = fullLength | |
} else { | |
totalBytesRead += bytesRead | |
} | |
progressListener.update(url, totalBytesRead, fullLength) | |
return bytesRead | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment