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

Picasso integration library #98

Merged
merged 10 commits into from Sep 26, 2020
Merged
Show file tree
Hide file tree
Changes from 9 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
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -39,6 +39,8 @@ docs/index.md
docs/contributing.md
docs/coil.md
docs/coil/
docs/picasso.md
docs/picasso/
# Mkdocs temporary serving folder
site
*.bak
13 changes: 1 addition & 12 deletions README.md
Expand Up @@ -3,22 +3,11 @@
Accompanist is a group of libraries that contains some utilities which I've found myself copying around projects which use [Jetpack Compose][compose]. Currently, it contains:

🖼️ [Coil image loading composables](./coil/README.md)
🖼️ [Picasso image loading composables](./picasso/README.md)

[Jetpack Compose][compose] is a fast-moving project and I'll be updating these libraries to match the
latest tagged release as quickly as possible. Each [release listing](https://github.com/chrisbanes/accompanist/releases) will outline what version of Compose libraries it depends on.

## Download

```groovy
repositories {
mavenCentral()
}

dependencies {
implementation "dev.chrisbanes.accompanist:accompanist-coil:<version>"
}
```

### Accompanist Snapshots

Snapshots of the current development version of Accompanist are available, which track the latest commit. See [here](docs/using-snapshot-version.md) for more information.
Expand Down
4 changes: 4 additions & 0 deletions build.gradle
Expand Up @@ -141,6 +141,10 @@ subprojects {
packageListUrl.set(new URL("file://$rootDir/package-list-coil-base"))
}
}
externalDocumentationLink {
url.set(new URL("https://square.github.io/picasso/2.x/picasso/"))
packageListUrl.set(new URL("https://square.github.io/picasso/2.x/picasso/package-list"))
}
}
}
}
Expand Down
Expand Up @@ -92,7 +92,12 @@ object Libs {
const val gif = "io.coil-kt:coil-gif:$version"
}

const val picasso = "com.squareup.picasso:picasso:2.8"

const val truth = "com.google.truth:truth:1.0.1"

