diff --git a/picasso/README.md b/picasso/README.md index 06e00ba2a..18a4288af 100644 --- a/picasso/README.md +++ b/picasso/README.md @@ -89,6 +89,25 @@ PicassoImage( } ``` +## Custom Picasso + +If you wish to provide a default `Picasso` to use across all of your `PicassoImage` +calls, we provide the `AmbientPicasso` ambient. You can see it like so: + +``` kotlin +val picasso = Picasso.Builder(...) + // Customize as required + .build() + +Providers(AmbientPicasso provides picasso) { + // This will automatically use the value of AmbientPicasso + PicasoImage( + data = ... + ) +} +``` + + ## Download ```groovy diff --git a/picasso/api/picasso.api b/picasso/api/picasso.api index d7b765784..f1e02ffba 100644 --- a/picasso/api/picasso.api +++ b/picasso/api/picasso.api @@ -1,5 +1,6 @@ public final class dev/chrisbanes/accompanist/picasso/PicassoImage { public static final fun PicassoImage (Ljava/lang/Object;Landroidx/compose/ui/Modifier;Landroidx/compose/ui/Alignment;Landroidx/compose/ui/layout/ContentScale;Landroidx/compose/ui/graphics/ColorFilter;ZLcom/squareup/picasso/Picasso;Lkotlin/jvm/functions/Function2;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 PicassoImage (Ljava/lang/Object;Landroidx/compose/ui/Modifier;Lcom/squareup/picasso/Picasso;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V + public static final fun getAmbientPicasso ()Landroidx/compose/runtime/ProvidableAmbient; } diff --git a/picasso/src/androidTest/java/dev/chrisbanes/accompanist/picasso/PicassoTest.kt b/picasso/src/androidTest/java/dev/chrisbanes/accompanist/picasso/PicassoTest.kt index 8012960c8..754da2f05 100644 --- a/picasso/src/androidTest/java/dev/chrisbanes/accompanist/picasso/PicassoTest.kt +++ b/picasso/src/androidTest/java/dev/chrisbanes/accompanist/picasso/PicassoTest.kt @@ -19,12 +19,14 @@ package dev.chrisbanes.accompanist.picasso 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 import androidx.compose.ui.graphics.painter.ColorPainter import androidx.compose.ui.platform.testTag import androidx.compose.ui.unit.dp +import androidx.core.net.toUri import androidx.test.filters.LargeTest import androidx.test.filters.SdkSuppress import androidx.test.platform.app.InstrumentationRegistry @@ -223,6 +225,73 @@ class PicassoTest { .assertIsDisplayed() } + @SdkSuppress(minSdkVersion = 26) // captureToBitmap + @Test + fun customPicasso_param() { + val latch = CountDownLatch(1) + + val picasso = Picasso.Builder(InstrumentationRegistry.getInstrumentation().targetContext) + .requestTransformer { request -> + // Transform any request so that it points to a valid red image uri instead + request.buildUpon() + .setUri(server.url("/red").toString().toUri()) + .build() + } + .build() + + composeTestRule.setContent { + PicassoImage( + data = server.url("/noimage"), + picasso = picasso, + modifier = Modifier.preferredSize(128.dp, 128.dp).testTag(TestTags.Image), + onRequestCompleted = { latch.countDown() } + ) + } + + // Wait for the onRequestCompleted to release the latch + latch.await(5, TimeUnit.SECONDS) + + // Assert that the layout is displayed and that we're showing the red image + composeTestRule.onNodeWithTag(TestTags.Image) + .assertIsDisplayed() + .captureToBitmap() + .assertPixels { Color.Red } + } + + @SdkSuppress(minSdkVersion = 26) // captureToBitmap + @Test + fun customPicasso_ambient() { + val latch = CountDownLatch(1) + + val picasso = Picasso.Builder(InstrumentationRegistry.getInstrumentation().targetContext) + .requestTransformer { request -> + // Transform any request so that it points to a valid red image uri instead + request.buildUpon() + .setUri(server.url("/red").toString().toUri()) + .build() + } + .build() + + composeTestRule.setContent { + Providers(AmbientPicasso provides picasso) { + PicassoImage( + data = server.url("/noimage"), + modifier = Modifier.preferredSize(128.dp, 128.dp).testTag(TestTags.Image), + onRequestCompleted = { latch.countDown() } + ) + } + } + + // Wait for the onRequestCompleted to release the latch + latch.await(5, TimeUnit.SECONDS) + + // Assert that the layout is displayed and that we're showing the red image + composeTestRule.onNodeWithTag(TestTags.Image) + .assertIsDisplayed() + .captureToBitmap() + .assertPixels { Color.Red } + } + @Test fun errorStillHasSize() { val latch = CountDownLatch(1) diff --git a/picasso/src/main/java/dev/chrisbanes/accompanist/picasso/Picasso.kt b/picasso/src/main/java/dev/chrisbanes/accompanist/picasso/Picasso.kt index ab1f5c705..71d1c5fe2 100644 --- a/picasso/src/main/java/dev/chrisbanes/accompanist/picasso/Picasso.kt +++ b/picasso/src/main/java/dev/chrisbanes/accompanist/picasso/Picasso.kt @@ -24,6 +24,7 @@ import android.graphics.drawable.Drawable import android.net.Uri 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 @@ -47,6 +48,11 @@ import kotlinx.coroutines.suspendCancellableCoroutine import okhttp3.HttpUrl import java.io.File +/** + * Ambient containing the preferred [Picasso] to use in [PicassoImage]. Defaults to [Picasso.get]. + */ +val AmbientPicasso = staticAmbientOf { Picasso.get() } + /** * Creates a composable that will attempt to load the given [data] using [Picasso], and provides * complete content of how the current state is displayed: @@ -66,6 +72,8 @@ import java.io.File * * @param data The data to load. See [RequestCreator.data] for the types allowed. * @param modifier [Modifier] used to adjust the layout algorithm or draw decoration content. + * @param picasso The [Picasso] instance to use for requests. Defaults to the current value of + * [AmbientPicasso]. * @param requestBuilder Optional builder for the [RequestCreator]. * @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. @@ -76,7 +84,7 @@ import java.io.File fun PicassoImage( data: Any, modifier: Modifier = Modifier, - picasso: Picasso = Picasso.get(), + picasso: Picasso = AmbientPicasso.current, requestBuilder: (RequestCreator.(size: IntSize) -> RequestCreator)? = null, shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = DefaultRefetchOnSizeChangeLambda, onRequestCompleted: (ImageLoadState) -> Unit = EmptyRequestCompleteLambda, @@ -191,6 +199,8 @@ fun PicassoImage( * @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 picasso The [Picasso] instance to use for requests. Defaults to the current value + * of [AmbientPicasso]. * @param requestBuilder Optional builder for the [RequestCreator]. * @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. @@ -204,7 +214,7 @@ fun PicassoImage( contentScale: ContentScale = ContentScale.Fit, colorFilter: ColorFilter? = null, fadeIn: Boolean = false, - picasso: Picasso = Picasso.get(), + picasso: Picasso = AmbientPicasso.current, requestBuilder: (RequestCreator.(size: IntSize) -> RequestCreator)? = null, shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = DefaultRefetchOnSizeChangeLambda, onRequestCompleted: (ImageLoadState) -> Unit = EmptyRequestCompleteLambda,