From 0c8932db8e6c4473b3f8399c80cfdfff040d0d11 Mon Sep 17 00:00:00 2001 From: Chris Banes Date: Mon, 10 May 2021 14:12:10 +0100 Subject: [PATCH] Remove deprecated functions (#399) All of these functions were marked as deprecated at least a few releases ago and are due for removal. - CoilImage - GlideImage - MaterialLoadingImage - rememberAndroidSystemUiController - LocalSystemUiController - PagerState.pageChanges --- coil/api/current.api | 7 - .../accompanist/coil/DeprecatedCoilTest.kt | 567 ------------------ .../google/accompanist/coil/DeprecatedCoil.kt | 427 ------------- glide/api/current.api | 5 - .../accompanist/glide/DeprecatedGlideTest.kt | 562 ----------------- .../accompanist/glide/DeprecatedGlide.kt | 252 -------- imageloading-core/api/current.api | 8 - .../accompanist/imageloading/Deprecated.kt | 80 --- .../imageloading/DrawablePainter.kt | 25 +- .../imageloading/MaterialLoadingImage.kt | 164 ----- pager/api/current.api | 1 - .../com/google/accompanist/pager/PagerTest.kt | 45 -- .../google/accompanist/pager/PagerState.kt | 17 - systemuicontroller/api/current.api | 2 - .../systemuicontroller/SystemUiController.kt | 25 - 15 files changed, 6 insertions(+), 2181 deletions(-) delete mode 100644 coil/src/androidTest/java/com/google/accompanist/coil/DeprecatedCoilTest.kt delete mode 100644 coil/src/main/java/com/google/accompanist/coil/DeprecatedCoil.kt delete mode 100644 glide/src/androidTest/java/com/google/accompanist/glide/DeprecatedGlideTest.kt delete mode 100644 glide/src/main/java/com/google/accompanist/glide/DeprecatedGlide.kt delete mode 100644 imageloading-core/src/main/java/com/google/accompanist/imageloading/Deprecated.kt diff --git a/coil/api/current.api b/coil/api/current.api index 8a6a485d5..8d66bf713 100644 --- a/coil/api/current.api +++ b/coil/api/current.api @@ -1,13 +1,6 @@ // Signature format: 4.0 package com.google.accompanist.coil { - public final class CoilImage { - method @Deprecated @androidx.compose.runtime.Composable public static void CoilImage(coil.request.ImageRequest request, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function2? requestBuilder, optional coil.ImageLoader imageLoader, optional @DrawableRes int previewPlaceholder, optional kotlin.jvm.functions.Function2 shouldRefetchOnSizeChange, optional kotlin.jvm.functions.Function1 onRequestCompleted, kotlin.jvm.functions.Function2 content); - method @Deprecated @androidx.compose.runtime.Composable public static void CoilImage(coil.request.ImageRequest request, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional boolean fadeIn, optional kotlin.jvm.functions.Function2? requestBuilder, optional coil.ImageLoader imageLoader, optional @DrawableRes int previewPlaceholder, optional kotlin.jvm.functions.Function2 shouldRefetchOnSizeChange, optional kotlin.jvm.functions.Function1 onRequestCompleted, optional kotlin.jvm.functions.Function2? error, optional kotlin.jvm.functions.Function1? loading); - method @Deprecated @androidx.compose.runtime.Composable public static void CoilImage(Object data, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function2? requestBuilder, optional coil.ImageLoader imageLoader, optional @DrawableRes int previewPlaceholder, optional kotlin.jvm.functions.Function2 shouldRefetchOnSizeChange, optional kotlin.jvm.functions.Function1 onRequestCompleted, kotlin.jvm.functions.Function2 content); - method @Deprecated @androidx.compose.runtime.Composable public static void CoilImage(Object data, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional boolean fadeIn, optional kotlin.jvm.functions.Function2? requestBuilder, optional coil.ImageLoader imageLoader, optional @DrawableRes int previewPlaceholder, optional kotlin.jvm.functions.Function2 shouldRefetchOnSizeChange, optional kotlin.jvm.functions.Function1 onRequestCompleted, optional kotlin.jvm.functions.Function2? error, optional kotlin.jvm.functions.Function1? loading); - } - public final class CoilKt { method public static androidx.compose.runtime.ProvidableCompositionLocal getLocalImageLoader(); method @androidx.compose.runtime.Composable public static com.google.accompanist.imageloading.LoadPainter rememberCoilPainter(Object? request, optional coil.ImageLoader imageLoader, optional com.google.accompanist.imageloading.ShouldRefetchOnSizeChange shouldRefetchOnSizeChange, optional kotlin.jvm.functions.Function2? requestBuilder, optional boolean fadeIn, optional int fadeInDurationMs, optional @DrawableRes int previewPlaceholder); diff --git a/coil/src/androidTest/java/com/google/accompanist/coil/DeprecatedCoilTest.kt b/coil/src/androidTest/java/com/google/accompanist/coil/DeprecatedCoilTest.kt deleted file mode 100644 index 2f34bb4a3..000000000 --- a/coil/src/androidTest/java/com/google/accompanist/coil/DeprecatedCoilTest.kt +++ /dev/null @@ -1,567 +0,0 @@ -/* - * 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. - */ - -package com.google.accompanist.coil - -import android.graphics.drawable.ShapeDrawable -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.size -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.painter.ColorPainter -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.test.assertHeightIsAtLeast -import androidx.compose.ui.test.assertHeightIsEqualTo -import androidx.compose.ui.test.assertIsDisplayed -import androidx.compose.ui.test.assertWidthIsAtLeast -import androidx.compose.ui.test.assertWidthIsEqualTo -import androidx.compose.ui.test.captureToImage -import androidx.compose.ui.test.junit4.createComposeRule -import androidx.compose.ui.test.onNodeWithTag -import androidx.compose.ui.unit.dp -import androidx.test.filters.LargeTest -import androidx.test.filters.SdkSuppress -import androidx.test.platform.app.InstrumentationRegistry -import coil.EventListener -import coil.ImageLoader -import coil.annotation.ExperimentalCoilApi -import coil.decode.Options -import coil.fetch.Fetcher -import coil.request.CachePolicy -import coil.request.ImageRequest -import com.google.accompanist.coil.test.R -import com.google.accompanist.imageloading.ImageLoadState -import com.google.accompanist.imageloading.test.ImageMockWebServer -import com.google.accompanist.imageloading.test.assertPixels -import com.google.accompanist.imageloading.test.receiveBlocking -import com.google.accompanist.imageloading.test.resourceUri -import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withTimeoutOrNull -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 -import java.util.concurrent.atomic.AtomicInteger - -@Suppress("DEPRECATION") // This is testing the deprecated functions -@LargeTest -@RunWith(JUnit4::class) -class DeprecatedCoilTest { - @get:Rule - val composeTestRule = createComposeRule() - - // Our MockWebServer. We use a response delay to simulate real-world conditions - private val server = ImageMockWebServer() - - @Before - fun setup() { - // Start our mock web server - server.start() - } - - @After - fun teardown() { - // Shutdown our mock web server - server.shutdown() - } - - @Test - fun onRequestCompleted_fromImageRequest() { - val results = ArrayList() - var requestCompleted by mutableStateOf(false) - - composeTestRule.setContent { - CoilImage( - data = server.url("/image"), - requestBuilder = { - listener { _, _ -> requestCompleted = true } - }, - contentDescription = null, - modifier = Modifier.size(128.dp, 128.dp), - onRequestCompleted = { results += it } - ) - } - - // Wait for the Coil request listener to run - composeTestRule.waitUntil(10_000) { requestCompleted } - - composeTestRule.runOnIdle { - // And assert that we got a single successful result - assertThat(results).hasSize(1) - assertThat(results[0]).isInstanceOf(ImageLoadState.Success::class.java) - } - } - - @Test - fun onRequestCompleted_fromBuilder() { - val results = ArrayList() - var requestCompleted by mutableStateOf(false) - - composeTestRule.setContent { - CoilImage( - data = server.url("/image"), - requestBuilder = { - listener { _, _ -> requestCompleted = true } - }, - contentDescription = null, - modifier = Modifier.size(128.dp, 128.dp), - onRequestCompleted = { results += it } - ) - } - - // Wait for the Coil request listener to run - composeTestRule.waitUntil(10_000) { requestCompleted } - - composeTestRule.runOnIdle { - // And assert that we got a single successful result - assertThat(results).hasSize(1) - assertThat(results[0]).isInstanceOf(ImageLoadState.Success::class.java) - } - } - - @Test - fun basicLoad_http() { - var requestCompleted by mutableStateOf(false) - - composeTestRule.setContent { - CoilImage( - data = server.url("/image"), - contentDescription = null, - modifier = Modifier - .size(128.dp, 128.dp) - .testTag(CoilTestTags.Image), - onRequestCompleted = { requestCompleted = true } - ) - } - - // Wait for the onRequestCompleted to run - composeTestRule.waitUntil(10_000) { requestCompleted } - - composeTestRule.onNodeWithTag(CoilTestTags.Image) - .assertIsDisplayed() - .assertWidthIsEqualTo(128.dp) - .assertHeightIsEqualTo(128.dp) - } - - @Test - @SdkSuppress(minSdkVersion = 26) // captureToImage is SDK 26+ - fun basicLoad_drawable() { - var requestCompleted by mutableStateOf(false) - - composeTestRule.setContent { - CoilImage( - data = resourceUri(R.drawable.red_rectangle), - contentDescription = null, - modifier = Modifier - .size(128.dp, 128.dp) - .testTag(CoilTestTags.Image), - onRequestCompleted = { requestCompleted = true } - ) - } - - // Wait for the onRequestCompleted to run - composeTestRule.waitUntil(10_000) { requestCompleted } - - composeTestRule.onNodeWithTag(CoilTestTags.Image) - .assertWidthIsEqualTo(128.dp) - .assertHeightIsEqualTo(128.dp) - .assertIsDisplayed() - .captureToImage() - .assertPixels(Color.Red) - } - - @OptIn(ExperimentalCoilApi::class) - @Test - fun basicLoad_customImageLoader() { - val context = InstrumentationRegistry.getInstrumentation().targetContext - var requestCompleted by mutableStateOf(false) - - // Build a custom ImageLoader with a fake EventListener - val eventListener = object : EventListener { - val startCalled = AtomicInteger() - - override fun fetchStart(request: ImageRequest, fetcher: Fetcher<*>, options: Options) { - startCalled.incrementAndGet() - } - } - val imageLoader = ImageLoader.Builder(context) - .eventListener(eventListener) - .build() - - composeTestRule.setContent { - CoilImage( - data = server.url("/image"), - contentDescription = null, - modifier = Modifier.size(128.dp, 128.dp), - imageLoader = imageLoader, - onRequestCompleted = { requestCompleted = true } - ) - } - - // Wait for the onRequestCompleted to run - composeTestRule.waitUntil(10_000) { requestCompleted } - - // Verify that our eventListener was invoked - assertThat(eventListener.startCalled.get()).isAtLeast(1) - } - - @OptIn(ExperimentalCoilApi::class) - @Test - fun basicLoad_customImageLoader_ambient() { - val context = InstrumentationRegistry.getInstrumentation().targetContext - var requestCompleted by mutableStateOf(false) - - // Build a custom ImageLoader with a fake EventListener - val eventListener = object : EventListener { - val startCalled = AtomicInteger() - - override fun fetchStart(request: ImageRequest, fetcher: Fetcher<*>, options: Options) { - startCalled.incrementAndGet() - } - } - val imageLoader = ImageLoader.Builder(context) - .eventListener(eventListener) - .build() - - composeTestRule.setContent { - CompositionLocalProvider(LocalImageLoader provides imageLoader) { - CoilImage( - data = server.url("/image"), - contentDescription = null, - modifier = Modifier.size(128.dp, 128.dp), - onRequestCompleted = { requestCompleted = true } - ) - } - } - - // Wait for the onRequestCompleted to run - composeTestRule.waitUntil(10_000) { requestCompleted } - - // Verify that our eventListener was invoked - assertThat(eventListener.startCalled.get()).isAtLeast(1) - } - - @OptIn(ExperimentalCoroutinesApi::class) - @Test - @SdkSuppress(minSdkVersion = 26) // captureToImage is SDK 26+ - fun basicLoad_switchData() { - var loadCompleteSignal by mutableStateOf(false) - var data by mutableStateOf(server.url("/red")) - - composeTestRule.setContent { - CoilImage( - data = data, - contentDescription = null, - modifier = Modifier - .size(128.dp, 128.dp) - .testTag(CoilTestTags.Image), - onRequestCompleted = { loadCompleteSignal = true } - ) - } - - // Await the first load - composeTestRule.waitUntil(10_000) { loadCompleteSignal } - loadCompleteSignal = false - - // Assert that the content is completely Red - composeTestRule.onNodeWithTag(CoilTestTags.Image) - .assertWidthIsEqualTo(128.dp) - .assertHeightIsEqualTo(128.dp) - .assertIsDisplayed() - .captureToImage() - .assertPixels(Color.Red) - - // Now switch the data URI to the blue drawable - data = server.url("/blue") - - // Await the second load - composeTestRule.waitUntil(10_000) { loadCompleteSignal } - - // Assert that the content is completely Blue - composeTestRule.onNodeWithTag(CoilTestTags.Image) - .assertWidthIsEqualTo(128.dp) - .assertHeightIsEqualTo(128.dp) - .assertIsDisplayed() - .captureToImage() - .assertPixels(Color.Blue) - } - - @OptIn(ExperimentalCoroutinesApi::class) - @Test - fun basicLoad_changeSize() { - val loadCompleteSignal = Channel(Channel.UNLIMITED) - var size by mutableStateOf(128.dp) - - composeTestRule.setContent { - CoilImage( - data = server.url("/red"), - contentDescription = null, - modifier = Modifier - .size(size) - .testTag(CoilTestTags.Image), - onRequestCompleted = { loadCompleteSignal.offer(it) } - ) - } - - // Await the first load - assertThat(loadCompleteSignal.receiveBlocking()) - .isInstanceOf(ImageLoadState.Success::class.java) - - // Now change the size - size = 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 - fun basicLoad_nosize() { - var requestCompleted by mutableStateOf(false) - - composeTestRule.setContent { - CoilImage( - data = server.url("/image"), - contentDescription = null, - modifier = Modifier.testTag(CoilTestTags.Image), - onRequestCompleted = { requestCompleted = true } - ) - } - - // Wait for the onRequestCompleted to run - composeTestRule.waitUntil(10_000) { requestCompleted } - - composeTestRule.onNodeWithTag(CoilTestTags.Image) - .assertWidthIsAtLeast(1.dp) - .assertHeightIsAtLeast(1.dp) - .assertIsDisplayed() - } - - @Test - fun errorStillHasSize() { - var requestCompleted by mutableStateOf(false) - - composeTestRule.setContent { - CoilImage( - data = server.url("/noimage"), - contentDescription = null, - modifier = Modifier - .size(128.dp, 128.dp) - .testTag(CoilTestTags.Image), - onRequestCompleted = { requestCompleted = true } - ) - } - - // Wait for the onRequestCompleted to run - composeTestRule.waitUntil(10_000) { requestCompleted } - - // Assert that the layout is in the tree and has the correct size - composeTestRule.onNodeWithTag(CoilTestTags.Image) - .assertIsDisplayed() - .assertWidthIsEqualTo(128.dp) - .assertHeightIsEqualTo(128.dp) - } - - @Test - fun content_error() { - var requestCompleted by mutableStateOf(false) - val states = ArrayList() - - composeTestRule.setContent { - CoilImage( - data = server.url("/noimage"), - modifier = Modifier.size(128.dp, 128.dp), - // Disable any caches. If the item is in the cache, the fetch is - // synchronous which means the Loading state is skipped - imageLoader = noCacheImageLoader(), - onRequestCompleted = { requestCompleted = true } - ) { state -> - states.add(state) - } - } - - // Wait for the onRequestCompleted to run - composeTestRule.waitUntil(10_000) { requestCompleted } - - composeTestRule.runOnIdle { - // Check that the final state is an Error - assertThat(states.last()).isInstanceOf(ImageLoadState.Error::class.java) - } - } - - @Test - fun content_success() { - var requestCompleted by mutableStateOf(false) - val states = ArrayList() - - composeTestRule.setContent { - CoilImage( - data = server.url("/image"), - modifier = Modifier.size(128.dp, 128.dp), - // Disable any caches. If the item is in the cache, the fetch is - // synchronous which means the Loading state is skipped - imageLoader = noCacheImageLoader(), - onRequestCompleted = { requestCompleted = true } - ) { state -> - states.add(state) - } - } - - // Wait for the onRequestCompleted to run - composeTestRule.waitUntil(10_000) { requestCompleted } - - composeTestRule.runOnIdle { - // Check that the final state is a Success - assertThat(states.last()).isInstanceOf(ImageLoadState.Success::class.java) - } - } - - @Test - @SdkSuppress(minSdkVersion = 26) // captureToImage is SDK 26+ - fun content_custom() { - var requestCompleted by mutableStateOf(false) - - composeTestRule.setContent { - CoilImage( - data = server.url("/image"), - modifier = Modifier - .size(128.dp, 128.dp) - .testTag(CoilTestTags.Image), - onRequestCompleted = { requestCompleted = true } - ) { - // Return an Image which just draws cyan - Image( - painter = ColorPainter(Color.Cyan), - contentDescription = null, - modifier = Modifier.matchParentSize() - ) - } - } - - // Wait for the onRequestCompleted to run - composeTestRule.waitUntil(10_000) { requestCompleted } - - // Assert that the whole layout is drawn cyan - composeTestRule.onNodeWithTag(CoilTestTags.Image) - .assertIsDisplayed() - .captureToImage() - .assertPixels(Color.Cyan, 0.05f) - } - - @Test - @SdkSuppress(minSdkVersion = 26) // captureToImage is SDK 26+ - fun error_slot() { - var requestCompleted by mutableStateOf(false) - - composeTestRule.setContent { - CoilImage( - data = server.url("/noimage"), - error = { - // Return failure content which just draws red - Image( - painter = ColorPainter(Color.Red), - contentDescription = null, - modifier = Modifier.matchParentSize() - ) - }, - contentDescription = null, - modifier = Modifier - .size(128.dp, 128.dp) - .testTag(CoilTestTags.Image), - onRequestCompleted = { requestCompleted = true } - ) - } - - // Wait for the onRequestCompleted to run - composeTestRule.waitUntil(10_000) { requestCompleted } - - // Assert that the whole layout is drawn red - composeTestRule.onNodeWithTag(CoilTestTags.Image) - .assertIsDisplayed() - .captureToImage() - .assertPixels(Color.Red) - } - - @Test(expected = IllegalArgumentException::class) - fun data_drawable_throws() { - composeTestRule.setContent { - CoilImage( - data = ShapeDrawable(), - contentDescription = null, - modifier = Modifier.size(128.dp, 128.dp), - ) - } - } - - @Test(expected = IllegalArgumentException::class) - fun data_imagebitmap_throws() { - composeTestRule.setContent { - CoilImage( - data = painterResource(android.R.drawable.ic_delete), - contentDescription = null, - modifier = Modifier.size(128.dp, 128.dp), - ) - } - } - - @Test(expected = IllegalArgumentException::class) - fun data_imagevector_throws() { - composeTestRule.setContent { - CoilImage( - data = painterResource(R.drawable.ic_android_black_24dp), - contentDescription = null, - modifier = Modifier.size(128.dp, 128.dp), - ) - } - } - - @Test(expected = IllegalArgumentException::class) - fun data_painter_throws() { - composeTestRule.setContent { - CoilImage( - data = ColorPainter(Color.Magenta), - contentDescription = null, - modifier = Modifier.size(128.dp, 128.dp), - ) - } - } -} - -/** - * [ImageLoader] which disables all caching - */ -private fun noCacheImageLoader(): ImageLoader { - val ctx = InstrumentationRegistry.getInstrumentation().targetContext - return ImageLoader.Builder(ctx) - .memoryCachePolicy(CachePolicy.DISABLED) - .diskCachePolicy(CachePolicy.DISABLED) - .build() -} diff --git a/coil/src/main/java/com/google/accompanist/coil/DeprecatedCoil.kt b/coil/src/main/java/com/google/accompanist/coil/DeprecatedCoil.kt deleted file mode 100644 index de6307402..000000000 --- a/coil/src/main/java/com/google/accompanist/coil/DeprecatedCoil.kt +++ /dev/null @@ -1,427 +0,0 @@ -/* - * Copyright 2021 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. - */ - -@file:JvmName("CoilImage") -@file:JvmMultifileClass - -@file:Suppress("DEPRECATION") - -package com.google.accompanist.coil - -import androidx.annotation.DrawableRes -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.BoxScope -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.snapshotFlow -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.unit.IntSize -import coil.ImageLoader -import coil.request.ImageRequest -import com.google.accompanist.imageloading.ImageLoadState -import com.google.accompanist.imageloading.ImageSuchDeprecated -import com.google.accompanist.imageloading.MaterialLoadingImage -import com.google.accompanist.imageloading.isFinalState -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.filter - -/** - * Creates a composable that will attempt to load the given [data] using [Coil], and provides - * complete content of how the current state is displayed: - * - * ``` - * CoilImage( - * data = "https://www.image.url", - * ) { imageState -> - * when (imageState) { - * is ImageLoadState.Success -> // TODO - * is ImageLoadState.Error -> // TODO - * ImageLoadState.Loading -> // TODO - * ImageLoadState.Empty -> // TODO - * } - * } - * ``` - * - * @param data The data to load. See [ImageRequest.Builder.data] for the types allowed. - * @param modifier [Modifier] used to adjust the layout algorithm or draw decoration content. - * @param requestBuilder Optional builder for the [ImageRequest]. - * @param imageLoader The [ImageLoader] to use when requesting the image. Defaults to - * [CoilPainterDefaults.defaultImageLoader]. - * @param previewPlaceholder Drawable resource ID which will be displayed when this function is - * ran in preview mode. - * @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. - */ -@Deprecated( - "Replaced with Image() and rememberCoilPainter()", - ReplaceWith( - expression = """Image( - painter = rememberCoilPainter( - request = data, - imageLoader = imageLoader, - requestBuilder = requestBuilder, - fadeIn = fadeIn, - previewPlaceholder = previewPlaceholder, - shouldRefetchOnSizeChange = shouldRefetchOnSizeChange, - ), - contentDescription = null, - modifier = modifier, - )""", - "androidx.compose.foundation.Image", - "com.google.accompanist.coil.rememberCoilPainter", - ) -) -@Composable -fun CoilImage( - data: Any, - modifier: Modifier = Modifier, - requestBuilder: (ImageRequest.Builder.(size: IntSize) -> ImageRequest.Builder)? = null, - imageLoader: ImageLoader = CoilPainterDefaults.defaultImageLoader(), - @DrawableRes previewPlaceholder: Int = 0, - shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = { _, _ -> false }, - onRequestCompleted: (ImageLoadState) -> Unit = {}, - content: @Composable BoxScope.(imageLoadState: ImageLoadState) -> Unit -) { - val painter = rememberCoilPainter( - request = data, - requestBuilder = requestBuilder, - imageLoader = imageLoader, - shouldRefetchOnSizeChange = shouldRefetchOnSizeChange, - previewPlaceholder = previewPlaceholder, - ) - - LaunchedEffect(painter) { - snapshotFlow { painter.loadState } - .filter { it.isFinalState() } - .collect { onRequestCompleted(it) } - } - - @Suppress("DEPRECATION") - ImageSuchDeprecated( - loadPainter = painter, - modifier = modifier, - previewPlaceholder = previewPlaceholder, - content = content, - ) -} - -/** - * Creates a composable that will attempt to load the given [request] using [Coil], and provides - * complete content of how the current state is displayed: - * - * ``` - * CoilImage( - * data = "https://www.image.url", - * ) { imageState -> - * when (imageState) { - * is ImageLoadState.Success -> // TODO - * is ImageLoadState.Error -> // TODO - * ImageLoadState.Loading -> // TODO - * ImageLoadState.Empty -> // TODO - * } - * } - * ``` - * - * @param request The request to execute. If the request does not have a [ImageRequest.sizeResolver] - * set, one will be set on the request using the layout constraints. - * @param modifier [Modifier] used to adjust the layout algorithm or draw decoration content. - * @param requestBuilder Optional builder for the [ImageRequest]. - * @param imageLoader The [ImageLoader] to use when requesting the image. Defaults to - * [CoilPainterDefaults.defaultImageLoader]. - * @param previewPlaceholder Drawable resource ID which will be displayed when this function is - * ran in preview mode. - * @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. - */ -@Deprecated( - "Replaced with Image() and rememberCoilPainter()", - ReplaceWith( - expression = """Image( - painter = rememberCoilPainter( - request = request, - imageLoader = imageLoader, - requestBuilder = requestBuilder, - fadeIn = fadeIn, - previewPlaceholder = previewPlaceholder, - shouldRefetchOnSizeChange = shouldRefetchOnSizeChange, - ), - contentDescription = null, - modifier = modifier, - )""", - "androidx.compose.foundation.Image", - "com.google.accompanist.coil.rememberCoilPainter", - ) -) -@Composable -fun CoilImage( - request: ImageRequest, - modifier: Modifier = Modifier, - requestBuilder: (ImageRequest.Builder.(size: IntSize) -> ImageRequest.Builder)? = null, - imageLoader: ImageLoader = CoilPainterDefaults.defaultImageLoader(), - @DrawableRes previewPlaceholder: Int = 0, - shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = { _, _ -> false }, - onRequestCompleted: (ImageLoadState) -> Unit = {}, - content: @Composable BoxScope.(imageLoadState: ImageLoadState) -> Unit -) { - CoilImage( - data = request, - modifier = modifier, - requestBuilder = requestBuilder, - imageLoader = imageLoader, - previewPlaceholder = previewPlaceholder, - shouldRefetchOnSizeChange = shouldRefetchOnSizeChange, - onRequestCompleted = onRequestCompleted, - content = content - ) -} - -/** - * Creates a composable that will attempt to load the given [data] using [coil.Coil], and then - * display the result in an [Image]. - * - * This version of the function is more opinionated, providing: - * - * - Support for displaying alternative content while the request is 'loading'. - * See the [loading] parameter. - * - Support for displaying alternative content if the request was unsuccessful. - * See the [error] parameter. - * - Support for automatically fading-in the image once loaded. See the [fadeIn] parameter. - * - * ``` - * CoilImage( - * data = "https://www.image.url", - * fadeIn = true, - * loading = { - * Stack(Modifier.fillMaxSize()) { - * CircularProgressIndicator(Modifier.align(Alignment.Center)) - * } - * } - * ) - * ``` - * - * @param data The data to load. See [ImageRequest.Builder.data] for the types allowed. - * @param contentDescription text used by accessibility services to describe what this image - * represents. This should always be provided unless this image is used for decorative purposes, - * and does not represent a meaningful action that a user can take. This text should be - * localized, such as by using [androidx.compose.ui.res.stringResource] or similar. - * @param modifier [Modifier] used to adjust the layout algorithm or draw decoration content. - * @param alignment Optional alignment parameter used to place the loaded [ImageBitmap] in the - * given bounds defined by the width and height. - * @param contentScale Optional scale parameter used to determine the aspect ratio scaling to be - * used if the bounds are a different size from the intrinsic size of the loaded [ImageBitmap]. - * @param colorFilter Optional colorFilter to apply for the [Painter] when it is rendered onscreen. - * @param error Content to be displayed when the request failed. - * @param loading Content to be displayed when the request is in progress. - * @param fadeIn Whether to run a fade-in animation when images are successfully loaded. - * Default: `false`. - * @param requestBuilder Optional builder for the [ImageRequest]. - * @param imageLoader The [ImageLoader] to use when requesting the image. Defaults to - * [CoilPainterDefaults.defaultImageLoader]. - * @param previewPlaceholder Drawable resource ID which will be displayed when this function is - * ran in preview mode. - * @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. - */ -@Deprecated( - "Replaced with Image() and rememberCoilPainter()", - ReplaceWith( - expression = """Image( - painter = rememberCoilPainter( - request = data, - imageLoader = imageLoader, - requestBuilder = requestBuilder, - fadeIn = fadeIn, - previewPlaceholder = previewPlaceholder, - shouldRefetchOnSizeChange = shouldRefetchOnSizeChange, - ), - contentDescription = contentDescription, - modifier = modifier, - alignment = alignment, - contentScale = contentScale, - colorFilter = colorFilter, - )""", - "androidx.compose.foundation.Image", - "com.google.accompanist.coil.rememberCoilPainter", - ) -) -@Composable -fun CoilImage( - data: Any, - contentDescription: String?, - modifier: Modifier = Modifier, - alignment: Alignment = Alignment.Center, - contentScale: ContentScale = ContentScale.Fit, - colorFilter: ColorFilter? = null, - fadeIn: Boolean = false, - requestBuilder: (ImageRequest.Builder.(size: IntSize) -> ImageRequest.Builder)? = null, - imageLoader: ImageLoader = CoilPainterDefaults.defaultImageLoader(), - @DrawableRes previewPlaceholder: Int = 0, - shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = { _, _ -> false }, - onRequestCompleted: (ImageLoadState) -> Unit = {}, - error: @Composable (BoxScope.(ImageLoadState.Error) -> Unit)? = null, - loading: @Composable (BoxScope.() -> Unit)? = null, -) { - val painter = rememberCoilPainter( - request = data, - requestBuilder = requestBuilder, - imageLoader = imageLoader, - shouldRefetchOnSizeChange = shouldRefetchOnSizeChange, - fadeIn = false, // MaterialLoadingImage performs the fade - ) - - LaunchedEffect(painter) { - snapshotFlow { painter.loadState } - .filter { it.isFinalState() } - .collect { onRequestCompleted(it) } - } - - @Suppress("DEPRECATION") - ImageSuchDeprecated( - loadPainter = painter, - modifier = modifier, - previewPlaceholder = previewPlaceholder, - ) { imageState -> - when (imageState) { - is ImageLoadState.Success -> { - MaterialLoadingImage( - result = imageState, - contentDescription = contentDescription, - alignment = alignment, - contentScale = contentScale, - colorFilter = colorFilter, - fadeInEnabled = fadeIn, - ) - } - is ImageLoadState.Error -> if (error != null) error(imageState) - is ImageLoadState.Loading -> if (loading != null) loading() - else -> Unit - } - } -} - -/** - * Creates a composable that will attempt to load the given [request] using [Coil], and then - * display the result in an [Image]. - * - * This version of the function is more opinionated, providing: - * - * - Support for displaying alternative content while the request is 'loading'. - * See the [loading] parameter. - * - Support for displaying alternative content if the request was unsuccessful. - * See the [error] parameter. - * - Support for automatically fading-in the image once loaded. See the [fadeIn] parameter. - * - * ``` - * CoilImage( - * data = "https://www.image.url", - * fadeIn = true, - * loading = { - * Stack(Modifier.fillMaxSize()) { - * CircularProgressIndicator(Modifier.align(Alignment.Center)) - * } - * } - * ) - * ``` - * - * @param request The request to execute. If the request does not have a [ImageRequest.sizeResolver] - * set, one will be set on the request using the layout constraints. - * @param contentDescription text used by accessibility services to describe what this image - * represents. This should always be provided unless this image is used for decorative purposes, - * and does not represent a meaningful action that a user can take. This text should be - * localized, such as by using [androidx.compose.ui.res.stringResource] or similar. - * @param modifier [Modifier] used to adjust the layout algorithm or draw decoration content. - * @param alignment Optional alignment parameter used to place the loaded [ImageBitmap] in the - * given bounds defined by the width and height. - * @param contentScale Optional scale parameter used to determine the aspect ratio scaling to be - * used if the bounds are a different size from the intrinsic size of the loaded [ImageBitmap]. - * @param colorFilter Optional colorFilter to apply for the [Painter] when it is rendered onscreen. - * @param error Content to be displayed when the request failed. - * @param loading Content to be displayed when the request is in progress. - * @param fadeIn Whether to run a fade-in animation when images are successfully loaded. - * Default: `false`. - * @param requestBuilder Optional builder for the [ImageRequest]. - * @param imageLoader The [ImageLoader] to use when requesting the image. Defaults to - * [CoilPainterDefaults.defaultImageLoader]. - * @param previewPlaceholder Drawable resource ID which will be displayed when this function is - * ran in preview mode. - * @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. - */ -@Deprecated( - "Replaced with Image() and rememberCoilPainter()", - ReplaceWith( - expression = """Image( - painter = rememberCoilPainter( - data = request, - imageLoader = imageLoader, - requestBuilder = requestBuilder, - fadeIn = fadeIn, - previewPlaceholder = previewPlaceholder, - shouldRefetchOnSizeChange = shouldRefetchOnSizeChange, - ), - contentDescription = contentDescription, - modifier = modifier, - alignment = alignment, - contentScale = contentScale, - colorFilter = colorFilter, - )""", - "androidx.compose.foundation.Image", - "com.google.accompanist.coil.rememberCoilPainter", - ) -) -@Composable -fun CoilImage( - request: ImageRequest, - contentDescription: String?, - modifier: Modifier = Modifier, - alignment: Alignment = Alignment.Center, - contentScale: ContentScale = ContentScale.Fit, - colorFilter: ColorFilter? = null, - fadeIn: Boolean = false, - requestBuilder: (ImageRequest.Builder.(size: IntSize) -> ImageRequest.Builder)? = null, - imageLoader: ImageLoader = CoilPainterDefaults.defaultImageLoader(), - @DrawableRes previewPlaceholder: Int = 0, - shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = { _, _ -> false }, - onRequestCompleted: (ImageLoadState) -> Unit = {}, - error: @Composable (BoxScope.(ImageLoadState.Error) -> Unit)? = null, - loading: @Composable (BoxScope.() -> Unit)? = null, -) { - CoilImage( - data = request, - contentDescription = contentDescription, - modifier = modifier, - alignment = alignment, - contentScale = contentScale, - colorFilter = colorFilter, - fadeIn = fadeIn, - requestBuilder = requestBuilder, - imageLoader = imageLoader, - previewPlaceholder = previewPlaceholder, - shouldRefetchOnSizeChange = shouldRefetchOnSizeChange, - onRequestCompleted = onRequestCompleted, - error = error, - loading = loading, - ) -} diff --git a/glide/api/current.api b/glide/api/current.api index a969f0233..6d3b8c7a8 100644 --- a/glide/api/current.api +++ b/glide/api/current.api @@ -1,11 +1,6 @@ // Signature format: 4.0 package com.google.accompanist.glide { - public final class GlideImage { - method @Deprecated @androidx.compose.runtime.Composable public static void GlideImage(Object data, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function2,? super androidx.compose.ui.unit.IntSize,? extends com.bumptech.glide.RequestBuilder>? requestBuilder, optional com.bumptech.glide.RequestManager requestManager, optional @DrawableRes int previewPlaceholder, optional kotlin.jvm.functions.Function2 shouldRefetchOnSizeChange, optional kotlin.jvm.functions.Function1 onRequestCompleted, kotlin.jvm.functions.Function2 content); - method @Deprecated @androidx.compose.runtime.Composable public static void GlideImage(Object data, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional boolean fadeIn, optional kotlin.jvm.functions.Function2,? super androidx.compose.ui.unit.IntSize,? extends com.bumptech.glide.RequestBuilder>? requestBuilder, optional com.bumptech.glide.RequestManager requestManager, optional @DrawableRes int previewPlaceholder, optional kotlin.jvm.functions.Function2 shouldRefetchOnSizeChange, optional kotlin.jvm.functions.Function1 onRequestCompleted, optional kotlin.jvm.functions.Function2? error, optional kotlin.jvm.functions.Function1? loading); - } - public final class GlideKt { method public static androidx.compose.runtime.ProvidableCompositionLocal getLocalRequestManager(); method @androidx.compose.runtime.Composable public static com.google.accompanist.imageloading.LoadPainter rememberGlidePainter(Object? request, optional com.bumptech.glide.RequestManager requestManager, optional com.google.accompanist.imageloading.ShouldRefetchOnSizeChange shouldRefetchOnSizeChange, optional kotlin.jvm.functions.Function2,? super androidx.compose.ui.unit.IntSize,? extends com.bumptech.glide.RequestBuilder>? requestBuilder, optional boolean fadeIn, optional int fadeInDurationMs, optional @DrawableRes int previewPlaceholder); diff --git a/glide/src/androidTest/java/com/google/accompanist/glide/DeprecatedGlideTest.kt b/glide/src/androidTest/java/com/google/accompanist/glide/DeprecatedGlideTest.kt deleted file mode 100644 index 8aecb3c27..000000000 --- a/glide/src/androidTest/java/com/google/accompanist/glide/DeprecatedGlideTest.kt +++ /dev/null @@ -1,562 +0,0 @@ -/* - * 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. - */ - -package com.google.accompanist.glide - -import android.graphics.drawable.Drawable -import android.graphics.drawable.ShapeDrawable -import androidx.activity.ComponentActivity -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.size -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.painter.ColorPainter -import androidx.compose.ui.platform.LocalView -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.test.assertHeightIsAtLeast -import androidx.compose.ui.test.assertHeightIsEqualTo -import androidx.compose.ui.test.assertIsDisplayed -import androidx.compose.ui.test.assertWidthIsAtLeast -import androidx.compose.ui.test.assertWidthIsEqualTo -import androidx.compose.ui.test.captureToImage -import androidx.compose.ui.test.junit4.createAndroidComposeRule -import androidx.compose.ui.test.onNodeWithTag -import androidx.compose.ui.unit.dp -import androidx.lifecycle.Lifecycle -import androidx.test.filters.LargeTest -import androidx.test.filters.SdkSuppress -import com.bumptech.glide.Glide -import com.bumptech.glide.load.DataSource -import com.bumptech.glide.load.engine.GlideException -import com.bumptech.glide.request.RequestListener -import com.bumptech.glide.request.target.Target -import com.google.accompanist.glide.test.R -import com.google.accompanist.imageloading.ImageLoadState -import com.google.accompanist.imageloading.test.ImageMockWebServer -import com.google.accompanist.imageloading.test.assertPixels -import com.google.accompanist.imageloading.test.receiveBlocking -import com.google.accompanist.imageloading.test.resourceUri -import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.test.runBlockingTest -import kotlinx.coroutines.withTimeoutOrNull -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 - -@Suppress("DEPRECATION") -@OptIn(ExperimentalCoroutinesApi::class) -@LargeTest -@RunWith(JUnit4::class) -class DeprecatedGlideTest { - @get:Rule - val composeTestRule = createAndroidComposeRule(ComponentActivity::class.java) - - // Our MockWebServer. We use a response delay to simulate real-world conditions - private val server = ImageMockWebServer() - - @Before - fun setup() { - // Start our mock web server - server.start() - } - - @After - fun teardown() { - // Shutdown our mock web server - server.shutdown() - } - - @Test - fun onRequestCompleted() { - val results = ArrayList() - var requestCompleted by mutableStateOf(false) - - composeTestRule.setContent { - GlideImage( - data = server.url("/image").toString(), - requestBuilder = { - listener(object : RequestListener { - override fun onLoadFailed( - exception: GlideException?, - model: Any?, - target: Target?, - isFirstResource: Boolean - ): Boolean { - requestCompleted = true - // False so that Glide still invokes the Target - return false - } - - override fun onResourceReady( - resource: Drawable?, - model: Any?, - target: Target?, - source: DataSource?, - isFirstResource: Boolean - ): Boolean { - requestCompleted = true - // False so that Glide still invokes the Target - return false - } - }) - }, - contentDescription = null, - modifier = Modifier.size(128.dp, 128.dp), - onRequestCompleted = { results += it } - ) - } - - // Wait for the Glide request listener to release the latch - composeTestRule.waitUntil(10_000) { requestCompleted } - - composeTestRule.runOnIdle { - // And assert that we got a single successful result - assertThat(results).hasSize(1) - assertThat(results[0]).isInstanceOf(ImageLoadState.Success::class.java) - } - } - - @Test - fun basicLoad_http() { - var requestCompleted by mutableStateOf(false) - - composeTestRule.setContent { - GlideImage( - data = server.url("/image").toString(), - contentDescription = null, - modifier = Modifier - .size(128.dp, 128.dp) - .testTag(GlideTestTags.Image), - onRequestCompleted = { requestCompleted = true } - ) - } - - // Wait for the onRequestCompleted to release the latch - composeTestRule.waitUntil(10_000) { requestCompleted } - - composeTestRule.onNodeWithTag(GlideTestTags.Image) - .assertIsDisplayed() - .assertWidthIsEqualTo(128.dp) - .assertHeightIsEqualTo(128.dp) - } - - @Test - @SdkSuppress(minSdkVersion = 26) // captureToImage is SDK 26+ - fun basicLoad_drawable() { - var requestCompleted by mutableStateOf(false) - - composeTestRule.setContent { - GlideImage( - data = resourceUri(R.drawable.red_rectangle), - contentDescription = null, - modifier = Modifier - .size(128.dp, 128.dp) - .testTag(GlideTestTags.Image), - onRequestCompleted = { requestCompleted = true } - ) - } - - // Wait for the onRequestCompleted to release the latch - composeTestRule.waitUntil(10_000) { requestCompleted } - - composeTestRule.onNodeWithTag(GlideTestTags.Image) - .assertWidthIsEqualTo(128.dp) - .assertHeightIsEqualTo(128.dp) - .assertIsDisplayed() - .captureToImage() - .assertPixels(Color.Red) - } - - @OptIn(ExperimentalCoroutinesApi::class) - @Test - @SdkSuppress(minSdkVersion = 26) // captureToImage is SDK 26+ - fun basicLoad_switchData() { - var loadCompleteSignal by mutableStateOf(false) - var data by mutableStateOf(server.url("/red")) - - composeTestRule.setContent { - GlideImage( - data = data.toString(), - contentDescription = null, - modifier = Modifier - .size(128.dp, 128.dp) - .testTag(GlideTestTags.Image), - onRequestCompleted = { loadCompleteSignal = true } - ) - } - - // Await the first load - composeTestRule.waitUntil(10_000) { loadCompleteSignal } - loadCompleteSignal = false - - // Assert that the content is completely Red - composeTestRule.onNodeWithTag(GlideTestTags.Image) - .assertWidthIsEqualTo(128.dp) - .assertHeightIsEqualTo(128.dp) - .assertIsDisplayed() - .captureToImage() - .assertPixels(Color.Red) - - // Now switch the data URI to the blue drawable - data = server.url("/blue") - - // Await the second load - composeTestRule.waitUntil(10_000) { loadCompleteSignal } - - // Assert that the content is completely Blue - composeTestRule.onNodeWithTag(GlideTestTags.Image) - .assertWidthIsEqualTo(128.dp) - .assertHeightIsEqualTo(128.dp) - .assertIsDisplayed() - .captureToImage() - .assertPixels(Color.Blue) - } - - @OptIn(ExperimentalCoroutinesApi::class) - @Test - fun basicLoad_changeSize() = runBlockingTest { - val loadCompleteSignal = Channel(Channel.UNLIMITED) - var size by mutableStateOf(128.dp) - - composeTestRule.setContent { - GlideImage( - data = server.url("/red").toString(), - contentDescription = null, - modifier = Modifier - .size(size) - .testTag(GlideTestTags.Image), - onRequestCompleted = { loadCompleteSignal.offer(it) } - ) - } - - // Await the first load - assertThat(loadCompleteSignal.receiveBlocking()) - .isInstanceOf(ImageLoadState.Success::class.java) - - // Now change the size - size = 256.dp - - // Await the potential second load (which shouldn't come) - val result = withTimeoutOrNull(3000) { loadCompleteSignal.receive() } - assertThat(result).isNull() - - // Close the signal channel - loadCompleteSignal.close() - } - - @Test - fun basicLoad_nosize() { - var requestCompleted by mutableStateOf(false) - - composeTestRule.setContent { - GlideImage( - data = server.url("/image").toString(), - contentDescription = null, - modifier = Modifier.testTag(GlideTestTags.Image), - onRequestCompleted = { requestCompleted = true } - ) - } - - // Wait for the onRequestCompleted to release the latch - composeTestRule.waitUntil(10_000) { requestCompleted } - - composeTestRule.onNodeWithTag(GlideTestTags.Image) - .assertWidthIsAtLeast(1.dp) - .assertHeightIsAtLeast(1.dp) - .assertIsDisplayed() - } - - @Test - fun customRequestManager_param() { - var requestCompleted by mutableStateOf(false) - val loaded = mutableListOf() - - composeTestRule.setContent { - // Create a RequestManager with a listener which updates our loaded list - val glide = Glide.with(LocalView.current) - .addDefaultRequestListener(SimpleRequestListener { model -> loaded += model }) - - GlideImage( - data = server.url("/image").toString(), - requestManager = glide, - contentDescription = null, - modifier = Modifier.size(128.dp, 128.dp), - onRequestCompleted = { requestCompleted = true } - ) - } - - // Wait for the onRequestCompleted to release the latch - composeTestRule.waitUntil(10_000) { requestCompleted } - - // Assert that the listener was called - assertThat(loaded).hasSize(1) - } - - @Test - fun customRequestManager_ambient() { - var requestCompleted by mutableStateOf(false) - val loaded = mutableListOf() - - composeTestRule.setContent { - // Create a RequestManager with a listener which updates our loaded list - val glide = Glide.with(LocalView.current) - .addDefaultRequestListener(SimpleRequestListener { model -> loaded += model }) - - CompositionLocalProvider(LocalRequestManager provides glide) { - GlideImage( - data = server.url("/image").toString(), - contentDescription = null, - modifier = Modifier.size(128.dp, 128.dp), - onRequestCompleted = { requestCompleted = true } - ) - } - } - - // Wait for the onRequestCompleted to release the latch - composeTestRule.waitUntil(10_000) { requestCompleted } - - // Assert that the listener was called - assertThat(loaded).hasSize(1) - } - - @Test - fun errorStillHasSize() { - var requestCompleted by mutableStateOf(false) - - composeTestRule.setContent { - GlideImage( - data = server.url("/noimage").toString(), - contentDescription = null, - modifier = Modifier - .size(128.dp, 128.dp) - .testTag(GlideTestTags.Image), - onRequestCompleted = { requestCompleted = true } - ) - } - - // Wait for the onRequestCompleted to release the latch - composeTestRule.waitUntil(10_000) { requestCompleted } - - // Assert that the layout is in the tree and has the correct size - composeTestRule.onNodeWithTag(GlideTestTags.Image) - .assertIsDisplayed() - .assertWidthIsEqualTo(128.dp) - .assertHeightIsEqualTo(128.dp) - } - - @Test - fun content_error() { - var requestCompleted by mutableStateOf(false) - val states = ArrayList() - - composeTestRule.setContent { - GlideImage( - data = server.url("/noimage").toString(), - requestBuilder = { - // Disable memory cache. If the item is in the cache, the fetch is - // synchronous and the dispatcher pause has no effect - skipMemoryCache(true) - }, - modifier = Modifier.size(128.dp, 128.dp), - onRequestCompleted = { requestCompleted = true } - ) { state -> - states.add(state) - } - } - - // Wait for the onRequestCompleted to release the latch - composeTestRule.waitUntil(10_000) { requestCompleted } - - composeTestRule.runOnIdle { - // Check that the final state is an Error - assertThat(states.last()).isInstanceOf(ImageLoadState.Error::class.java) - } - } - - @Test - fun content_success() { - var requestCompleted by mutableStateOf(false) - val states = ArrayList() - - composeTestRule.setContent { - GlideImage( - data = server.url("/image").toString(), - requestBuilder = { - // Disable memory cache. If the item is in the cache, the fetch is - // synchronous and the dispatcher pause has no effect - skipMemoryCache(true) - }, - modifier = Modifier.size(128.dp, 128.dp), - onRequestCompleted = { requestCompleted = true } - ) { state -> - states.add(state) - } - } - - // Wait for the onRequestCompleted to release the latch - composeTestRule.waitUntil(10_000) { requestCompleted } - - composeTestRule.runOnIdle { - // Check that the final state is a Success - assertThat(states.last()).isInstanceOf(ImageLoadState.Success::class.java) - } - } - - @Test - @SdkSuppress(minSdkVersion = 26) // captureToImage is SDK 26+ - fun content_custom() { - var requestCompleted by mutableStateOf(false) - - composeTestRule.setContent { - GlideImage( - data = server.url("/image").toString(), - modifier = Modifier - .size(128.dp, 128.dp) - .testTag(GlideTestTags.Image), - onRequestCompleted = { requestCompleted = true } - ) { - // Return an Image which just draws cyan - Image( - painter = ColorPainter(Color.Cyan), - contentDescription = null, - modifier = Modifier.matchParentSize() - ) - } - } - - // Wait for the onRequestCompleted to release the latch - composeTestRule.waitUntil(10_000) { requestCompleted } - - // Assert that the whole layout is drawn cyan - composeTestRule.onNodeWithTag(GlideTestTags.Image) - .assertIsDisplayed() - .captureToImage() - .assertPixels(Color.Cyan, 0.05f) - } - - @Test - @SdkSuppress(minSdkVersion = 26) // captureToImage is SDK 26+ - fun error_slot() { - var requestCompleted by mutableStateOf(false) - - composeTestRule.setContent { - GlideImage( - data = server.url("/noimage").toString(), - error = { - // Return failure content which just draws red - Image( - painter = ColorPainter(Color.Red), - contentDescription = null, - modifier = Modifier.matchParentSize() - ) - }, - contentDescription = null, - modifier = Modifier - .size(128.dp, 128.dp) - .testTag(GlideTestTags.Image), - onRequestCompleted = { requestCompleted = true } - ) - } - - // Wait for the onRequestCompleted to release the latch - composeTestRule.waitUntil(10_000) { requestCompleted } - - // Assert that the whole layout is drawn red - composeTestRule.onNodeWithTag(GlideTestTags.Image) - .assertIsDisplayed() - .captureToImage() - .assertPixels(Color.Red) - } - - @Test(expected = IllegalArgumentException::class) - fun data_drawable_throws() { - composeTestRule.setContent { - GlideImage( - data = ShapeDrawable(), - contentDescription = null, - modifier = Modifier.size(128.dp, 128.dp), - ) - } - } - - @Test(expected = IllegalArgumentException::class) - fun data_imagebitmap_throws() { - composeTestRule.setContent { - GlideImage( - data = painterResource(android.R.drawable.ic_delete), - contentDescription = null, - modifier = Modifier.size(128.dp, 128.dp), - ) - } - } - - @Test(expected = IllegalArgumentException::class) - fun data_imagevector_throws() { - composeTestRule.setContent { - GlideImage( - data = painterResource(R.drawable.ic_android_black_24dp), - contentDescription = null, - modifier = Modifier.size(128.dp, 128.dp), - ) - } - } - - @Test(expected = IllegalArgumentException::class) - fun data_painter_throws() { - composeTestRule.setContent { - GlideImage( - data = ColorPainter(Color.Magenta), - contentDescription = null, - modifier = Modifier.size(128.dp, 128.dp), - ) - } - } - - @Test - fun error_stoppedThenResumed() { - var requestCompleted by mutableStateOf(false) - - composeTestRule.setContent { - GlideImage( - data = "", - contentDescription = null, - modifier = Modifier.size(128.dp, 128.dp), - onRequestCompleted = { requestCompleted = true } - ) - } - - // Wait for the onRequestCompleted to release the latch - composeTestRule.waitUntil(10_000) { requestCompleted } - - // Now stop the activity, then resume it - composeTestRule.activityRule.scenario - .moveToState(Lifecycle.State.CREATED) - .moveToState(Lifecycle.State.RESUMED) - - // And wait for idle. We shouldn't crash. - composeTestRule.waitForIdle() - } -} diff --git a/glide/src/main/java/com/google/accompanist/glide/DeprecatedGlide.kt b/glide/src/main/java/com/google/accompanist/glide/DeprecatedGlide.kt deleted file mode 100644 index 28fb922e3..000000000 --- a/glide/src/main/java/com/google/accompanist/glide/DeprecatedGlide.kt +++ /dev/null @@ -1,252 +0,0 @@ -/* - * 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. - */ - -@file:JvmName("GlideImage") -@file:JvmMultifileClass - -@file:Suppress("DEPRECATION") - -package com.google.accompanist.glide - -import android.graphics.drawable.Drawable -import androidx.annotation.DrawableRes -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.BoxScope -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.snapshotFlow -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.ImageBitmap -import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.unit.IntSize -import com.bumptech.glide.Glide -import com.bumptech.glide.RequestBuilder -import com.bumptech.glide.RequestManager -import com.google.accompanist.imageloading.ImageLoadState -import com.google.accompanist.imageloading.ImageSuchDeprecated -import com.google.accompanist.imageloading.MaterialLoadingImage -import com.google.accompanist.imageloading.isFinalState -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.filter - -/** - * Creates a composable that will attempt to load the given [data] using [Glide], and provides - * complete content of how the current state is displayed: - * - * ``` - * GlideImage( - * data = "https://www.image.url", - * ) { imageState -> - * when (imageState) { - * is ImageLoadState.Success -> // TODO - * is ImageLoadState.Error -> // TODO - * ImageLoadState.Loading -> // TODO - * ImageLoadState.Empty -> // TODO - * } - * } - * ``` - * - * @param data The data to load. - * @param modifier [Modifier] used to adjust the layout algorithm or draw decoration content. - * @param requestBuilder Optional builder for the [RequestBuilder]. - * @param requestManager The [RequestManager] to use when requesting the image. Defaults to the - * current value of [LocalRequestManager]. - * @param previewPlaceholder Drawable resource ID which will be displayed when this function is - * ran in preview mode. - * @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. - */ -@Deprecated( - "Replaced with Image() and rememberGlidePainter()", - ReplaceWith( - expression = """Image( - painter = rememberGlidePainter( - request = data, - requestManager = requestManager, - requestBuilder = requestBuilder, - fadeIn = fadeIn, - previewPlaceholder = previewPlaceholder, - shouldRefetchOnSizeChange = shouldRefetchOnSizeChange, - ), - contentDescription = null, - modifier = modifier, - )""", - "androidx.compose.foundation.Image", - "com.google.accompanist.glide.rememberGlidePainter", - ) -) -@Composable -fun GlideImage( - data: Any, - modifier: Modifier = Modifier, - requestBuilder: (RequestBuilder.(size: IntSize) -> RequestBuilder)? = null, - requestManager: RequestManager = GlidePainterDefaults.defaultRequestManager(), - @DrawableRes previewPlaceholder: Int = 0, - shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = { _, _ -> false }, - onRequestCompleted: (ImageLoadState) -> Unit = {}, - content: @Composable BoxScope.(imageLoadState: ImageLoadState) -> Unit -) { - val painter = rememberGlidePainter( - request = data, - requestManager = requestManager, - requestBuilder = requestBuilder, - shouldRefetchOnSizeChange = shouldRefetchOnSizeChange, - ) - - LaunchedEffect(painter) { - snapshotFlow { painter.loadState } - .filter { it.isFinalState() } - .collect { onRequestCompleted(it) } - } - - @Suppress("DEPRECATION") - ImageSuchDeprecated( - loadPainter = painter, - modifier = modifier, - previewPlaceholder = previewPlaceholder, - content = content - ) -} - -/** - * Creates a composable that will attempt to load the given [data] using [Glide], and then - * display the result in an [Image]. - * - * This version of the function is more opinionated, providing: - * - * - Support for displaying alternative content while the request is 'loading'. - * See the [loading] parameter. - * - Support for displaying alternative content if the request was unsuccessful. - * See the [error] parameter. - * - Support for automatically fading-in the image once loaded. See the [fadeIn] parameter. - * - * ``` - * GlideImage( - * data = "https://www.image.url", - * fadeIn = true, - * loading = { - * Stack(Modifier.fillMaxSize()) { - * CircularProgressIndicator(Modifier.align(Alignment.Center)) - * } - * } - * ) - * ``` - * - * @param data The data to load. - * @param contentDescription text used by accessibility services to describe what this image - * represents. This should always be provided unless this image is used for decorative purposes, - * and does not represent a meaningful action that a user can take. This text should be - * localized, such as by using [androidx.compose.ui.res.stringResource] or similar. - * @param modifier [Modifier] used to adjust the layout algorithm or draw decoration content. - * @param alignment Optional alignment parameter used to place the loaded [ImageBitmap] in the - * given bounds defined by the width and height. - * @param contentScale Optional scale parameter used to determine the aspect ratio scaling to be - * used if the bounds are a different size from the intrinsic size of the loaded [ImageBitmap]. - * @param colorFilter Optional colorFilter to apply for the [Painter] when it is rendered onscreen. - * @param error Content to be displayed when the request failed. - * @param loading Content to be displayed when the request is in progress. - * @param fadeIn Whether to run a fade-in animation when images are successfully loaded. - * Default: `false`. - * @param requestBuilder Optional builder for the [RequestBuilder]. - * @param requestManager The [RequestManager] to use when requesting the image. Defaults to the - * current value of [LocalRequestManager]. - * @param previewPlaceholder Drawable resource ID which will be displayed when this function is - * ran in preview mode. - * @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. - */ -@Deprecated( - "Replaced with Image() and rememberGlidePainter()", - ReplaceWith( - expression = """Image( - painter = rememberGlidePainter( - request = data, - requestManager = requestManager, - requestBuilder = requestBuilder, - fadeIn = fadeIn, - previewPlaceholder = previewPlaceholder, - shouldRefetchOnSizeChange = shouldRefetchOnSizeChange, - ), - contentDescription = contentDescription, - modifier = modifier, - alignment = alignment, - contentScale = contentScale, - colorFilter = colorFilter, - )""", - "androidx.compose.foundation.Image", - "com.google.accompanist.glide.rememberGlidePainter", - ) -) -@Composable -fun GlideImage( - data: Any, - contentDescription: String?, - modifier: Modifier = Modifier, - alignment: Alignment = Alignment.Center, - contentScale: ContentScale = ContentScale.Fit, - colorFilter: ColorFilter? = null, - fadeIn: Boolean = false, - requestBuilder: (RequestBuilder.(size: IntSize) -> RequestBuilder)? = null, - requestManager: RequestManager = GlidePainterDefaults.defaultRequestManager(), - @DrawableRes previewPlaceholder: Int = 0, - shouldRefetchOnSizeChange: (currentResult: ImageLoadState, size: IntSize) -> Boolean = { _, _ -> false }, - onRequestCompleted: (ImageLoadState) -> Unit = {}, - error: @Composable (BoxScope.(ImageLoadState.Error) -> Unit)? = null, - loading: @Composable (BoxScope.() -> Unit)? = null, -) { - val painter = rememberGlidePainter( - request = data, - requestManager = requestManager, - requestBuilder = requestBuilder, - shouldRefetchOnSizeChange = shouldRefetchOnSizeChange, - fadeIn = false, // MaterialLoadingImage performs the fade - ) - - LaunchedEffect(painter) { - snapshotFlow { painter.loadState } - .filter { it.isFinalState() } - .collect { onRequestCompleted(it) } - } - - @Suppress("DEPRECATION") - ImageSuchDeprecated( - loadPainter = painter, - modifier = modifier, - previewPlaceholder = previewPlaceholder, - ) { imageState -> - when (imageState) { - is ImageLoadState.Success -> { - MaterialLoadingImage( - result = imageState, - contentDescription = contentDescription, - alignment = alignment, - contentScale = contentScale, - colorFilter = colorFilter, - fadeInEnabled = fadeIn, - ) - } - is ImageLoadState.Error -> if (error != null) error(imageState) - is ImageLoadState.Loading -> if (loading != null) loading() - else -> Unit - } - } -} diff --git a/imageloading-core/api/current.api b/imageloading-core/api/current.api index 958832bd6..667087be2 100644 --- a/imageloading-core/api/current.api +++ b/imageloading-core/api/current.api @@ -7,13 +7,8 @@ package com.google.accompanist.imageloading { enum_constant public static final com.google.accompanist.imageloading.DataSource NETWORK; } - public final class DeprecatedKt { - method @Deprecated public static kotlin.jvm.functions.Function2 getDefaultRefetchOnSizeChangeLambda(); - } - public final class DrawablePainterKt { method @androidx.compose.runtime.Composable public static androidx.compose.ui.graphics.painter.Painter rememberDrawablePainter(android.graphics.drawable.Drawable? drawable); - method @Deprecated public static androidx.compose.ui.graphics.painter.Painter toPainter(android.graphics.drawable.Drawable); } public abstract sealed class ImageLoadState { @@ -97,9 +92,6 @@ package com.google.accompanist.imageloading { } public final class MaterialLoadingImage { - method @Deprecated @androidx.compose.runtime.Composable public static void MaterialLoadingImage(androidx.compose.ui.graphics.ImageBitmap asset, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional boolean fadeInEnabled, optional int fadeInDurationMs); - method @Deprecated @androidx.compose.runtime.Composable public static void MaterialLoadingImage(androidx.compose.ui.graphics.painter.Painter painter, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional boolean fadeInEnabled, optional int fadeInDurationMs); - method @Deprecated @androidx.compose.runtime.Composable public static void MaterialLoadingImage(com.google.accompanist.imageloading.ImageLoadState.Success result, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional boolean skipFadeWhenLoadedFromMemory, optional boolean fadeInEnabled, optional int fadeInDurationMs); } public fun interface ShouldRefetchOnSizeChange { diff --git a/imageloading-core/src/main/java/com/google/accompanist/imageloading/Deprecated.kt b/imageloading-core/src/main/java/com/google/accompanist/imageloading/Deprecated.kt deleted file mode 100644 index 7e370eec2..000000000 --- a/imageloading-core/src/main/java/com/google/accompanist/imageloading/Deprecated.kt +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2021 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. - */ - -@file:Suppress("unused") - -package com.google.accompanist.imageloading - -import androidx.annotation.DrawableRes -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxScope -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.layout.layout -import androidx.compose.ui.platform.LocalInspectionMode -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.unit.IntSize - -/** - * Default lambda for use in the `shouldRefetchOnSizeChange` parameter. - */ -@Deprecated("Create your own lambda instead", ReplaceWith("{ _, _ -> false }")) -val DefaultRefetchOnSizeChangeLambda: (ImageLoadState, IntSize) -> Boolean = { _, _ -> false } - -/** - * @hide - */ -@Deprecated("Only used to help migration. DO NOT USE.") -@Composable -fun ImageSuchDeprecated( - loadPainter: LoadPainter, - modifier: Modifier = Modifier, - @DrawableRes previewPlaceholder: Int = 0, - content: @Composable BoxScope.(imageLoadState: ImageLoadState) -> Unit, -) { - if (LocalInspectionMode.current && previewPlaceholder != 0) { - // If we're in inspection mode (preview) and we have a preview placeholder, just draw - // that using an Image and return - Image( - painter = painterResource(previewPlaceholder), - contentDescription = null, - modifier = modifier, - ) - return - } - - Box( - propagateMinConstraints = true, - modifier = modifier - // Layout modifier to receive the incoming constraints, such that we can use them - // to update our request size - .layout { measurable, constraints -> - loadPainter.requestSize = IntSize( - width = if (constraints.hasBoundedWidth) constraints.maxWidth else -1, - height = if (constraints.hasBoundedHeight) constraints.maxHeight else -1 - ) - - // No-op measure + layout - val placeable = measurable.measure(constraints) - layout(width = placeable.width, height = placeable.height) { - placeable.place(0, 0) - } - } - ) { - content(loadPainter.loadState) - } -} diff --git a/imageloading-core/src/main/java/com/google/accompanist/imageloading/DrawablePainter.kt b/imageloading-core/src/main/java/com/google/accompanist/imageloading/DrawablePainter.kt index 413ff370a..588472c59 100644 --- a/imageloading-core/src/main/java/com/google/accompanist/imageloading/DrawablePainter.kt +++ b/imageloading-core/src/main/java/com/google/accompanist/imageloading/DrawablePainter.kt @@ -156,8 +156,12 @@ private class DrawablePainter( @Composable fun rememberDrawablePainter(drawable: Drawable?): Painter { val painter = remember(drawable) { - @Suppress("DEPRECATION") // We will inline the code once we remove the function - drawable?.toPainter() ?: EmptyPainter + when (drawable) { + null -> EmptyPainter + is BitmapDrawable -> BitmapPainter(drawable.bitmap.asImageBitmap()) + is ColorDrawable -> ColorPainter(Color(drawable.color)) + else -> DrawablePainter(drawable.mutate()) + } } DisposableEffect(painter) { @@ -169,20 +173,3 @@ fun rememberDrawablePainter(drawable: Drawable?): Painter { return painter } - -/** - * Allows wrapping of a [Drawable] into a [Painter], attempting to un-wrap the drawable contents - * and use Compose primitives where possible. - */ -@Deprecated( - "Migrate to rememberDrawablePainter()", - ReplaceWith( - "rememberDrawablePainter(this)", - "com.google.accompanist.imageloading.rememberDrawablePainter", - ) -) -fun Drawable.toPainter(): Painter = when (this) { - is BitmapDrawable -> BitmapPainter(bitmap.asImageBitmap()) - is ColorDrawable -> ColorPainter(Color(color)) - else -> DrawablePainter(mutate()) -} diff --git a/imageloading-core/src/main/java/com/google/accompanist/imageloading/MaterialLoadingImage.kt b/imageloading-core/src/main/java/com/google/accompanist/imageloading/MaterialLoadingImage.kt index c506258e0..2a98d07cb 100644 --- a/imageloading-core/src/main/java/com/google/accompanist/imageloading/MaterialLoadingImage.kt +++ b/imageloading-core/src/main/java/com/google/accompanist/imageloading/MaterialLoadingImage.kt @@ -22,7 +22,6 @@ import androidx.compose.animation.core.MutableTransitionState import androidx.compose.animation.core.animateFloat import androidx.compose.animation.core.tween import androidx.compose.animation.core.updateTransition -import androidx.compose.foundation.Image import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable import androidx.compose.runtime.State @@ -30,170 +29,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorMatrix -import androidx.compose.ui.graphics.ImageBitmap -import androidx.compose.ui.graphics.painter.BitmapPainter -import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.layout.ContentScale - -internal const val DefaultTransitionDuration = 1000 - -/** - * A wrapper around [Image] which implements the - * [Material Image Loading](https://material.io/archive/guidelines/patterns/loading-images.html) - * pattern. - * - * @param asset The [ImageBitmap] to draw. - * @param contentDescription text used by accessibility services to describe what this image - * represents. This should always be provided unless this image is used for decorative purposes, - * and does not represent a meaningful action that a user can take. This text should be - * localized, such as by using [androidx.compose.ui.res.stringResource] or similar. - * @param modifier Modifier used to adjust the layout algorithm or draw decoration content (ex. - * background) - * @param alignment Optional alignment parameter used to place the [ImageBitmap] in the given - * bounds defined by the width and height. - * @param contentScale Optional scale parameter used to determine the aspect ratio scaling to be used - * if the bounds are a different size from the intrinsic size of the [ImageBitmap]. - * @param colorFilter Optional ColorFilter to apply for the [ImageBitmap] when it is rendered - * onscreen - * @param fadeInEnabled Whether the fade-in animation should be used or not. - * @param fadeInDurationMs The duration of the fade-in animation in milliseconds. - */ -@Deprecated("Migrate to rememberCoilPainter() or rememberGlidePainter()") -@Composable -fun MaterialLoadingImage( - asset: ImageBitmap, - contentDescription: String?, - modifier: Modifier = Modifier, - alignment: Alignment = Alignment.Center, - contentScale: ContentScale = ContentScale.Fit, - colorFilter: ColorFilter? = null, - fadeInEnabled: Boolean = true, - fadeInDurationMs: Int = DefaultTransitionDuration -) { - @Suppress("DEPRECATION") - MaterialLoadingImage( - painter = BitmapPainter(asset), - contentDescription = contentDescription, - modifier = modifier, - alignment = alignment, - contentScale = contentScale, - colorFilter = colorFilter, - fadeInEnabled = fadeInEnabled, - fadeInDurationMs = fadeInDurationMs - ) -} - -/** - * A wrapper around [Image] which implements the - * [Material Image Loading](https://material.io/archive/guidelines/patterns/loading-images.html) - * pattern. - * - * @param painter The [Painter] to draw. - * @param contentDescription text used by accessibility services to describe what this image - * represents. This should always be provided unless this image is used for decorative purposes, - * and does not represent a meaningful action that a user can take. This text should be - * localized, such as by using [androidx.compose.ui.res.stringResource] or similar. - * @param modifier Modifier used to adjust the layout algorithm or draw decoration content (ex. - * background) - * @param alignment Optional alignment parameter used to place the [painter] in the given - * bounds defined by the width and height. - * @param contentScale Optional scale parameter used to determine the aspect ratio scaling to be used - * if the bounds are a different size from the intrinsic size of the [ImageBitmap]. - * @param colorFilter Optional ColorFilter to apply for the [ImageBitmap] when it is rendered - * onscreen - * @param fadeInEnabled Whether the fade-in animation should be used or not. - * @param fadeInDurationMs The duration of the fade-in animation in milliseconds. - */ -@Deprecated("Migrate to rememberCoilPainter() or rememberGlidePainter()") -@Composable -fun MaterialLoadingImage( - painter: Painter, - contentDescription: String?, - modifier: Modifier = Modifier, - alignment: Alignment = Alignment.Center, - contentScale: ContentScale = ContentScale.Fit, - colorFilter: ColorFilter? = null, - fadeInEnabled: Boolean = true, - fadeInDurationMs: Int = DefaultTransitionDuration -) { - val cf = if (fadeInEnabled) { - val fadeInTransition = updateFadeInTransition(key = painter, durationMs = fadeInDurationMs) - remember { ColorMatrix() } - .apply { - updateAlpha(fadeInTransition.alpha) - updateBrightness(fadeInTransition.brightness) - updateSaturation(fadeInTransition.saturation) - } - .let { matrix -> - ColorFilter.colorMatrix(matrix) - } - } else { - // If fade in isn't enable, just use the provided `colorFilter` - colorFilter - } - - Image( - painter = painter, - contentDescription = contentDescription, - alignment = alignment, - contentScale = contentScale, - colorFilter = cf, - modifier = modifier, - ) -} - -/** - * A wrapper around [Image] which implements the - * [Material Image Loading](https://material.io/archive/guidelines/patterns/loading-images.html) - * pattern. - * - * @param result A [ImageLoadState.Success] instance. - * @param contentDescription text used by accessibility services to describe what this image - * represents. This should always be provided unless this image is used for decorative purposes, - * and does not represent a meaningful action that a user can take. This text should be - * localized, such as by using [androidx.compose.ui.res.stringResource] or similar. - * @param modifier Modifier used to adjust the layout algorithm or draw decoration content (ex. - * background) - * @param alignment Optional alignment parameter used to place the [ImageBitmap] in the given - * bounds defined by the width and height. - * @param contentScale Optional scale parameter used to determine the aspect ratio scaling to be used - * if the bounds are a different size from the intrinsic size of the [ImageBitmap]. - * @param colorFilter Optional ColorFilter to apply for the [ImageBitmap] when it is rendered - * onscreen - * @param skipFadeWhenLoadedFromMemory Whether the fade animation should be skipped when the result - * has been loaded from memory. - * @param fadeInEnabled Whether the fade-in animation should be used or not. - * @param fadeInDurationMs The duration of the fade-in animation in milliseconds. - */ -@Deprecated("Migrate to rememberCoilPainter() or rememberGlidePainter()") -@Composable -fun MaterialLoadingImage( - result: ImageLoadState.Success, - contentDescription: String?, - modifier: Modifier = Modifier, - alignment: Alignment = Alignment.Center, - contentScale: ContentScale = ContentScale.Fit, - colorFilter: ColorFilter? = null, - skipFadeWhenLoadedFromMemory: Boolean = true, - fadeInEnabled: Boolean = true, - fadeInDurationMs: Int = DefaultTransitionDuration -) { - @Suppress("DEPRECATION") - MaterialLoadingImage( - painter = rememberDrawablePainter(result.result), - contentDescription = contentDescription, - alignment = alignment, - contentScale = contentScale, - colorFilter = colorFilter, - modifier = modifier, - fadeInEnabled = fadeInEnabled && !(skipFadeWhenLoadedFromMemory && result.isFromMemory()), - fadeInDurationMs = fadeInDurationMs, - ) -} @Composable internal fun updateFadeInTransition(key: Any, durationMs: Int): FadeInTransition { diff --git a/pager/api/current.api b/pager/api/current.api index 1b20640bb..6261be404 100644 --- a/pager/api/current.api +++ b/pager/api/current.api @@ -48,7 +48,6 @@ package com.google.accompanist.pager { } public final class PagerStateKt { - method @Deprecated public static inline kotlinx.coroutines.flow.Flow! getPageChanges(com.google.accompanist.pager.PagerState); method @androidx.compose.runtime.Composable @com.google.accompanist.pager.ExperimentalPagerApi public static com.google.accompanist.pager.PagerState rememberPagerState(@IntRange(from=0) int pageCount, optional @IntRange(from=0) int initialPage, optional @FloatRange(from=0.0, to=1.0) float initialPageOffset, optional @IntRange(from=1) int initialOffscreenLimit); } diff --git a/pager/src/androidTest/java/com/google/accompanist/pager/PagerTest.kt b/pager/src/androidTest/java/com/google/accompanist/pager/PagerTest.kt index ad05494d4..9816abfab 100644 --- a/pager/src/androidTest/java/com/google/accompanist/pager/PagerTest.kt +++ b/pager/src/androidTest/java/com/google/accompanist/pager/PagerTest.kt @@ -27,8 +27,6 @@ import androidx.compose.ui.test.performScrollTo import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.FlowPreview -import kotlinx.coroutines.flow.produceIn import kotlinx.coroutines.withContext import org.junit.Ignore import org.junit.Rule @@ -197,49 +195,6 @@ abstract class PagerTest { assertPagerLayout(0, pagerState.pageCount) } - @Suppress("DEPRECATION") // pageChanges - @OptIn(FlowPreview::class) - @Test - fun pageChanges() = suspendTest { - val pagerState = setPagerContent(pageCount = 10) - - // Collect the pageChanges flow into a Channel, allowing us to poll values - val pageChangedChannel = pagerState.pageChanges.produceIn(this) - - // Assert that the first emission is 0 - assertThat(pageChangedChannel.receive()).isEqualTo(0) - - // Now swipe to page 1.. - composeTestRule.onNodeWithTag("0").swipeAcrossCenter( - distancePercentage = -MediumSwipeDistance, - velocity = MediumVelocity, - ) - // ...and assert that the page 2 is emitted - assertThat(pageChangedChannel.receive()).isEqualTo(1) - - // Now swipe to page 2... - composeTestRule.onNodeWithTag("1").swipeAcrossCenter( - distancePercentage = -MediumSwipeDistance, - velocity = MediumVelocity, - ) - // ...and assert that the page 2 is emitted - assertThat(pageChangedChannel.receive()).isEqualTo(2) - - // Now swipe back to page 1... - composeTestRule.onNodeWithTag("2").swipeAcrossCenter( - distancePercentage = MediumSwipeDistance, - velocity = MediumVelocity, - ) - // ...and assert that the page 1 is emitted - assertThat(pageChangedChannel.receive()).isEqualTo(1) - - // Assert that there are no more events - assertThat(pageChangedChannel.poll()).isNull() - - // Cancel the channel - pageChangedChannel.cancel() - } - @Test fun scrollToPage() = suspendTest { val pagerState = setPagerContent(pageCount = 10) diff --git a/pager/src/main/java/com/google/accompanist/pager/PagerState.kt b/pager/src/main/java/com/google/accompanist/pager/PagerState.kt index 868430958..dfda5f8be 100644 --- a/pager/src/main/java/com/google/accompanist/pager/PagerState.kt +++ b/pager/src/main/java/com/google/accompanist/pager/PagerState.kt @@ -40,7 +40,6 @@ import androidx.compose.runtime.saveable.Saver import androidx.compose.runtime.saveable.listSaver import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue -import androidx.compose.runtime.snapshotFlow import kotlin.math.absoluteValue import kotlin.math.floor import kotlin.math.roundToInt @@ -635,22 +634,6 @@ class PagerState( } } -/** - * A flow which emits the currently selected page. - * - * @sample com.google.accompanist.sample.pager.PageChangesSample - */ -@Deprecated( - message = "Use snapshotFlow directly", - ReplaceWith( - expression = "snapshotFlow { this.currentPage }", - "androidx.compose.runtime.snapshotFlow" - ) -) -@ExperimentalPagerApi -inline val PagerState.pageChanges - get() = snapshotFlow { currentPage } - @Stable internal class PageLayoutInfo { var page: Int? by mutableStateOf(null) diff --git a/systemuicontroller/api/current.api b/systemuicontroller/api/current.api index cd5ba6d74..3b69b9e3e 100644 --- a/systemuicontroller/api/current.api +++ b/systemuicontroller/api/current.api @@ -9,8 +9,6 @@ package com.google.accompanist.systemuicontroller { } public final class SystemUiControllerKt { - method @Deprecated public static androidx.compose.runtime.ProvidableCompositionLocal getLocalSystemUiController(); - method @Deprecated @androidx.compose.runtime.Composable public static com.google.accompanist.systemuicontroller.SystemUiController rememberAndroidSystemUiController(optional android.view.View view); method @androidx.compose.runtime.Composable public static com.google.accompanist.systemuicontroller.SystemUiController rememberSystemUiController(); } diff --git a/systemuicontroller/src/main/java/com/google/accompanist/systemuicontroller/SystemUiController.kt b/systemuicontroller/src/main/java/com/google/accompanist/systemuicontroller/SystemUiController.kt index e23372114..d10870172 100644 --- a/systemuicontroller/src/main/java/com/google/accompanist/systemuicontroller/SystemUiController.kt +++ b/systemuicontroller/src/main/java/com/google/accompanist/systemuicontroller/SystemUiController.kt @@ -25,7 +25,6 @@ import android.view.Window import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable import androidx.compose.runtime.remember -import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.compositeOver import androidx.compose.ui.graphics.luminance @@ -116,18 +115,6 @@ fun rememberSystemUiController(): SystemUiController { return remember(view) { AndroidSystemUiController(view) } } -@Deprecated( - "Migrate to rememberSystemUiController()", - ReplaceWith( - "rememberSystemUiController()", - "com.google.accompanist.systemuicontroller.rememberSystemUiController" - ) -) -@Composable -fun rememberAndroidSystemUiController( - view: View = LocalView.current -): SystemUiController = remember(view) { AndroidSystemUiController(view) } - /** * A helper class for setting the navigation and status bar colors for a [View], gracefully * degrading behavior based upon API level. @@ -193,18 +180,6 @@ internal class AndroidSystemUiController(view: View) : SystemUiController { } } -/** - * An [androidx.compose.runtime.CompositionLocalProvider] holding the current - * [LocalSystemUiController]. Defaults to a no-op controller; consumers should - * [provide][androidx.compose.runtime.CompositionLocalProvider] a real one. - * - * @sample com.google.accompanist.sample.systemuicontroller.SystemUiControllerSample - */ -@Deprecated("Use rememberSystemUiController()") -val LocalSystemUiController = staticCompositionLocalOf { - NoOpSystemUiController -} - private val BlackScrim = Color(0f, 0f, 0f, 0.3f) // 30% opaque black private val BlackScrimmed: (Color) -> Color = { original -> BlackScrim.compositeOver(original)