const val mockWebServer = "com.squareup.okhttp3:mockwebserver:3.12.2"
object OkHttp {
const val okhttp = "com.squareup.okhttp3:okhttp:3.12.2"
const val mockWebServer = "com.squareup.okhttp3:mockwebserver:3.12.2"
}
}
5 changes: 2 additions & 3 deletions coil/README.md
Expand Up @@ -65,9 +65,9 @@ CoilImage(
)
```

### Custom layout
## Custom content

If you need more control over the animation, you can use the `content` composable version of `CoilImage`, to display the result in a `MaterialLoadingImage`:
If you need more control over the animation, or you want to provide custom layout for the loaded image, you can use the `content` composable version of `CoilImage`:

``` kotlin
CoilImage(
Expand All @@ -86,7 +86,6 @@ CoilImage(
ImageLoadState.Empty -> /* TODO */
}
}
```

## GIFs

Expand Down
2 changes: 1 addition & 1 deletion coil/build.gradle
Expand Up @@ -87,7 +87,7 @@ dependencies {
androidTestImplementation Libs.junit
androidTestImplementation Libs.truth

androidTestImplementation Libs.mockWebServer
androidTestImplementation Libs.OkHttp.mockWebServer

androidTestImplementation Libs.Coroutines.test

Expand Down
2 changes: 1 addition & 1 deletion coil/src/androidTest/AndroidManifest.xml
Expand Up @@ -18,7 +18,7 @@
xmlns:tools="http://schemas.android.com/tools"
package="dev.chrisbanes.accompanist.coil.test">

<uses-sdk android:targetSdkVersion="29" />
<uses-sdk android:targetSdkVersion="30" />

<!-- Needed for MockWebServer -->
<uses-permission android:name="android.permission.INTERNET" />
Expand Down
Expand Up @@ -58,6 +58,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.test.runBlockingTest
import kotlinx.coroutines.withTimeout
import kotlinx.coroutines.withTimeoutOrNull
import okhttp3.mockwebserver.Dispatcher
import okhttp3.mockwebserver.MockResponse
Expand Down Expand Up @@ -244,7 +245,9 @@ class CoilTest {

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

// Assert that the content is completely Red
Expand All @@ -259,7 +262,11 @@ class CoilTest {
drawableResId.value = R.drawable.blue_rectangle

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

// Assert that the content is completely Blue
composeTestRule.onNodeWithTag(CoilTestTags.Image)
Expand All @@ -282,7 +289,7 @@ class CoilTest {
composeTestRule.setContent {
val size = sizeFlow.collectAsState()
CoilImage(
data = resourceUri(R.drawable.blue_rectangle),
data = resourceUri(R.drawable.red_rectangle),
modifier = Modifier.preferredSize(size.value).testTag(CoilTestTags.Image),
onRequestCompleted = { loadCompleteSignal.offer(Unit) }
)
Expand Down
22 changes: 10 additions & 12 deletions coil/src/main/java/dev/chrisbanes/accompanist/coil/Coil.kt
Expand Up @@ -36,6 +36,8 @@ import coil.imageLoader
import coil.request.ImageRequest
import coil.request.ImageResult
import dev.chrisbanes.accompanist.imageloading.DataSource
import dev.chrisbanes.accompanist.imageloading.DefaultRefetchOnSizeChangeLambda
import dev.chrisbanes.accompanist.imageloading.EmptyRequestCompleteLambda
import dev.chrisbanes.accompanist.imageloading.ImageLoad
import dev.chrisbanes.accompanist.imageloading.ImageLoadState
import dev.chrisbanes.accompanist.imageloading.MaterialLoadingImage
Expand Down Expand Up @@ -74,8 +76,8 @@ fun CoilImage(
modifier: Modifier = Modifier,
requestBuilder: (ImageRequest.Builder.(size: IntSize) -> ImageRequest.Builder)? = null,
imageLoader: ImageLoader = ContextAmbient.current.imageLoader,
shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = defaultRefetchOnSizeChangeLambda,
onRequestCompleted: (ImageLoadState) -> Unit = emptySuccessLambda,
shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = DefaultRefetchOnSizeChangeLambda,
onRequestCompleted: (ImageLoadState) -> Unit = EmptyRequestCompleteLambda,
content: @Composable (imageLoadState: ImageLoadState) -> Unit
) {
CoilImage(
Expand Down Expand Up @@ -123,8 +125,8 @@ fun CoilImage(
modifier: Modifier = Modifier,
requestBuilder: (ImageRequest.Builder.(size: IntSize) -> ImageRequest.Builder)? = null,
imageLoader: ImageLoader = ContextAmbient.current.imageLoader,
shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = defaultRefetchOnSizeChangeLambda,
onRequestCompleted: (ImageLoadState) -> Unit = emptySuccessLambda,
shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = DefaultRefetchOnSizeChangeLambda,
onRequestCompleted: (ImageLoadState) -> Unit = EmptyRequestCompleteLambda,
content: @Composable (imageLoadState: ImageLoadState) -> Unit
) {
ImageLoad(
Expand Down Expand Up @@ -210,8 +212,8 @@ fun CoilImage(
fadeIn: Boolean = false,
requestBuilder: (ImageRequest.Builder.(size: IntSize) -> ImageRequest.Builder)? = null,
imageLoader: ImageLoader = ContextAmbient.current.imageLoader,
shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = defaultRefetchOnSizeChangeLambda,
onRequestCompleted: (ImageLoadState) -> Unit = emptySuccessLambda,
shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = DefaultRefetchOnSizeChangeLambda,
onRequestCompleted: (ImageLoadState) -> Unit = EmptyRequestCompleteLambda,
error: @Composable ((ImageLoadState.Error) -> Unit)? = null,
loading: @Composable (() -> Unit)? = null,
) {
Expand Down Expand Up @@ -283,8 +285,8 @@ fun CoilImage(
fadeIn: Boolean = false,
requestBuilder: (ImageRequest.Builder.(size: IntSize) -> ImageRequest.Builder)? = null,
imageLoader: ImageLoader = ContextAmbient.current.imageLoader,
shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = defaultRefetchOnSizeChangeLambda,
onRequestCompleted: (ImageLoadState) -> Unit = emptySuccessLambda,
shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = DefaultRefetchOnSizeChangeLambda,
onRequestCompleted: (ImageLoadState) -> Unit = EmptyRequestCompleteLambda,
error: @Composable ((ImageLoadState.Error) -> Unit)? = null,
loading: @Composable (() -> Unit)? = null,
) {
Expand Down Expand Up @@ -372,7 +374,3 @@ internal fun Any.toImageRequest(): ImageRequest {
remember(this) { ImageRequest.Builder(context).data(this).build() }
}
}

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

internal val defaultRefetchOnSizeChangeLambda: (ImageLoadState, IntSize) -> Boolean = { _, _ -> false }
10 changes: 6 additions & 4 deletions coil/src/main/java/dev/chrisbanes/accompanist/coil/Crossfade.kt
Expand Up @@ -28,6 +28,8 @@ import androidx.compose.ui.unit.IntSize
import coil.ImageLoader
import coil.imageLoader
import coil.request.ImageRequest
import dev.chrisbanes.accompanist.imageloading.DefaultRefetchOnSizeChangeLambda
import dev.chrisbanes.accompanist.imageloading.EmptyRequestCompleteLambda
import dev.chrisbanes.accompanist.imageloading.ImageLoadState
import dev.chrisbanes.accompanist.imageloading.MaterialLoadingImage

Expand Down Expand Up @@ -64,8 +66,8 @@ fun CoilImageWithCrossfade(
contentScale: ContentScale = ContentScale.Fit,
crossfadeDuration: Int = DefaultTransitionDuration,
imageLoader: ImageLoader = ContextAmbient.current.imageLoader,
shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = defaultRefetchOnSizeChangeLambda,
onRequestCompleted: (ImageLoadState) -> Unit = emptySuccessLambda,
shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = DefaultRefetchOnSizeChangeLambda,
onRequestCompleted: (ImageLoadState) -> Unit = EmptyRequestCompleteLambda,
error: @Composable ((ImageLoadState.Error) -> Unit)? = null,
loading: @Composable (() -> Unit)? = null
) {
Expand Down Expand Up @@ -115,8 +117,8 @@ fun CoilImageWithCrossfade(
contentScale: ContentScale = ContentScale.Fit,
crossfadeDuration: Int = DefaultTransitionDuration,
imageLoader: ImageLoader = ContextAmbient.current.imageLoader,
shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = defaultRefetchOnSizeChangeLambda,
onRequestCompleted: (ImageLoadState) -> Unit = emptySuccessLambda,
shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = DefaultRefetchOnSizeChangeLambda,
onRequestCompleted: (ImageLoadState) -> Unit = EmptyRequestCompleteLambda,
error: @Composable ((ImageLoadState.Error) -> Unit)? = null,
loading: @Composable (() -> Unit)? = null
) {
Expand Down
6 changes: 5 additions & 1 deletion generate_docs.sh
Expand Up @@ -22,9 +22,13 @@ sed -i.bak 's/images\/social.png/header.png/' docs/index.md
cp coil/README.md docs/coil.md
mkdir -p docs/coil
cp coil/images/crossfade.gif docs/coil/crossfade.gif

sed -i.bak 's/images\/crossfade.gif/crossfade.gif/' docs/coil.md

cp picasso/README.md docs/picasso.md
mkdir -p docs/picasso
cp picasso/images/crossfade.gif docs/picasso/crossfade.gif
sed -i.bak 's/images\/crossfade.gif/crossfade.gif/' docs/picasso.md

# Convert docs/xxx.md links to just xxx/
sed -i.bak 's/docs\/\([a-zA-Z-]*\).md/\1/' docs/index.md

Expand Down
4 changes: 3 additions & 1 deletion imageloading-core/api/imageloading-core.api
Expand Up @@ -16,7 +16,9 @@ public final class dev/chrisbanes/accompanist/imageloading/DataSource : java/lan
}

public final class dev/chrisbanes/accompanist/imageloading/ImageLoad {
public static final fun ImageLoad (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;Landroidx/compose/ui/Modifier;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 ImageLoad (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;Landroidx/compose/ui/Modifier;Ljava/lang/Object;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 getDefaultRefetchOnSizeChangeLambda ()Lkotlin/jvm/functions/Function2;
public static final fun getEmptyRequestCompleteLambda ()Lkotlin/jvm/functions/Function1;
}

public abstract class dev/chrisbanes/accompanist/imageloading/ImageLoadState {
Expand Down
Expand Up @@ -46,23 +46,27 @@ import kotlinx.coroutines.flow.flow
* @param request The request to execute.
* @param executeRequest Suspending lambda to execute an image loading request.
* @param modifier [Modifier] used to adjust the layout algorithm or draw decoration content.
* @param requestKey The object to key this request on. If the request type supports equality then
* the default value will work. Otherwise pass in the `data` value.
* @param modifier [Modifier] used to adjust the layout algorithm or draw decoration content.
* @param transformRequestForSize Optionally transform [request] for the given [IntSize].
* @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.
* @param content Content to be displayed for the given state.
*/
@Composable
fun <T> ImageLoad(
fun <T : Any> ImageLoad(
request: T,
executeRequest: suspend (T) -> ImageLoadState,
modifier: Modifier = Modifier,
requestKey: Any = request,
transformRequestForSize: (T, IntSize) -> T? = { r, _ -> r },
shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = defaultRefetchOnSizeChangeLambda,
onRequestCompleted: (ImageLoadState) -> Unit = emptySuccessLambda,
shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = DefaultRefetchOnSizeChangeLambda,
onRequestCompleted: (ImageLoadState) -> Unit = EmptyRequestCompleteLambda,
content: @Composable (imageLoadState: ImageLoadState) -> Unit
) {
var state by stateFor<ImageLoadState>(request) { ImageLoadState.Empty }
var state by stateFor<ImageLoadState>(requestKey) { ImageLoadState.Empty }

// This may look a little weird, but allows the launchInComposition callback to always
// invoke the last provided [onRequestCompleted].
Expand All @@ -77,7 +81,7 @@ fun <T> ImageLoad(
val callback = remember { mutableStateOf(onRequestCompleted, referentialEqualityPolicy()) }
callback.value = onRequestCompleted

val requestActor = remember(request) {
val requestActor = remember(requestKey) {
ImageLoadRequestActor(executeRequest)
}

Expand Down Expand Up @@ -142,6 +146,12 @@ private fun <T> ImageLoadRequestActor(
}
}

internal val emptySuccessLambda: (ImageLoadState) -> Unit = {}
/**
* Empty lamdba for use in the `onRequestCompleted` parameter.
*/
val EmptyRequestCompleteLambda: (ImageLoadState) -> Unit = {}

internal val defaultRefetchOnSizeChangeLambda: (ImageLoadState, IntSize) -> Boolean = { _, _ -> false }
/**
* Default lamdba for use in the `shouldRefetchOnSizeChange` parameter.
*/
val DefaultRefetchOnSizeChangeLambda: (ImageLoadState, IntSize) -> Boolean = { _, _ -> false }
5 changes: 4 additions & 1 deletion mkdocs.yml
Expand Up @@ -13,12 +13,15 @@ repo_url: 'https://github.com/chrisbanes/accompanist'
nav:
- 'Overview': index.md
- 'Coil': coil.md
- 'Picasso': picasso.md
- 'API':
- 'Coil': api/coil/index.md
- 'Picasso': api/picasso/index.md
- 'Image Loading Core': api/imageloading-core/index.md
- 'Snapshots': using-snapshot-version.md
- 'Contributing': contributing.md
- 'Maintainers':
- 'Update guide': updating.md
- 'Update guide': updating.md

# Configuration
theme:
Expand Down