-
Notifications
You must be signed in to change notification settings - Fork 6.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Remove GlidePainter in favor of Modifier nodes / Flows
Using a custom Modifier node instead of a Painter follows a recommendation from the Compose team. It will allow us or library users to compose Glide's modifier in the future for animations of other effects. For now we do not actually expose the Modifier directly. This change adds a GlideSubcomposition that uses' Glide's flows integration to allow subcomposition based on image load state. Subcomposition allows us to deprecate the expensive Composable placeholder variants and also allows users to implement complex layouts or animations. In a subsequent change we'll explore adding a default crossfade and/or exposing the Glide modifier node so that users of the library can compose modifiers themselves.
- Loading branch information
Showing
26 changed files
with
849 additions
and
301 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
169 changes: 169 additions & 0 deletions
169
...se/src/androidTest/java/com/bumptech/glide/integration/compose/GlideSubcompositionTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
package com.bumptech.glide.integration.compose | ||
|
||
import android.content.Context | ||
import androidx.compose.foundation.Image | ||
import androidx.compose.foundation.layout.Box | ||
import androidx.compose.foundation.layout.size | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.unit.dp | ||
import androidx.test.core.app.ApplicationProvider | ||
import com.bumptech.glide.Glide | ||
import com.bumptech.glide.integration.compose.test.GlideComposeRule | ||
import com.bumptech.glide.load.DataSource | ||
import com.google.common.truth.Truth.assertThat | ||
import org.junit.Rule | ||
import org.junit.Test | ||
|
||
@OptIn(ExperimentalGlideComposeApi::class) | ||
class GlideSubcompositionTest { | ||
val context: Context = ApplicationProvider.getApplicationContext() | ||
|
||
@get:Rule | ||
val glideComposeRule = GlideComposeRule() | ||
|
||
@Test | ||
fun glideSubcomposition_withoutSize_startsWithStateLoading() { | ||
var currentState: RequestState? = null | ||
glideComposeRule.setContent { | ||
GlideSubcomposition(model = android.R.drawable.star_big_on) { | ||
if (currentState == null) { | ||
currentState = state | ||
} | ||
} | ||
} | ||
assertThat(currentState).isEqualTo(RequestState.Loading) | ||
} | ||
|
||
@Test | ||
fun glideSubcomposition_withOverrideSize_loadsImage() { | ||
var currentState: RequestState? = null | ||
glideComposeRule.setContent { | ||
GlideSubcomposition( | ||
android.R.drawable.star_big_on, | ||
requestBuilderTransform = { it.override(50) } | ||
) { | ||
currentState = state | ||
} | ||
} | ||
glideComposeRule.waitForIdle() | ||
assertThat(currentState).isInstanceOf(RequestState.Success::class.java) | ||
} | ||
|
||
@Test | ||
fun glideSubcomposition_whenDrawnWithSize_loadsImage() { | ||
var currentState: RequestState? = null | ||
glideComposeRule.setContent { | ||
GlideSubcomposition(model = android.R.drawable.star_big_on) { | ||
currentState = state | ||
Image( | ||
painter = painter, | ||
contentDescription = "", | ||
) | ||
} | ||
} | ||
glideComposeRule.waitForIdle() | ||
assertThat(currentState).isInstanceOf(RequestState.Success::class.java) | ||
} | ||
|
||
@Test | ||
fun glideSubcomposition_withLayoutSize_startsWithStateLoading() { | ||
var currentState: RequestState? = null | ||
glideComposeRule.setContent { | ||
GlideSubcomposition(model = android.R.drawable.star_big_on, Modifier.size(10.dp)) { | ||
if (currentState == null) { | ||
currentState = state | ||
} | ||
Image( | ||
painter = painter, | ||
contentDescription = "", | ||
) | ||
} | ||
} | ||
assertThat(currentState).isEqualTo(RequestState.Loading) | ||
} | ||
|
||
@Test | ||
fun glideSubcomposition_withLayoutSize_appliedToBox_loadsImage() { | ||
var currentState: RequestState? = null | ||
glideComposeRule.setContent { | ||
GlideSubcomposition(model = android.R.drawable.star_big_on, Modifier.size(10.dp)) { | ||
currentState = state | ||
Box(Modifier.size(10.dp)) | ||
} | ||
} | ||
glideComposeRule.waitForIdle() | ||
assertThat(currentState).isInstanceOf(RequestState.Success::class.java) | ||
} | ||
|
||
@Test | ||
fun glideSubcomposition_withOverrideSize_andInvalidImage_setsStateToFailed() { | ||
var currentState: RequestState? = null | ||
glideComposeRule.setContent { | ||
GlideSubcomposition(model = 1234, requestBuilderTransform = { it.override(50) }) { | ||
currentState = state | ||
} | ||
} | ||
glideComposeRule.waitForIdle() | ||
assertThat(currentState).isEqualTo(RequestState.Failure) | ||
} | ||
|
||
@Test | ||
fun glideSubcomposition_withLayoutSize_andInvalidImage_setsStateToFailed() { | ||
var currentState: RequestState? = null | ||
glideComposeRule.setContent { | ||
GlideSubcomposition(model = 1234, Modifier.size(10.dp)) { | ||
currentState = state | ||
} | ||
} | ||
glideComposeRule.waitForIdle() | ||
assertThat(currentState).isEqualTo(RequestState.Failure) | ||
} | ||
|
||
@Test | ||
fun glideSubcomposition_onLoadFromSource_setsDataSourceToSource() { | ||
var dataSource: DataSource? = null | ||
glideComposeRule.setContent { | ||
GlideSubcomposition( | ||
model = android.R.drawable.star_big_on, | ||
requestBuilderTransform = { it.override(50) } | ||
) { | ||
val currentState = state | ||
if (currentState is RequestState.Success) { | ||
dataSource = currentState.dataSource | ||
} | ||
} | ||
} | ||
glideComposeRule.waitForIdle() | ||
assertThat(dataSource).isEqualTo(DataSource.LOCAL) | ||
} | ||
|
||
@Test | ||
fun glideSubcomposition_onLoadFromMemory_setsDataSourceToMemory() { | ||
var dataSource: DataSource? = null | ||
val resourceId = android.R.drawable.star_big_on | ||
val overrideSize = 50 | ||
// TODO: Compose always uses the generic paths to load models, so it skips options that are | ||
// set by default by Glide's various class specific .load() method overrides. | ||
val future = Glide.with(context).load(resourceId as Any).override(overrideSize).submit() | ||
glideComposeRule.waitForIdle() | ||
future.get() | ||
glideComposeRule.setContent { | ||
GlideSubcomposition( | ||
model = resourceId, | ||
requestBuilderTransform = { it.override(overrideSize) } | ||
) { | ||
val currentState = state | ||
if (currentState is RequestState.Success) { | ||
dataSource = currentState.dataSource | ||
} | ||
} | ||
} | ||
|
||
glideComposeRule.waitForIdle() | ||
assertThat(dataSource).isEqualTo(DataSource.MEMORY_CACHE) | ||
} | ||
|
||
// private fun assertThatDataSource(painter: GlidePainter?): ComparableSubject<DataSource> = | ||
// assertThat((painter!!.state as GlidePainter.State.Success).dataSource) | ||
} | ||
|
Oops, something went wrong.