Skip to content

Commit

Permalink
Add AmbientRequestManager for GlideImage (#129)
Browse files Browse the repository at this point in the history
* Add AmbientRequestManager for GlideImage

Similar to what has been added to Coil + Picasso

* Fix tests

* Remove unnecessary SdkSuppress

* Update glide/src/androidTest/java/dev/chrisbanes/accompanist/glide/GlideTest.kt

Co-authored-by: Nick Butcher <nickbutcher@users.noreply.github.com>

* Update glide/src/androidTest/java/dev/chrisbanes/accompanist/glide/GlideTest.kt

Co-authored-by: Nick Butcher <nickbutcher@users.noreply.github.com>

Co-authored-by: Nick Butcher <nickbutcher@users.noreply.github.com>
  • Loading branch information
chrisbanes and nickbutcher committed Oct 26, 2020
1 parent f2112d6 commit e3e1a4d
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 4 deletions.
22 changes: 22 additions & 0 deletions glide/README.md
Expand Up @@ -98,6 +98,28 @@ Accompanist Glide supports GIFs through Glide's own GIF support. There's nothing

![Example GIF](https://media.giphy.com/media/6oMKugqovQnjW/giphy.gif)

## Custom RequestManager

If you wish to provide a default `RequestManager` to use across all of your `GlideImage`
calls, we provide the `AmbientRequestManager` ambient.

You can use it like so:

``` kotlin
val requestManager = Glide.with(...)
// customize the RequestManager as needed
.build()

Providers(AmbientRequestManager provides requestManager) {
// This will automatically use the value of AmbientRequestManager
GlideImage(
data = ...
)
}
```

For more information on ambients, see [here](https://developer.android.com/reference/kotlin/androidx/compose/runtime/Ambient).

## Download

```groovy
Expand Down
6 changes: 6 additions & 0 deletions glide/api/glide.api
@@ -1,5 +1,11 @@
public final class dev/chrisbanes/accompanist/glide/GlideImage {
public static final fun GlideImage (Ljava/lang/Object;Landroidx/compose/ui/Modifier;Landroidx/compose/ui/Alignment;Landroidx/compose/ui/layout/ContentScale;Landroidx/compose/ui/graphics/ColorFilter;ZLkotlin/jvm/functions/Function2;Lcom/bumptech/glide/RequestManager;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V
public static final fun GlideImage (Ljava/lang/Object;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function2;Lcom/bumptech/glide/RequestManager;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V
public static final fun getAmbientRequestManager ()Landroidx/compose/runtime/ProvidableAmbient;
}

public final class dev/chrisbanes/accompanist/glide/GlideImageConstants {
public static final field INSTANCE Ldev/chrisbanes/accompanist/glide/GlideImageConstants;
public final fun defaultRequestManager (Landroidx/compose/runtime/Composer;I)Lcom/bumptech/glide/RequestManager;
}

Expand Up @@ -20,11 +20,13 @@ import android.graphics.drawable.Drawable
import androidx.compose.foundation.Image
import androidx.compose.foundation.Text
import androidx.compose.foundation.layout.preferredSize
import androidx.compose.runtime.Providers
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.onCommit
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.ColorPainter
import androidx.compose.ui.platform.ViewAmbient
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.dp
import androidx.test.filters.LargeTest
Expand Down Expand Up @@ -279,6 +281,57 @@ class GlideTest {
.assertIsDisplayed()
}

@Test
fun customRequestManager_param() {
val latch = CountDownLatch(1)
val loaded = mutableListOf<Any>()

composeTestRule.setContent {
// Create a RequestManager with a listener which updates our loaded list
val glide = Glide.with(ViewAmbient.current)
.addDefaultRequestListener(SimpleRequestListener { model -> loaded += model })

GlideImage(
data = server.url("/image").toString(),
requestManager = glide,
modifier = Modifier.preferredSize(128.dp, 128.dp),
onRequestCompleted = { latch.countDown() }
)
}

// Wait for the onRequestCompleted to release the latch
latch.await(5, TimeUnit.SECONDS)

// Assert that the listener was called
assertThat(loaded).hasSize(1)
}

@Test
fun customRequestManager_ambient() {
val latch = CountDownLatch(1)
val loaded = mutableListOf<Any>()

composeTestRule.setContent {
// Create a RequestManager with a listener which updates our loaded list
val glide = Glide.with(ViewAmbient.current)
.addDefaultRequestListener(SimpleRequestListener { model -> loaded += model })

Providers(AmbientRequestManager provides glide) {
GlideImage(
data = server.url("/image").toString(),
modifier = Modifier.preferredSize(128.dp, 128.dp),
onRequestCompleted = { latch.countDown() }
)
}
}

// Wait for the onRequestCompleted to release the latch
latch.await(5, TimeUnit.SECONDS)

// Assert that the listener was called
assertThat(loaded).hasSize(1)
}

@Test
fun errorStillHasSize() {
val latch = CountDownLatch(1)
Expand Down
@@ -0,0 +1,50 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package dev.chrisbanes.accompanist.glide

import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target

/**
* Simple wrapper around RequestListener for use in tests
*/
internal class SimpleRequestListener(
private val onComplete: (Any) -> Unit
) : RequestListener<Any> {
override fun onLoadFailed(
e: GlideException?,
model: Any,
target: Target<Any>?,
isFirstResource: Boolean
): Boolean {
onComplete(model)
return false
}

override fun onResourceReady(
resource: Any?,
model: Any,
target: Target<Any>?,
dataSource: DataSource?,
isFirstResource: Boolean
): Boolean {
onComplete(model)
return false
}
}
27 changes: 23 additions & 4 deletions glide/src/main/java/dev/chrisbanes/accompanist/glide/GlideImage.kt
Expand Up @@ -22,6 +22,7 @@ package dev.chrisbanes.accompanist.glide
import android.graphics.drawable.Drawable
import androidx.compose.foundation.Image
import androidx.compose.runtime.Composable
import androidx.compose.runtime.staticAmbientOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
Expand All @@ -46,6 +47,22 @@ import dev.chrisbanes.accompanist.imageloading.toPainter
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.suspendCancellableCoroutine

/**
* Ambient containing the preferred [RequestManager] to use in [GlideImage].
*/
val AmbientRequestManager = staticAmbientOf<RequestManager?> { null }

object GlideImageConstants {
/**
* Returns the default [RequestManager] value for the `requestManager` parameter
* in [GlideImage].
*/
@Composable
fun defaultRequestManager(): RequestManager {
return AmbientRequestManager.current ?: Glide.with(ViewAmbient.current)
}
}

/**
* Creates a composable that will attempt to load the given [data] using [Glide], and provides
* complete content of how the current state is displayed:
Expand All @@ -66,7 +83,8 @@ import kotlinx.coroutines.suspendCancellableCoroutine
* @param data The data to load.
* @param modifier [Modifier] used to adjust the layout algorithm or draw decoration content.
* @param requestBuilder Optional builder for the [RequestBuilder].
* @param requestManager The [RequestManager] to use when requesting the image. Defaults to `Glide.with(view)`
* @param requestManager The [RequestManager] to use when requesting the image. Defaults to the
* current value of [AmbientRequestManager].
* @param shouldRefetchOnSizeChange Lambda which will be invoked when the size changes, allowing
* optional re-fetching of the image. Return true to re-fetch the image.
* @param onRequestCompleted Listener which will be called when the loading request has finished.
Expand All @@ -77,7 +95,7 @@ fun GlideImage(
data: Any,
modifier: Modifier = Modifier,
requestBuilder: (RequestBuilder<Drawable>.(size: IntSize) -> RequestBuilder<Drawable>)? = null,
requestManager: RequestManager = Glide.with(ViewAmbient.current),
requestManager: RequestManager = GlideImageConstants.defaultRequestManager(),
shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = DefaultRefetchOnSizeChangeLambda,
onRequestCompleted: (ImageLoadState) -> Unit = EmptyRequestCompleteLambda,
content: @Composable (imageLoadState: ImageLoadState) -> Unit
Expand Down Expand Up @@ -133,7 +151,8 @@ fun GlideImage(
* @param fadeIn Whether to run a fade-in animation when images are successfully loaded.
* Default: `false`.
* @param requestBuilder Optional builder for the [RequestBuilder].
* @param requestManager The [RequestManager] to use when requesting the image. Defaults to `Glide.with(view)`
* @param requestManager The [RequestManager] to use when requesting the image. Defaults to the
* current value of [AmbientRequestManager].
* @param shouldRefetchOnSizeChange Lambda which will be invoked when the size changes, allowing
* optional re-fetching of the image. Return true to re-fetch the image.
* @param onRequestCompleted Listener which will be called when the loading request has finished.
Expand All @@ -147,7 +166,7 @@ fun GlideImage(
colorFilter: ColorFilter? = null,
fadeIn: Boolean = false,
requestBuilder: (RequestBuilder<Drawable>.(size: IntSize) -> RequestBuilder<Drawable>)? = null,
requestManager: RequestManager = Glide.with(ViewAmbient.current),
requestManager: RequestManager = GlideImageConstants.defaultRequestManager(),
shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = DefaultRefetchOnSizeChangeLambda,
onRequestCompleted: (ImageLoadState) -> Unit = EmptyRequestCompleteLambda,
error: @Composable ((ImageLoadState.Error) -> Unit)? = null,
Expand Down

0 comments on commit e3e1a4d

Please sign in to comment.