Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add AmbientImageLoader for Coil #127

Merged
merged 4 commits into from Oct 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 18 additions & 0 deletions coil/README.md
Expand Up @@ -93,6 +93,24 @@ CoilImage(

Accompanist Coil supports GIFs through Coil's own GIF support. Follow the [setup instructions](https://coil-kt.github.io/coil/gifs/) and it should just work.

## Custom ImageLoader

If you wish to provide a default [`ImageLoader`](https://coil-kt.github.io/coil/image_loaders/) to use across all of your `CoilImage`
calls, we provide the `AmbientImageLoader` ambient. You can see it like so:
chrisbanes marked this conversation as resolved.
Show resolved Hide resolved

``` kotlin
val imageLoader = ImageLoader.Builder(context)
// customize the ImageLoader as needed
.build()

Providers(AmbientImageLoader provides imageLoader) {
// This will automatically use the value of AmbientImageLoader
CoilImage(
data = ...
)
}
```

## Download

```groovy
Expand Down
6 changes: 6 additions & 0 deletions coil/api/coil.api
Expand Up @@ -8,5 +8,11 @@ public final class dev/chrisbanes/accompanist/coil/CoilImage {
public static fun ErrorResult$annotations ()V
public static fun RequestResult$annotations ()V
public static fun SuccessResult$annotations ()V
public static final fun getAmbientImageLoader ()Landroidx/compose/runtime/ProvidableAmbient;
}

public final class dev/chrisbanes/accompanist/coil/CoilImageConstants {
public static final field INSTANCE Ldev/chrisbanes/accompanist/coil/CoilImageConstants;
public final fun defaultImageLoader (Landroidx/compose/runtime/Composer;I)Lcoil/ImageLoader;
}

Expand Up @@ -19,6 +19,7 @@ package dev.chrisbanes.accompanist.coil
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.ui.Modifier
import androidx.compose.ui.graphics.Color
Expand Down Expand Up @@ -67,6 +68,7 @@ import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger

@LargeTest
@RunWith(JUnit4::class)
Expand Down Expand Up @@ -194,11 +196,10 @@ class CoilTest {

// Build a custom ImageLoader with a fake EventListener
val eventListener = object : EventListener {
var startCalled = 0
private set
val startCalled = AtomicInteger()

override fun fetchStart(request: ImageRequest, fetcher: Fetcher<*>, options: Options) {
startCalled++
startCalled.incrementAndGet()
}
}
val imageLoader = ImageLoader.Builder(context)
Expand All @@ -218,7 +219,42 @@ class CoilTest {
latch.await(5, TimeUnit.SECONDS)

// Verify that our eventListener was invoked
assertThat(eventListener.startCalled).isAtLeast(1)
assertThat(eventListener.startCalled.get()).isAtLeast(1)
}

@OptIn(ExperimentalCoilApi::class)
@Test
fun basicLoad_customImageLoader_ambient() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val latch = CountDownLatch(1)

// Build a custom ImageLoader with a fake EventListener
val eventListener = object : EventListener {
val startCalled = AtomicInteger()

override fun fetchStart(request: ImageRequest, fetcher: Fetcher<*>, options: Options) {
startCalled.incrementAndGet()
}
}
val imageLoader = ImageLoader.Builder(context)
.eventListener(eventListener)
.build()

composeTestRule.setContent {
Providers(AmbientImageLoader provides imageLoader) {
CoilImage(
data = server.url("/image"),
modifier = Modifier.preferredSize(128.dp, 128.dp),
onRequestCompleted = { latch.countDown() }
)
}
}

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

// Verify that our eventListener was invoked
assertThat(eventListener.startCalled.get()).isAtLeast(1)
}

@OptIn(ExperimentalCoroutinesApi::class)
Expand Down
40 changes: 28 additions & 12 deletions coil/src/main/java/dev/chrisbanes/accompanist/coil/Coil.kt
Expand Up @@ -22,6 +22,7 @@ package dev.chrisbanes.accompanist.coil
import androidx.compose.foundation.Image
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.staticAmbientOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
Expand All @@ -43,6 +44,21 @@ import dev.chrisbanes.accompanist.imageloading.ImageLoadState
import dev.chrisbanes.accompanist.imageloading.MaterialLoadingImage
import dev.chrisbanes.accompanist.imageloading.toPainter

/**
* Ambient containing the preferred [ImageLoader] to use in [CoilImage].
*/
val AmbientImageLoader = staticAmbientOf<ImageLoader?> { null }

object CoilImageConstants {
chrisbanes marked this conversation as resolved.
Show resolved Hide resolved
/**
* Returns the default [ImageLoader] value for the `imageLoader` parameter in [CoilImage].
*/
@Composable
fun defaultImageLoader(): ImageLoader {
return AmbientImageLoader.current ?: ContextAmbient.current.imageLoader
}
}

/**
* Creates a composable that will attempt to load the given [data] using [Coil], and provides
* complete content of how the current state is displayed:
Expand All @@ -63,8 +79,8 @@ import dev.chrisbanes.accompanist.imageloading.toPainter
* @param data The data to load. See [ImageRequest.Builder.data] for the types allowed.
* @param modifier [Modifier] used to adjust the layout algorithm or draw decoration content.
* @param requestBuilder Optional builder for the [ImageRequest].
* @param imageLoader The [ImageLoader] to use when requesting the image. Defaults to [Coil]'s
* default image loader.
* @param imageLoader The [ImageLoader] to use when requesting the image. Defaults to
* [CoilImageConstants.defaultImageLoader].
* @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 @@ -75,7 +91,7 @@ fun CoilImage(
data: Any,
modifier: Modifier = Modifier,
requestBuilder: (ImageRequest.Builder.(size: IntSize) -> ImageRequest.Builder)? = null,
imageLoader: ImageLoader = ContextAmbient.current.imageLoader,
imageLoader: ImageLoader = CoilImageConstants.defaultImageLoader(),
shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = DefaultRefetchOnSizeChangeLambda,
onRequestCompleted: (ImageLoadState) -> Unit = EmptyRequestCompleteLambda,
content: @Composable (imageLoadState: ImageLoadState) -> Unit
Expand Down Expand Up @@ -112,8 +128,8 @@ fun CoilImage(
* set, one will be set on the request using the layout constraints.
* @param modifier [Modifier] used to adjust the layout algorithm or draw decoration content.
* @param requestBuilder Optional builder for the [ImageRequest].
* @param imageLoader The [ImageLoader] to use when requesting the image. Defaults to [Coil]'s
* default image loader.
* @param imageLoader The [ImageLoader] to use when requesting the image. Defaults to
* [CoilImageConstants.defaultImageLoader].
* @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 @@ -124,7 +140,7 @@ fun CoilImage(
request: ImageRequest,
modifier: Modifier = Modifier,
requestBuilder: (ImageRequest.Builder.(size: IntSize) -> ImageRequest.Builder)? = null,
imageLoader: ImageLoader = ContextAmbient.current.imageLoader,
imageLoader: ImageLoader = CoilImageConstants.defaultImageLoader(),
shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = DefaultRefetchOnSizeChangeLambda,
onRequestCompleted: (ImageLoadState) -> Unit = EmptyRequestCompleteLambda,
content: @Composable (imageLoadState: ImageLoadState) -> Unit
Expand Down Expand Up @@ -196,8 +212,8 @@ fun CoilImage(
* @param fadeIn Whether to run a fade-in animation when images are successfully loaded.
* Default: `false`.
* @param requestBuilder Optional builder for the [ImageRequest].
* @param imageLoader The [ImageLoader] to use when requesting the image. Defaults to [Coil]'s
* default image loader.
* @param imageLoader The [ImageLoader] to use when requesting the image. Defaults to
* [CoilImageConstants.defaultImageLoader].
* @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 @@ -211,7 +227,7 @@ fun CoilImage(
colorFilter: ColorFilter? = null,
fadeIn: Boolean = false,
requestBuilder: (ImageRequest.Builder.(size: IntSize) -> ImageRequest.Builder)? = null,
imageLoader: ImageLoader = ContextAmbient.current.imageLoader,
imageLoader: ImageLoader = CoilImageConstants.defaultImageLoader(),
shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = DefaultRefetchOnSizeChangeLambda,
onRequestCompleted: (ImageLoadState) -> Unit = EmptyRequestCompleteLambda,
error: @Composable ((ImageLoadState.Error) -> Unit)? = null,
Expand Down Expand Up @@ -269,8 +285,8 @@ fun CoilImage(
* @param loading Content to be displayed when the request is in progress.
* @param fadeIn Whether to run a fade-in animation when images are successfully loaded. Default: `false`.
* @param requestBuilder Optional builder for the [ImageRequest].
* @param imageLoader The [ImageLoader] to use when requesting the image. Defaults to [Coil]'s
* default image loader.
* @param imageLoader The [ImageLoader] to use when requesting the image. Defaults to
* [CoilImageConstants.defaultImageLoader].
* @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 @@ -284,7 +300,7 @@ fun CoilImage(
colorFilter: ColorFilter? = null,
fadeIn: Boolean = false,
requestBuilder: (ImageRequest.Builder.(size: IntSize) -> ImageRequest.Builder)? = null,
imageLoader: ImageLoader = ContextAmbient.current.imageLoader,
imageLoader: ImageLoader = CoilImageConstants.defaultImageLoader(),
shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = DefaultRefetchOnSizeChangeLambda,
onRequestCompleted: (ImageLoadState) -> Unit = EmptyRequestCompleteLambda,
error: @Composable ((ImageLoadState.Error) -> Unit)? = null,
Expand Down