Skip to content

Commit

Permalink
Merge branch 'main' into snapshot
Browse files Browse the repository at this point in the history
  • Loading branch information
Chris Banes committed Jul 7, 2020
2 parents 48566df + 7e8397d commit b7aee45
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ object Versions {
}

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

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,18 @@ import androidx.ui.unit.Dp
import androidx.ui.unit.IntSize
import org.junit.Assert

fun SemanticsNodeInteraction.assertSize(density: Density, width: Dp, height: Dp) {
assertSize(with(density) { IntSize(width.toIntPx(), height.toIntPx()) })
fun SemanticsNodeInteraction.assertSize(
density: Density,
width: Dp,
height: Dp
): SemanticsNodeInteraction {
return assertSize(with(density) { IntSize(width.toIntPx(), height.toIntPx()) })
}

fun SemanticsNodeInteraction.assertSize(expected: IntSize) {
fun SemanticsNodeInteraction.assertSize(
expected: IntSize
): SemanticsNodeInteraction {
val node = fetchSemanticsNode("Assert size")
Assert.assertEquals(expected, node.size)
return this
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ package dev.chrisbanes.accompanist.coil

import android.content.ContentResolver
import android.net.Uri
import androidx.annotation.RawRes
import androidx.compose.Composable
import androidx.compose.collectAsState
import androidx.core.net.toUri
import androidx.test.filters.LargeTest
import androidx.test.filters.SdkSuppress
Expand All @@ -43,6 +43,9 @@ import coil.request.GetRequest
import com.google.common.truth.Truth.assertThat
import dev.chrisbanes.accompanist.coil.test.R
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.test.runBlockingTest
import org.junit.Rule
Expand All @@ -66,7 +69,7 @@ class CoilTest {
composeTestRule.setContent {
CoilImage(
request = GetRequest.Builder(ContextAmbient.current)
.data(rawUri(R.raw.sample))
.data(resourceUri(R.raw.sample))
.listener { _, _ -> latch.countDown() }
.build(),
modifier = Modifier.preferredSize(128.dp, 128.dp),
Expand All @@ -85,12 +88,12 @@ class CoilTest {
}

@Test
fun basicLoad() {
fun basicLoad_raw() {
val latch = CountDownLatch(1)

composeTestRule.setContent {
CoilImage(
data = rawUri(R.raw.sample),
data = resourceUri(R.raw.sample),
modifier = Modifier.preferredSize(128.dp, 128.dp).testTag(CoilTestTags.Image),
onRequestCompleted = { latch.countDown() }
)
Expand All @@ -104,14 +107,82 @@ class CoilTest {
.assertSize(composeTestRule.density, 128.dp, 128.dp)
}

@Test
@SdkSuppress(minSdkVersion = 26) // captureToBitmap is SDK 26+
fun basicLoad_drawable() {
val latch = CountDownLatch(1)

composeTestRule.setContent {
CoilImage(
data = resourceUri(R.drawable.red_rectangle),
modifier = Modifier.preferredSize(128.dp, 128.dp).testTag(CoilTestTags.Image),
onRequestCompleted = { latch.countDown() }
)
}

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

findByTag(CoilTestTags.Image)
.assertSize(composeTestRule.density, 128.dp, 128.dp)
.assertIsDisplayed()
.captureToBitmap()
.assertPixels { Color.Red }
}

@OptIn(ExperimentalCoroutinesApi::class)
@Test
@SdkSuppress(minSdkVersion = 26) // captureToBitmap is SDK 26+
fun basicLoad_switchData() {
val loadCompleteSignal = Channel<Unit>(Channel.UNLIMITED)
val drawableResId = MutableStateFlow(R.drawable.red_rectangle)

composeTestRule.setContent {
val resId = drawableResId.collectAsState()
CoilImage(
data = resourceUri(resId.value),
modifier = Modifier.preferredSize(128.dp, 128.dp).testTag(CoilTestTags.Image),
onRequestCompleted = { loadCompleteSignal.offer(Unit) }
)
}

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

// Assert that the content is completely Red
findByTag(CoilTestTags.Image)
.assertSize(composeTestRule.density, 128.dp, 128.dp)
.assertIsDisplayed()
.captureToBitmap()
.assertPixels { Color.Red }

// Now switch the data URI to the blue drawable
drawableResId.value = R.drawable.blue_rectangle

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

// Assert that the content is completely Blue
findByTag(CoilTestTags.Image)
.assertSize(composeTestRule.density, 128.dp, 128.dp)
.assertIsDisplayed()
.captureToBitmap()
.assertPixels { Color.Blue }

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

@Test
@SdkSuppress(minSdkVersion = 26) // captureToBitmap is SDK 26+
fun customGetPainter() {
val latch = CountDownLatch(1)

composeTestRule.setContent {
CoilImage(
data = rawUri(R.raw.sample),
data = resourceUri(R.raw.sample),
getSuccessPainter = {
// Return a custom success painter which just draws cyan
ColorPainter(Color.Cyan)
Expand Down Expand Up @@ -163,7 +234,7 @@ class CoilTest {
composeTestRule.setContent {
CoilImage(
request = GetRequest.Builder(ContextAmbient.current)
.data(rawUri(R.raw.sample))
.data(resourceUri(R.raw.sample))
// Disable memory cache. If the item is in the cache, the fetch is
// synchronous and the dispatcher pause has no effect
.memoryCachePolicy(CachePolicy.DISABLED)
Expand Down Expand Up @@ -217,6 +288,6 @@ class CoilTest {
}

@Composable
fun rawUri(@RawRes id: Int): Uri {
fun resourceUri(id: Int): Uri {
return "${ContentResolver.SCHEME_ANDROID_RESOURCE}://${ContextAmbient.current.packageName}/$id".toUri()
}
20 changes: 20 additions & 0 deletions coil/src/androidTest/res/drawable-nodpi/blue_rectangle.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->

<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#0000FF" />
</shape>
20 changes: 20 additions & 0 deletions coil/src/androidTest/res/drawable-nodpi/red_rectangle.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->

<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#FF0000" />
</shape>
34 changes: 23 additions & 11 deletions coil/src/main/java/dev/chrisbanes/accompanist/coil/Coil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import androidx.compose.launchInComposition
import androidx.compose.remember
import androidx.compose.setValue
import androidx.compose.state
import androidx.compose.stateFor
import androidx.core.graphics.drawable.toBitmap
import androidx.ui.core.Alignment
import androidx.ui.core.ContentScale
Expand Down Expand Up @@ -123,7 +124,7 @@ fun CoilImage(
loading: @Composable (() -> Unit)? = null,
onRequestCompleted: (RequestResult) -> Unit = emptySuccessLambda
) {
var result by state<RequestResult?> { null }
var result by stateFor<RequestResult?>(request.data) { null }

// This may look a little weird, but allows the launchInComposition callback to always
// invoke the last provided [onRequestCompleted].
Expand Down Expand Up @@ -222,7 +223,13 @@ private fun CoilRequestActor(
// Now execute the request in Coil...
Coil.imageLoader(transformedRequest.context)
.execute(transformedRequest)
.toResult()
.toResult(size)
.also {
// Tell RenderThread to pre-upload this bitmap. Saves the GPU upload cost on the
// first draw. See https://github.com/square/picasso/issues/1620 for a explanation
// from @ChrisCraik
it.image?.prepareToDraw()
}
}
}

Expand All @@ -243,8 +250,8 @@ data class SuccessResult(
override val image: ImageAsset,
val source: DataSource
) : RequestResult() {
internal constructor(result: coil.request.SuccessResult) : this(
image = result.drawable.toImageAsset(),
internal constructor(result: coil.request.SuccessResult, fallbackSize: IntSize) : this(
image = result.drawable.toImageAsset(fallbackSize),
source = result.source
)
}
Expand All @@ -259,16 +266,18 @@ data class ErrorResult(
override val image: ImageAsset?,
val throwable: Throwable
) : RequestResult() {
internal constructor(result: coil.request.ErrorResult) : this(
image = result.drawable?.toImageAsset(),
internal constructor(result: coil.request.ErrorResult, fallbackSize: IntSize) : this(
image = result.drawable?.toImageAsset(fallbackSize),
throwable = result.throwable
)
}

private fun coil.request.RequestResult.toResult(): RequestResult {
private fun coil.request.RequestResult.toResult(
fallbackSize: IntSize = IntSize.Zero
): RequestResult {
return when (this) {
is coil.request.SuccessResult -> SuccessResult(this)
is coil.request.ErrorResult -> ErrorResult(this)
is coil.request.SuccessResult -> SuccessResult(this, fallbackSize)
is coil.request.ErrorResult -> ErrorResult(this, fallbackSize)
}
}

Expand All @@ -286,6 +295,9 @@ internal fun defaultSuccessPainterGetter(result: SuccessResult): Painter {

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

internal fun Drawable.toImageAsset(): ImageAsset {
return toBitmap().asImageAsset()
internal fun Drawable.toImageAsset(fallbackSize: IntSize = IntSize.Zero): ImageAsset {
return toBitmap(
width = if (intrinsicWidth > 0) intrinsicWidth else fallbackSize.width,
height = if (intrinsicHeight > 0) intrinsicHeight else fallbackSize.height
).asImageAsset()
}

0 comments on commit b7aee45

Please sign in to comment.