Skip to content

Commit

Permalink
Attempt to execute image request in Android Studio previews. (#2329)
Browse files Browse the repository at this point in the history
* Attempt to execute image request in Android Studio previews.

* Fix tests.

* Inline.

* Rework API.

* Spotless.
  • Loading branch information
colinrtwhite authored Jun 26, 2024
1 parent 6ada1f8 commit 33ea5bc
Show file tree
Hide file tree
Showing 12 changed files with 149 additions and 125 deletions.
8 changes: 7 additions & 1 deletion coil-compose-core/api/android/coil-compose-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public final class coil3/compose/AsyncImagePainterKt {
public abstract interface class coil3/compose/AsyncImagePreviewHandler {
public static final field Companion Lcoil3/compose/AsyncImagePreviewHandler$Companion;
public static final field Default Lcoil3/compose/AsyncImagePreviewHandler;
public abstract fun handle (Lcoil3/ImageLoader;Lcoil3/request/ImageRequest;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun handle (Lcoil3/ImageLoader;Lcoil3/request/ImageRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class coil3/compose/AsyncImagePreviewHandler$Companion {
Expand Down Expand Up @@ -126,7 +126,13 @@ public final class coil3/compose/ImagePainter : androidx/compose/ui/graphics/pai
public fun getIntrinsicSize-NH-jbRc ()J
}

public final class coil3/compose/ImagePainter_androidKt {
public static final fun asPainter-55t9-rM (Lcoil3/Image;Landroid/content/Context;I)Landroidx/compose/ui/graphics/painter/Painter;
public static synthetic fun asPainter-55t9-rM$default (Lcoil3/Image;Landroid/content/Context;IILjava/lang/Object;)Landroidx/compose/ui/graphics/painter/Painter;
}

public final class coil3/compose/LocalAsyncImagePreviewHandlerKt {
public static final fun AsyncImagePreviewHandler (Lkotlin/jvm/functions/Function2;)Lcoil3/compose/AsyncImagePreviewHandler;
public static final fun getLocalAsyncImagePreviewHandler ()Landroidx/compose/runtime/ProvidableCompositionLocal;
}

Expand Down
4 changes: 3 additions & 1 deletion coil-compose-core/api/coil-compose-core.klib.api
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

// Library unique name: <io.coil-kt.coil3:coil-compose-core>
abstract fun interface coil3.compose/AsyncImagePreviewHandler { // coil3.compose/AsyncImagePreviewHandler|null[0]
abstract suspend fun handle(coil3/ImageLoader, coil3.request/ImageRequest, kotlin/Function1<coil3/Image, androidx.compose.ui.graphics.painter/Painter>): coil3.compose/AsyncImagePainter.State // coil3.compose/AsyncImagePreviewHandler.handle|handle(coil3.ImageLoader;coil3.request.ImageRequest;kotlin.Function1<coil3.Image,androidx.compose.ui.graphics.painter.Painter>){}[0]
abstract suspend fun handle(coil3/ImageLoader, coil3.request/ImageRequest): coil3.compose/AsyncImagePainter.State // coil3.compose/AsyncImagePreviewHandler.handle|handle(coil3.ImageLoader;coil3.request.ImageRequest){}[0]
final object Companion { // coil3.compose/AsyncImagePreviewHandler.Companion|null[0]
final val Default // coil3.compose/AsyncImagePreviewHandler.Companion.Default|{}Default[0]
final fun <get-Default>(): coil3.compose/AsyncImagePreviewHandler // coil3.compose/AsyncImagePreviewHandler.Companion.Default.<get-Default>|<get-Default>(){}[0]
Expand Down Expand Up @@ -116,13 +116,15 @@ final class coil3.compose/ImagePainter : androidx.compose.ui.graphics.painter/Pa
final fun <get-intrinsicSize>(): androidx.compose.ui.geometry/Size // coil3.compose/ImagePainter.intrinsicSize.<get-intrinsicSize>|<get-intrinsicSize>(){}[0]
}
final fun (coil3.compose/SubcomposeAsyncImageScope).coil3.compose/SubcomposeAsyncImageContent(androidx.compose.ui/Modifier?, androidx.compose.ui.graphics.painter/Painter?, kotlin/String?, androidx.compose.ui/Alignment?, androidx.compose.ui.layout/ContentScale?, kotlin/Float, androidx.compose.ui.graphics/ColorFilter?, kotlin/Boolean, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int) // coil3.compose/SubcomposeAsyncImageContent|SubcomposeAsyncImageContent@coil3.compose.SubcomposeAsyncImageScope(androidx.compose.ui.Modifier?;androidx.compose.ui.graphics.painter.Painter?;kotlin.String?;androidx.compose.ui.Alignment?;androidx.compose.ui.layout.ContentScale?;kotlin.Float;androidx.compose.ui.graphics.ColorFilter?;kotlin.Boolean;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0]
final fun (coil3/Image).coil3.compose/asPainter(coil3/PlatformContext, androidx.compose.ui.graphics/FilterQuality = ...): androidx.compose.ui.graphics.painter/Painter // coil3.compose/asPainter|asPainter@coil3.Image(coil3.PlatformContext;androidx.compose.ui.graphics.FilterQuality){}[0]
final fun coil3.compose/AsyncImage(kotlin/Any?, kotlin/String?, coil3/ImageLoader, androidx.compose.ui/Modifier?, androidx.compose.ui.graphics.painter/Painter?, androidx.compose.ui.graphics.painter/Painter?, androidx.compose.ui.graphics.painter/Painter?, kotlin/Function1<coil3.compose/AsyncImagePainter.State.Loading, kotlin/Unit>?, kotlin/Function1<coil3.compose/AsyncImagePainter.State.Success, kotlin/Unit>?, kotlin/Function1<coil3.compose/AsyncImagePainter.State.Error, kotlin/Unit>?, androidx.compose.ui/Alignment?, androidx.compose.ui.layout/ContentScale?, kotlin/Float, androidx.compose.ui.graphics/ColorFilter?, androidx.compose.ui.graphics/FilterQuality, kotlin/Boolean, coil3.compose/EqualityDelegate?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int, kotlin/Int) // coil3.compose/AsyncImage|AsyncImage(kotlin.Any?;kotlin.String?;coil3.ImageLoader;androidx.compose.ui.Modifier?;androidx.compose.ui.graphics.painter.Painter?;androidx.compose.ui.graphics.painter.Painter?;androidx.compose.ui.graphics.painter.Painter?;kotlin.Function1<coil3.compose.AsyncImagePainter.State.Loading,kotlin.Unit>?;kotlin.Function1<coil3.compose.AsyncImagePainter.State.Success,kotlin.Unit>?;kotlin.Function1<coil3.compose.AsyncImagePainter.State.Error,kotlin.Unit>?;androidx.compose.ui.Alignment?;androidx.compose.ui.layout.ContentScale?;kotlin.Float;androidx.compose.ui.graphics.ColorFilter?;androidx.compose.ui.graphics.FilterQuality;kotlin.Boolean;coil3.compose.EqualityDelegate?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int;kotlin.Int){}[0]
final fun coil3.compose/AsyncImage(kotlin/Any?, kotlin/String?, coil3/ImageLoader, androidx.compose.ui/Modifier?, kotlin/Function1<coil3.compose/AsyncImagePainter.State, coil3.compose/AsyncImagePainter.State>?, kotlin/Function1<coil3.compose/AsyncImagePainter.State, kotlin/Unit>?, androidx.compose.ui/Alignment?, androidx.compose.ui.layout/ContentScale?, kotlin/Float, androidx.compose.ui.graphics/ColorFilter?, androidx.compose.ui.graphics/FilterQuality, kotlin/Boolean, coil3.compose/EqualityDelegate?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int, kotlin/Int) // coil3.compose/AsyncImage|AsyncImage(kotlin.Any?;kotlin.String?;coil3.ImageLoader;androidx.compose.ui.Modifier?;kotlin.Function1<coil3.compose.AsyncImagePainter.State,coil3.compose.AsyncImagePainter.State>?;kotlin.Function1<coil3.compose.AsyncImagePainter.State,kotlin.Unit>?;androidx.compose.ui.Alignment?;androidx.compose.ui.layout.ContentScale?;kotlin.Float;androidx.compose.ui.graphics.ColorFilter?;androidx.compose.ui.graphics.FilterQuality;kotlin.Boolean;coil3.compose.EqualityDelegate?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int;kotlin.Int){}[0]
final fun coil3.compose/DrawScopeSizeResolver(): coil3.compose/DrawScopeSizeResolver // coil3.compose/DrawScopeSizeResolver|DrawScopeSizeResolver(){}[0]
final fun coil3.compose/SubcomposeAsyncImage(kotlin/Any?, kotlin/String?, coil3/ImageLoader, androidx.compose.ui/Modifier?, kotlin/Function1<coil3.compose/AsyncImagePainter.State, coil3.compose/AsyncImagePainter.State>?, kotlin/Function1<coil3.compose/AsyncImagePainter.State, kotlin/Unit>?, androidx.compose.ui/Alignment?, androidx.compose.ui.layout/ContentScale?, kotlin/Float, androidx.compose.ui.graphics/ColorFilter?, androidx.compose.ui.graphics/FilterQuality, kotlin/Boolean, coil3.compose/EqualityDelegate?, kotlin/Function3<coil3.compose/SubcomposeAsyncImageScope, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int, kotlin/Int) // coil3.compose/SubcomposeAsyncImage|SubcomposeAsyncImage(kotlin.Any?;kotlin.String?;coil3.ImageLoader;androidx.compose.ui.Modifier?;kotlin.Function1<coil3.compose.AsyncImagePainter.State,coil3.compose.AsyncImagePainter.State>?;kotlin.Function1<coil3.compose.AsyncImagePainter.State,kotlin.Unit>?;androidx.compose.ui.Alignment?;androidx.compose.ui.layout.ContentScale?;kotlin.Float;androidx.compose.ui.graphics.ColorFilter?;androidx.compose.ui.graphics.FilterQuality;kotlin.Boolean;coil3.compose.EqualityDelegate?;kotlin.Function3<coil3.compose.SubcomposeAsyncImageScope,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int;kotlin.Int){}[0]
final fun coil3.compose/SubcomposeAsyncImage(kotlin/Any?, kotlin/String?, coil3/ImageLoader, androidx.compose.ui/Modifier?, kotlin/Function1<coil3.compose/AsyncImagePainter.State, coil3.compose/AsyncImagePainter.State>?, kotlin/Function4<coil3.compose/SubcomposeAsyncImageScope, coil3.compose/AsyncImagePainter.State.Loading, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>?, kotlin/Function4<coil3.compose/SubcomposeAsyncImageScope, coil3.compose/AsyncImagePainter.State.Success, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>?, kotlin/Function4<coil3.compose/SubcomposeAsyncImageScope, coil3.compose/AsyncImagePainter.State.Error, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>?, kotlin/Function1<coil3.compose/AsyncImagePainter.State.Loading, kotlin/Unit>?, kotlin/Function1<coil3.compose/AsyncImagePainter.State.Success, kotlin/Unit>?, kotlin/Function1<coil3.compose/AsyncImagePainter.State.Error, kotlin/Unit>?, androidx.compose.ui/Alignment?, androidx.compose.ui.layout/ContentScale?, kotlin/Float, androidx.compose.ui.graphics/ColorFilter?, androidx.compose.ui.graphics/FilterQuality, kotlin/Boolean, coil3.compose/EqualityDelegate?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int, kotlin/Int) // coil3.compose/SubcomposeAsyncImage|SubcomposeAsyncImage(kotlin.Any?;kotlin.String?;coil3.ImageLoader;androidx.compose.ui.Modifier?;kotlin.Function1<coil3.compose.AsyncImagePainter.State,coil3.compose.AsyncImagePainter.State>?;kotlin.Function4<coil3.compose.SubcomposeAsyncImageScope,coil3.compose.AsyncImagePainter.State.Loading,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>?;kotlin.Function4<coil3.compose.SubcomposeAsyncImageScope,coil3.compose.AsyncImagePainter.State.Success,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>?;kotlin.Function4<coil3.compose.SubcomposeAsyncImageScope,coil3.compose.AsyncImagePainter.State.Error,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>?;kotlin.Function1<coil3.compose.AsyncImagePainter.State.Loading,kotlin.Unit>?;kotlin.Function1<coil3.compose.AsyncImagePainter.State.Success,kotlin.Unit>?;kotlin.Function1<coil3.compose.AsyncImagePainter.State.Error,kotlin.Unit>?;androidx.compose.ui.Alignment?;androidx.compose.ui.layout.ContentScale?;kotlin.Float;androidx.compose.ui.graphics.ColorFilter?;androidx.compose.ui.graphics.FilterQuality;kotlin.Boolean;coil3.compose.EqualityDelegate?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int;kotlin.Int){}[0]
final fun coil3.compose/rememberAsyncImagePainter(kotlin/Any?, coil3/ImageLoader, androidx.compose.ui.graphics.painter/Painter?, androidx.compose.ui.graphics.painter/Painter?, androidx.compose.ui.graphics.painter/Painter?, kotlin/Function1<coil3.compose/AsyncImagePainter.State.Loading, kotlin/Unit>?, kotlin/Function1<coil3.compose/AsyncImagePainter.State.Success, kotlin/Unit>?, kotlin/Function1<coil3.compose/AsyncImagePainter.State.Error, kotlin/Unit>?, androidx.compose.ui.layout/ContentScale?, androidx.compose.ui.graphics/FilterQuality, coil3.compose/EqualityDelegate?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int, kotlin/Int): coil3.compose/AsyncImagePainter // coil3.compose/rememberAsyncImagePainter|rememberAsyncImagePainter(kotlin.Any?;coil3.ImageLoader;androidx.compose.ui.graphics.painter.Painter?;androidx.compose.ui.graphics.painter.Painter?;androidx.compose.ui.graphics.painter.Painter?;kotlin.Function1<coil3.compose.AsyncImagePainter.State.Loading,kotlin.Unit>?;kotlin.Function1<coil3.compose.AsyncImagePainter.State.Success,kotlin.Unit>?;kotlin.Function1<coil3.compose.AsyncImagePainter.State.Error,kotlin.Unit>?;androidx.compose.ui.layout.ContentScale?;androidx.compose.ui.graphics.FilterQuality;coil3.compose.EqualityDelegate?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int;kotlin.Int){}[0]
final fun coil3.compose/rememberAsyncImagePainter(kotlin/Any?, coil3/ImageLoader, kotlin/Function1<coil3.compose/AsyncImagePainter.State, coil3.compose/AsyncImagePainter.State>?, kotlin/Function1<coil3.compose/AsyncImagePainter.State, kotlin/Unit>?, androidx.compose.ui.layout/ContentScale?, androidx.compose.ui.graphics/FilterQuality, coil3.compose/EqualityDelegate?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int): coil3.compose/AsyncImagePainter // coil3.compose/rememberAsyncImagePainter|rememberAsyncImagePainter(kotlin.Any?;coil3.ImageLoader;kotlin.Function1<coil3.compose.AsyncImagePainter.State,coil3.compose.AsyncImagePainter.State>?;kotlin.Function1<coil3.compose.AsyncImagePainter.State,kotlin.Unit>?;androidx.compose.ui.layout.ContentScale?;androidx.compose.ui.graphics.FilterQuality;coil3.compose.EqualityDelegate?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0]
final inline fun coil3.compose/AsyncImagePreviewHandler(crossinline kotlin.coroutines/SuspendFunction1<coil3.request/ImageRequest, coil3/Image?>): coil3.compose/AsyncImagePreviewHandler // coil3.compose/AsyncImagePreviewHandler|AsyncImagePreviewHandler(kotlin.coroutines.SuspendFunction1<coil3.request.ImageRequest,coil3.Image?>){}[0]
final val coil3.compose.internal/coil3_compose_internal_AsyncImageState$stableprop // coil3.compose.internal/coil3_compose_internal_AsyncImageState$stableprop|#static{}coil3_compose_internal_AsyncImageState$stableprop[0]
final val coil3.compose.internal/coil3_compose_internal_ConstraintsSizeResolver$stableprop // coil3.compose.internal/coil3_compose_internal_ConstraintsSizeResolver$stableprop|#static{}coil3_compose_internal_ConstraintsSizeResolver$stableprop[0]
final val coil3.compose.internal/coil3_compose_internal_ContentPainterElement$stableprop // coil3.compose.internal/coil3_compose_internal_ContentPainterElement$stableprop|#static{}coil3_compose_internal_ContentPainterElement$stableprop[0]
Expand Down
8 changes: 7 additions & 1 deletion coil-compose-core/api/jvm/coil-compose-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public final class coil3/compose/AsyncImagePainterKt {
public abstract interface class coil3/compose/AsyncImagePreviewHandler {
public static final field Companion Lcoil3/compose/AsyncImagePreviewHandler$Companion;
public static final field Default Lcoil3/compose/AsyncImagePreviewHandler;
public abstract fun handle (Lcoil3/ImageLoader;Lcoil3/request/ImageRequest;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun handle (Lcoil3/ImageLoader;Lcoil3/request/ImageRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class coil3/compose/AsyncImagePreviewHandler$Companion {
Expand Down Expand Up @@ -126,7 +126,13 @@ public final class coil3/compose/ImagePainter : androidx/compose/ui/graphics/pai
public fun getIntrinsicSize-NH-jbRc ()J
}

public final class coil3/compose/ImagePainter_nonAndroidKt {
public static final fun asPainter-55t9-rM (Lcoil3/Image;Lcoil3/PlatformContext;I)Landroidx/compose/ui/graphics/painter/Painter;
public static synthetic fun asPainter-55t9-rM$default (Lcoil3/Image;Lcoil3/PlatformContext;IILjava/lang/Object;)Landroidx/compose/ui/graphics/painter/Painter;
}

public final class coil3/compose/LocalAsyncImagePreviewHandlerKt {
public static final fun AsyncImagePreviewHandler (Lkotlin/jvm/functions/Function2;)Lcoil3/compose/AsyncImagePreviewHandler;
public static final fun getLocalAsyncImagePreviewHandler ()Landroidx/compose/runtime/ProvidableCompositionLocal;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -479,21 +479,25 @@ class AsyncImagePainterTest {
fun previewPlaceholder() {
assumeSupportsCaptureToImage()

val previewHandler = AsyncImagePreviewHandler(ImageRequest::placeholder)

composeTestRule.setContent {
CompositionLocalProvider(LocalInspectionMode provides true) {
Image(
painter = rememberAsyncImagePainter(
model = ImageRequest.Builder(LocalContext.current)
.data("https://example.com/image")
.placeholder(R.drawable.red_rectangle)
.build(),
imageLoader = imageLoader,
),
contentDescription = null,
modifier = Modifier
.size(128.dp)
.testTag(Image),
)
CompositionLocalProvider(LocalAsyncImagePreviewHandler provides previewHandler) {
Image(
painter = rememberAsyncImagePainter(
model = ImageRequest.Builder(LocalContext.current)
.data("https://example.com/image")
.placeholder(R.drawable.red_rectangle)
.build(),
imageLoader = imageLoader,
),
contentDescription = null,
modifier = Modifier
.size(128.dp)
.testTag(Image),
)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,7 @@
package coil3.compose

import android.graphics.drawable.Drawable
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.layout.ContentScale
import coil3.BitmapImage
import coil3.DrawableImage
import coil3.Image
import coil3.PlatformContext
import coil3.asDrawable
import coil3.compose.internal.CrossfadePainter
import coil3.request.GlobalLifecycle
import coil3.request.ImageRequest
Expand All @@ -19,7 +10,6 @@ import coil3.request.lifecycle
import coil3.request.transitionFactory
import coil3.transition.CrossfadeTransition
import coil3.transition.TransitionTarget
import com.google.accompanist.drawablepainter.DrawablePainter

internal actual fun validateRequestProperties(request: ImageRequest) {
require(request.target == null) { "request.target must be null." }
Expand All @@ -30,20 +20,6 @@ internal actual fun ImageRequest.Builder.applyGlobalLifecycle() {
lifecycle(GlobalLifecycle)
}

internal actual fun Image.toPainter(
context: PlatformContext,
filterQuality: FilterQuality,
): Painter = when (this) {
is BitmapImage -> BitmapPainter(
image = bitmap.asImageBitmap(),
filterQuality = filterQuality,
)
is DrawableImage -> DrawablePainter(
drawable = asDrawable(context.resources).mutate(),
)
else -> ImagePainter(this)
}

internal actual fun maybeNewCrossfadePainter(
previous: AsyncImagePainter.State,
current: AsyncImagePainter.State,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,32 @@
package coil3.compose

import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.nativeCanvas
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.graphics.painter.Painter
import coil3.BitmapImage
import coil3.DrawableImage
import coil3.Image
import coil3.PlatformContext
import coil3.annotation.ExperimentalCoilApi
import coil3.asDrawable
import com.google.accompanist.drawablepainter.DrawablePainter

@ExperimentalCoilApi
actual fun Image.asPainter(
context: PlatformContext,
filterQuality: FilterQuality,
): Painter = when (this) {
is BitmapImage -> BitmapPainter(
image = bitmap.asImageBitmap(),
filterQuality = filterQuality,
)
is DrawableImage -> DrawablePainter(
drawable = asDrawable(context.resources).mutate(),
)
else -> ImagePainter(this)
}

internal actual val Canvas.nativeCanvas get() = nativeCanvas
Loading

0 comments on commit 33ea5bc

Please sign in to comment.