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

Updating snapshot #43

Merged
merged 4 commits into from Jul 9, 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
Expand Up @@ -21,9 +21,9 @@ object Versions {
}

object Libs {
const val androidGradlePlugin = "com.android.tools.build:gradle:4.2.0-alpha03"
const val androidGradlePlugin = "com.android.tools.build:gradle:4.2.0-alpha04"

const val gradleMavenPublishPlugin = "com.vanniktech:gradle-maven-publish-plugin:0.11.1"
const val gradleMavenPublishPlugin = "com.vanniktech:gradle-maven-publish-plugin:0.12.0"

const val junit = "junit:junit:4.13"

Expand Down
Expand Up @@ -48,6 +48,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.test.runBlockingTest
import kotlinx.coroutines.withTimeoutOrNull
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
Expand Down Expand Up @@ -175,6 +176,39 @@ class CoilTest {
loadCompleteSignal.close()
}

@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun basicLoad_changeSize() {
val loadCompleteSignal = Channel<Unit>(Channel.UNLIMITED)
val sizeFlow = MutableStateFlow(128.dp)

composeTestRule.setContent {
val size = sizeFlow.collectAsState()
CoilImage(
data = resourceUri(R.drawable.blue_rectangle),
modifier = Modifier.preferredSize(size.value).testTag(CoilTestTags.Image),
onRequestCompleted = { loadCompleteSignal.offer(Unit) }
)
}

// Await the first load
runBlocking {
loadCompleteSignal.receive()
}

// Now change the size
sizeFlow.value = 256.dp

// Await the potential second load (which shouldn't come)
runBlocking {
val result = withTimeoutOrNull(3000) { loadCompleteSignal.receive() }
assertThat(result).isNull()
}

// Close the signal channel
loadCompleteSignal.close()
}

@Test
@SdkSuppress(minSdkVersion = 26) // captureToBitmap is SDK 26+
fun customGetPainter() {
Expand Down
32 changes: 24 additions & 8 deletions coil/src/main/java/dev/chrisbanes/accompanist/coil/Coil.kt
Expand Up @@ -58,6 +58,8 @@ import coil.request.GetRequestBuilder
* @param getFailurePainter Optional builder for the [Painter] to be used to draw the failure
* loading result. Passing in `null` will result in falling back to the default [Painter].
* @param loading Content to be displayed when the request is in progress.
* @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.
*/
@Composable
Expand All @@ -70,6 +72,7 @@ fun CoilImage(
getSuccessPainter: @Composable ((SuccessResult) -> Painter)? = null,
getFailurePainter: @Composable ((ErrorResult) -> Painter?)? = null,
loading: @Composable (() -> Unit)? = null,
shouldRefetchOnSizeChange: (currentResult: RequestResult, size: IntSize) -> Boolean = defaultRefetchOnSizeChangeLambda,
onRequestCompleted: (RequestResult) -> Unit = emptySuccessLambda
) {
CoilImage(
Expand All @@ -90,6 +93,7 @@ fun CoilImage(
getSuccessPainter = getSuccessPainter,
getFailurePainter = getFailurePainter,
loading = loading,
shouldRefetchOnSizeChange = shouldRefetchOnSizeChange,
modifier = modifier
)
}
Expand All @@ -111,6 +115,8 @@ fun CoilImage(
* @param getFailurePainter Optional builder for the [Painter] to be used to draw the failure
* loading result. Passing in `null` will result in falling back to the default [Painter].
* @param loading Content to be displayed when the request is in progress.
* @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.
*/
@Composable
Expand All @@ -123,9 +129,16 @@ fun CoilImage(
getSuccessPainter: @Composable ((SuccessResult) -> Painter)? = null,
getFailurePainter: @Composable ((ErrorResult) -> Painter?)? = null,
loading: @Composable (() -> Unit)? = null,
shouldRefetchOnSizeChange: (currentResult: RequestResult, size: IntSize) -> Boolean = defaultRefetchOnSizeChangeLambda,
onRequestCompleted: (RequestResult) -> Unit = emptySuccessLambda
) {
var result by stateFor<RequestResult?>(request.data) { null }
// GetRequest does not support object equality (as of Coil v0.10.1) so we can not key any
// remember calls using the request itself. For now we just use the [data] field, but
// ideally this should use [request] to track changes in size, transformations, etc too.
// See: https://github.com/coil-kt/coil/issues/405
val requestKey = request.data ?: request

var result by stateFor<RequestResult?>(requestKey) { null }

// This may look a little weird, but allows the launchInComposition callback to always
// invoke the last provided [onRequestCompleted].
Expand All @@ -140,11 +153,7 @@ fun CoilImage(
val callback = state { onRequestCompleted }
callback.value = onRequestCompleted

// GetRequest does not support object equality (as of Coil v0.10.1) so we can not key the
// remember() using the request itself. For now we just use the [data] field, but
// ideally this should use [request] to track changes in size, transformations, etc too.
// See: https://github.com/coil-kt/coil/issues/405
val requestActor = remember(request.data) { CoilRequestActor(request) }
val requestActor = remember(requestKey) { CoilRequestActor(request) }

launchInComposition(requestActor) {
// Launch the Actor
Expand Down Expand Up @@ -177,9 +186,14 @@ fun CoilImage(
else -> null
}

// Update the modifier, so that we listen for size changes to invoke the actor
val mod = modifier.onSizeChanged { size ->
// When the size changes, send it to the request actor
requestActor.send(size)
val r = result
if (r == null || shouldRefetchOnSizeChange(r, size)) {
// If we don't have a result yet, or the size has changed and we should re-fetch,
// send it to the request actor
requestActor.send(size)
}
}

if (painter == null) {
Expand Down Expand Up @@ -296,6 +310,8 @@ internal fun defaultSuccessPainterGetter(result: SuccessResult): Painter {

internal val emptySuccessLambda: (RequestResult) -> Unit = {}

internal val defaultRefetchOnSizeChangeLambda: (RequestResult, IntSize) -> Boolean = { _, _ -> false }

internal fun Drawable.toImageAsset(fallbackSize: IntSize = IntSize.Zero): ImageAsset {
return toBitmap(
width = if (intrinsicWidth > 0) intrinsicWidth else fallbackSize.width,
Expand Down
Expand Up @@ -67,6 +67,8 @@ private const val DefaultTransitionDuration = 1000
* @param crossfadeDuration The duration of the crossfade animation in milliseconds.
* @param getFailurePainter Optional builder for the [Painter] to be used to draw the failure
* loading result. Passing in `null` will result in falling back to the default [Painter].
* @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.
*/
@Composable
Expand All @@ -78,6 +80,7 @@ fun CoilImageWithCrossfade(
crossfadeDuration: Int = DefaultTransitionDuration,
getFailurePainter: @Composable ((ErrorResult) -> Painter?)? = null,
loading: @Composable (() -> Unit)? = null,
shouldRefetchOnSizeChange: (currentResult: RequestResult, size: IntSize) -> Boolean = defaultRefetchOnSizeChangeLambda,
onRequestCompleted: (RequestResult) -> Unit = emptySuccessLambda
) {
CoilImage(
Expand All @@ -88,6 +91,7 @@ fun CoilImageWithCrossfade(
getFailurePainter = getFailurePainter,
loading = loading,
modifier = modifier,
shouldRefetchOnSizeChange = shouldRefetchOnSizeChange,
onRequestCompleted = onRequestCompleted
)
}
Expand All @@ -109,6 +113,8 @@ fun CoilImageWithCrossfade(
* @param crossfadeDuration The duration of the crossfade animation in milliseconds.
* @param getFailurePainter Optional builder for the [Painter] to be used to draw the failure
* loading result. Passing in `null` will result in falling back to the default [Painter].
* @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.
*/
@Composable
Expand All @@ -120,6 +126,7 @@ fun CoilImageWithCrossfade(
crossfadeDuration: Int = DefaultTransitionDuration,
getFailurePainter: @Composable ((ErrorResult) -> Painter?)? = null,
loading: @Composable (() -> Unit)? = null,
shouldRefetchOnSizeChange: (currentResult: RequestResult, size: IntSize) -> Boolean = defaultRefetchOnSizeChangeLambda,
onRequestCompleted: (RequestResult) -> Unit = emptySuccessLambda
) {
CoilImage(
Expand All @@ -129,6 +136,7 @@ fun CoilImageWithCrossfade(
getSuccessPainter = { crossfadePainter(it, durationMs = crossfadeDuration) },
getFailurePainter = getFailurePainter,
loading = loading,
shouldRefetchOnSizeChange = shouldRefetchOnSizeChange,
modifier = modifier,
onRequestCompleted = onRequestCompleted
)
Expand Down