diff --git a/coil-compose-base/src/main/java/coil/compose/AsyncImagePainter.kt b/coil-compose-base/src/main/java/coil/compose/AsyncImagePainter.kt index 87c26f6ccd..4563071dc5 100644 --- a/coil-compose-base/src/main/java/coil/compose/AsyncImagePainter.kt +++ b/coil-compose-base/src/main/java/coil/compose/AsyncImagePainter.kt @@ -156,7 +156,7 @@ fun rememberAsyncImagePainter( @Stable class AsyncImagePainter internal constructor( request: ImageRequest, - imageLoader: ImageLoader + imageLoader: ImageLoader, ) : Painter(), RememberObserver { private var rememberScope: CoroutineScope? = null @@ -263,9 +263,11 @@ class AsyncImagePainter internal constructor( /** Update the [request] to work with [AsyncImagePainter]. */ private fun updateRequest(request: ImageRequest): ImageRequest { return request.newBuilder() - .target(onStart = { placeholder -> - updateState(State.Loading(placeholder?.toPainter())) - }) + .target( + onStart = { placeholder -> + updateState(State.Loading(placeholder?.toPainter())) + }, + ) .apply { if (request.defined.sizeResolver == null) { // If no other size resolver is set, suspend until the canvas size is positive. @@ -318,7 +320,7 @@ class AsyncImagePainter internal constructor( contentScale = contentScale, durationMillis = transition.durationMillis, fadeStart = result !is SuccessResult || !result.isPlaceholderCached, - preferExactIntrinsicSize = transition.preferExactIntrinsicSize + preferExactIntrinsicSize = transition.preferExactIntrinsicSize, ) } else { return null @@ -379,7 +381,7 @@ private fun validateRequest(request: ImageRequest) { when (request.data) { is ImageRequest.Builder -> unsupportedData( name = "ImageRequest.Builder", - description = "Did you forget to call ImageRequest.Builder.build()?" + description = "Did you forget to call ImageRequest.Builder.build()?", ) is ImageBitmap -> unsupportedData("ImageBitmap") is ImageVector -> unsupportedData("ImageVector") @@ -390,7 +392,7 @@ private fun validateRequest(request: ImageRequest) { private fun unsupportedData( name: String, - description: String = "If you wish to display this $name, use androidx.compose.foundation.Image." + description: String = "If you wish to display this $name, use androidx.compose.foundation.Image.", ): Nothing = throw IllegalArgumentException("Unsupported type: $name. $description") private val Size.isPositive get() = width >= 0.5 && height >= 0.5 @@ -399,7 +401,7 @@ private fun Size.toSizeOrNull() = when { isUnspecified -> CoilSize.ORIGINAL isPositive -> CoilSize( width = if (width.isFinite()) Dimension(width.roundToInt()) else Dimension.Undefined, - height = if (height.isFinite()) Dimension(height.roundToInt()) else Dimension.Undefined + height = if (height.isFinite()) Dimension(height.roundToInt()) else Dimension.Undefined, ) else -> null } diff --git a/coil-test-paparazzi/src/test/java/coil/test/PaparazziTest.kt b/coil-test-paparazzi/src/test/java/coil/test/PaparazziTest.kt index 28250adcf9..e02879bec5 100644 --- a/coil-test-paparazzi/src/test/java/coil/test/PaparazziTest.kt +++ b/coil-test-paparazzi/src/test/java/coil/test/PaparazziTest.kt @@ -3,13 +3,18 @@ package coil.test import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.widget.ImageView +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale -import app.cash.paparazzi.DeviceConfig.Companion.PIXEL_6 +import app.cash.paparazzi.DeviceConfig import app.cash.paparazzi.Paparazzi import coil.ImageLoader import coil.compose.AsyncImage +import coil.compose.rememberAsyncImagePainter import coil.decode.ImageSource import coil.request.ImageRequest +import coil.size.Size import kotlin.test.assertTrue import okio.Buffer import org.junit.Rule @@ -19,13 +24,16 @@ class PaparazziTest { @get:Rule val paparazzi = Paparazzi( - deviceConfig = PIXEL_6, + deviceConfig = DeviceConfig( + screenWidth = 320, + screenHeight = 470, + ), theme = "android:Theme.Material.Light.NoActionBar.Fullscreen", showSystemUi = false, ) @Test - fun loadView() { + fun imageView() { val url = "https://www.example.com/image.jpg" val drawable = object : ColorDrawable(Color.RED) { override fun getIntrinsicWidth() = 100 @@ -51,9 +59,8 @@ class PaparazziTest { } @Test - fun loadCompose() { + fun asyncImage() { val url = "https://www.example.com/image.jpg" - // Wrap the color drawable so it isn't automatically converted into a ColorPainter. val drawable = object : ColorDrawable(Color.RED) { override fun getIntrinsicWidth() = 100 override fun getIntrinsicHeight() = 100 @@ -71,6 +78,38 @@ class PaparazziTest { contentDescription = null, imageLoader = imageLoader, contentScale = ContentScale.None, + modifier = Modifier.fillMaxSize(), + ) + } + } + + @Test + fun rememberAsyncImagePainter() { + val url = "https://www.example.com/image.jpg" + val drawable = object : ColorDrawable(Color.RED) { + override fun getIntrinsicWidth() = 100 + override fun getIntrinsicHeight() = 100 + } + val engine = FakeImageLoaderEngine.Builder() + .intercept(url, drawable) + .build() + val imageLoader = ImageLoader.Builder(paparazzi.context) + .components { add(engine) } + .build() + + paparazzi.snapshot { + Image( + painter = rememberAsyncImagePainter( + // TODO: Figure out how to avoid having to specify an immediate size. + model = ImageRequest.Builder(paparazzi.context) + .data(url) + .size(Size.ORIGINAL) + .build(), + imageLoader = imageLoader, + ), + contentDescription = null, + contentScale = ContentScale.None, + modifier = Modifier.fillMaxSize(), ) } } diff --git a/coil-test-roborazzi/src/test/snapshots/images/coil.test.RoborazziComposeTest.loadCompose.png b/coil-test-paparazzi/src/test/snapshots/images/coil.test_PaparazziTest_asyncImage.png similarity index 100% rename from coil-test-roborazzi/src/test/snapshots/images/coil.test.RoborazziComposeTest.loadCompose.png rename to coil-test-paparazzi/src/test/snapshots/images/coil.test_PaparazziTest_asyncImage.png diff --git a/coil-test-roborazzi/src/test/snapshots/images/coil.test.RoborazziViewTest.loadView.png b/coil-test-paparazzi/src/test/snapshots/images/coil.test_PaparazziTest_imageView.png similarity index 100% rename from coil-test-roborazzi/src/test/snapshots/images/coil.test.RoborazziViewTest.loadView.png rename to coil-test-paparazzi/src/test/snapshots/images/coil.test_PaparazziTest_imageView.png diff --git a/coil-test-paparazzi/src/test/snapshots/images/coil.test_PaparazziTest_loadCompose.png b/coil-test-paparazzi/src/test/snapshots/images/coil.test_PaparazziTest_loadCompose.png deleted file mode 100644 index ed862f8e29..0000000000 Binary files a/coil-test-paparazzi/src/test/snapshots/images/coil.test_PaparazziTest_loadCompose.png and /dev/null differ diff --git a/coil-test-paparazzi/src/test/snapshots/images/coil.test_PaparazziTest_loadView.png b/coil-test-paparazzi/src/test/snapshots/images/coil.test_PaparazziTest_loadView.png deleted file mode 100644 index ed862f8e29..0000000000 Binary files a/coil-test-paparazzi/src/test/snapshots/images/coil.test_PaparazziTest_loadView.png and /dev/null differ diff --git a/coil-test-paparazzi/src/test/snapshots/images/coil.test_PaparazziTest_rememberAsyncImagePainter.png b/coil-test-paparazzi/src/test/snapshots/images/coil.test_PaparazziTest_rememberAsyncImagePainter.png new file mode 100644 index 0000000000..cfd10d8002 Binary files /dev/null and b/coil-test-paparazzi/src/test/snapshots/images/coil.test_PaparazziTest_rememberAsyncImagePainter.png differ diff --git a/coil-test-roborazzi/src/test/java/coil/test/RoborazziComposeTest.kt b/coil-test-roborazzi/src/test/java/coil/test/RoborazziComposeTest.kt index adc40acde5..c668cc0d0f 100644 --- a/coil-test-roborazzi/src/test/java/coil/test/RoborazziComposeTest.kt +++ b/coil-test-roborazzi/src/test/java/coil/test/RoborazziComposeTest.kt @@ -2,6 +2,7 @@ package coil.test import android.graphics.Color import android.graphics.drawable.ColorDrawable +import androidx.compose.foundation.Image import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale @@ -10,6 +11,9 @@ import androidx.compose.ui.test.onRoot import androidx.test.ext.junit.runners.AndroidJUnit4 import coil.ImageLoader import coil.compose.AsyncImage +import coil.compose.rememberAsyncImagePainter +import coil.request.ImageRequest +import coil.size.Size import coil.util.ComposeTestActivity import com.github.takahirom.roborazzi.RoborazziRule import org.junit.Rule @@ -29,14 +33,14 @@ class RoborazziComposeTest { composeRule = composeTestRule, captureRoot = composeTestRule.onRoot(), options = RoborazziRule.Options( + captureType = RoborazziRule.CaptureType.LastImage(), outputDirectoryPath = "src/test/snapshots/images", ) ) @Test - fun loadCompose() { + fun asyncImage() { val url = "https://www.example.com/image.jpg" - // Wrap the color drawable so it isn't automatically converted into a ColorPainter. val drawable = object : ColorDrawable(Color.RED) { override fun getIntrinsicWidth() = 100 override fun getIntrinsicHeight() = 100 @@ -58,4 +62,35 @@ class RoborazziComposeTest { ) } } + + @Test + fun rememberAsyncImagePainter() { + val url = "https://www.example.com/image.jpg" + val drawable = object : ColorDrawable(Color.RED) { + override fun getIntrinsicWidth() = 100 + override fun getIntrinsicHeight() = 100 + } + val engine = FakeImageLoaderEngine.Builder() + .intercept(url, drawable) + .build() + val imageLoader = ImageLoader.Builder(composeTestRule.activity) + .components { add(engine) } + .build() + + composeTestRule.setContent { + Image( + // TODO: Figure out how to avoid having to specify an immediate size. + painter = rememberAsyncImagePainter( + model = ImageRequest.Builder(composeTestRule.activity) + .data(url) + .size(Size.ORIGINAL) + .build(), + imageLoader = imageLoader, + ), + contentDescription = null, + contentScale = ContentScale.None, + modifier = Modifier.fillMaxSize(), + ) + } + } } diff --git a/coil-test-roborazzi/src/test/java/coil/test/RoborazziViewTest.kt b/coil-test-roborazzi/src/test/java/coil/test/RoborazziViewTest.kt index 25be876550..709a4cf40f 100644 --- a/coil-test-roborazzi/src/test/java/coil/test/RoborazziViewTest.kt +++ b/coil-test-roborazzi/src/test/java/coil/test/RoborazziViewTest.kt @@ -11,7 +11,7 @@ import coil.ImageLoader import coil.request.ImageRequest import coil.util.ViewTestActivity import coil.util.activity -import com.github.takahirom.roborazzi.captureRoboImage +import com.github.takahirom.roborazzi.RoborazziRule import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -24,8 +24,17 @@ class RoborazziViewTest { @get:Rule val activityRule = activityScenarioRule() + @get:Rule + val roborazziRule = RoborazziRule( + captureRoot = onView(isRoot()), + options = RoborazziRule.Options( + captureType = RoborazziRule.CaptureType.LastImage(), + outputDirectoryPath = "src/test/snapshots/images", + ) + ) + @Test - fun loadView() { + fun imageView() { val url = "https://www.example.com/image.jpg" val drawable = object : ColorDrawable(Color.RED) { override fun getIntrinsicWidth() = 100 @@ -47,9 +56,5 @@ class RoborazziViewTest { // Don't suspend to test that the image view is updated synchronously. imageLoader.enqueue(request) - - // https://github.com/takahirom/roborazzi/issues/9 - onView(isRoot()) - .captureRoboImage("src/test/snapshots/images/coil.test.RoborazziViewTest.loadView.png") } } diff --git a/coil-test-roborazzi/src/test/snapshots/images/coil.test.RoborazziComposeTest.asyncImage.png b/coil-test-roborazzi/src/test/snapshots/images/coil.test.RoborazziComposeTest.asyncImage.png new file mode 100644 index 0000000000..cfd10d8002 Binary files /dev/null and b/coil-test-roborazzi/src/test/snapshots/images/coil.test.RoborazziComposeTest.asyncImage.png differ diff --git a/coil-test-roborazzi/src/test/snapshots/images/coil.test.RoborazziComposeTest.rememberAsyncImagePainter.png b/coil-test-roborazzi/src/test/snapshots/images/coil.test.RoborazziComposeTest.rememberAsyncImagePainter.png new file mode 100644 index 0000000000..cfd10d8002 Binary files /dev/null and b/coil-test-roborazzi/src/test/snapshots/images/coil.test.RoborazziComposeTest.rememberAsyncImagePainter.png differ diff --git a/coil-test-roborazzi/src/test/snapshots/images/coil.test.RoborazziViewTest.imageView.png b/coil-test-roborazzi/src/test/snapshots/images/coil.test.RoborazziViewTest.imageView.png new file mode 100644 index 0000000000..cfd10d8002 Binary files /dev/null and b/coil-test-roborazzi/src/test/snapshots/images/coil.test.RoborazziViewTest.imageView.png differ diff --git a/coil-test/src/main/java/coil/test/FakeImageLoaderEngine.kt b/coil-test/src/main/java/coil/test/FakeImageLoaderEngine.kt index 3846c9dd68..d215862841 100644 --- a/coil-test/src/main/java/coil/test/FakeImageLoaderEngine.kt +++ b/coil-test/src/main/java/coil/test/FakeImageLoaderEngine.kt @@ -114,7 +114,9 @@ class FakeImageLoaderEngine private constructor( error("No interceptors handled this request and no fallback is set: ${chain.request.data}") } requestTransformer = RequestTransformer { request -> - request.newBuilder().transitionFactory(Transition.Factory.NONE).build() + request.newBuilder() + .transitionFactory(Transition.Factory.NONE) + .build() } }