Merge pull request #4347 from element-hq/feature/bma/coil3
Migrate to coil3
This commit is contained in:
commit
e0de81c376
44 changed files with 172 additions and 127 deletions
|
|
@ -5,12 +5,15 @@
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@file:OptIn(DelicateCoilApi::class)
|
||||||
|
|
||||||
package io.element.android.appnav
|
package io.element.android.appnav
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import coil.Coil
|
import coil3.SingletonImageLoader
|
||||||
|
import coil3.annotation.DelicateCoilApi
|
||||||
import com.bumble.appyx.core.composable.Children
|
import com.bumble.appyx.core.composable.Children
|
||||||
import com.bumble.appyx.core.lifecycle.subscribe
|
import com.bumble.appyx.core.lifecycle.subscribe
|
||||||
import com.bumble.appyx.core.modality.BuildContext
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
|
|
@ -69,7 +72,7 @@ class LoggedInAppScopeFlowNode @AssistedInject constructor(
|
||||||
super.onBuilt()
|
super.onBuilt()
|
||||||
lifecycle.subscribe(
|
lifecycle.subscribe(
|
||||||
onCreate = {
|
onCreate = {
|
||||||
Coil.setImageLoader(imageLoaderHolder.get(inputs.matrixClient))
|
SingletonImageLoader.setUnsafe(imageLoaderHolder.get(inputs.matrixClient))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,15 @@
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@file:OptIn(DelicateCoilApi::class)
|
||||||
|
|
||||||
package io.element.android.appnav
|
package io.element.android.appnav
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import coil.Coil
|
import coil3.SingletonImageLoader
|
||||||
|
import coil3.annotation.DelicateCoilApi
|
||||||
import com.bumble.appyx.core.lifecycle.subscribe
|
import com.bumble.appyx.core.lifecycle.subscribe
|
||||||
import com.bumble.appyx.core.modality.BuildContext
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
import com.bumble.appyx.core.node.Node
|
import com.bumble.appyx.core.node.Node
|
||||||
|
|
@ -55,7 +58,7 @@ class NotLoggedInFlowNode @AssistedInject constructor(
|
||||||
super.onBuilt()
|
super.onBuilt()
|
||||||
lifecycle.subscribe(
|
lifecycle.subscribe(
|
||||||
onCreate = {
|
onCreate = {
|
||||||
Coil.setImageLoader(notLoggedInImageLoaderFactory)
|
SingletonImageLoader.setUnsafe(notLoggedInImageLoaderFactory.newImageLoader())
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ package io.element.android.features.call.notifications
|
||||||
|
|
||||||
import androidx.core.graphics.drawable.IconCompat
|
import androidx.core.graphics.drawable.IconCompat
|
||||||
import androidx.test.platform.app.InstrumentationRegistry
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
import coil.ImageLoader
|
import coil3.ImageLoader
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.call.impl.notifications.RingingCallNotificationCreator
|
import io.element.android.features.call.impl.notifications.RingingCallNotificationCreator
|
||||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
|
@ -22,9 +23,10 @@ import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import coil.compose.AsyncImagePainter
|
import coil3.Extras
|
||||||
import coil.compose.rememberAsyncImagePainter
|
import coil3.compose.AsyncImagePainter
|
||||||
import coil.request.ImageRequest
|
import coil3.compose.rememberAsyncImagePainter
|
||||||
|
import coil3.request.ImageRequest
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.features.location.api.internal.StaticMapPlaceholder
|
import io.element.android.features.location.api.internal.StaticMapPlaceholder
|
||||||
import io.element.android.features.location.api.internal.StaticMapUrlBuilder
|
import io.element.android.features.location.api.internal.StaticMapUrlBuilder
|
||||||
|
|
@ -33,7 +35,6 @@ import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||||
import io.element.android.libraries.designsystem.utils.CommonDrawables
|
import io.element.android.libraries.designsystem.utils.CommonDrawables
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows a static map image downloaded via a third party service's static maps API.
|
* Shows a static map image downloaded via a third party service's static maps API.
|
||||||
|
|
@ -75,14 +76,15 @@ fun StaticMapView(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.size(width = constraints.maxWidth, height = constraints.maxHeight)
|
.size(width = constraints.maxWidth, height = constraints.maxHeight)
|
||||||
.setParameter("retry_hash", retryHash, memoryCacheKey = null)
|
.apply {
|
||||||
|
extras.set(Extras.Key("retry_hash"), retryHash).build()
|
||||||
|
}
|
||||||
.build()
|
.build()
|
||||||
}.apply {
|
|
||||||
Timber.d("Static map image request: ${this?.data}")
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if (painter.state is AsyncImagePainter.State.Success) {
|
val collectedState = painter.state.collectAsState()
|
||||||
|
if (collectedState.value is AsyncImagePainter.State.Success) {
|
||||||
Image(
|
Image(
|
||||||
painter = painter,
|
painter = painter,
|
||||||
contentDescription = contentDescription,
|
contentDescription = contentDescription,
|
||||||
|
|
@ -100,7 +102,7 @@ fun StaticMapView(
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
StaticMapPlaceholder(
|
StaticMapPlaceholder(
|
||||||
showProgress = painter.state is AsyncImagePainter.State.Loading,
|
showProgress = collectedState.value.isLoading(),
|
||||||
contentDescription = contentDescription,
|
contentDescription = contentDescription,
|
||||||
width = maxWidth,
|
width = maxWidth,
|
||||||
height = maxHeight,
|
height = maxHeight,
|
||||||
|
|
@ -110,6 +112,11 @@ fun StaticMapView(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun AsyncImagePainter.State.isLoading(): Boolean {
|
||||||
|
return this is AsyncImagePainter.State.Empty ||
|
||||||
|
this is AsyncImagePainter.State.Loading
|
||||||
|
}
|
||||||
|
|
||||||
@PreviewsDayNight
|
@PreviewsDayNight
|
||||||
@Composable
|
@Composable
|
||||||
internal fun StaticMapViewPreview() = ElementPreview {
|
internal fun StaticMapViewPreview() = ElementPreview {
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import coil.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.features.messages.impl.R
|
import io.element.android.features.messages.impl.R
|
||||||
import io.element.android.features.messages.impl.timeline.model.AggregatedReaction
|
import io.element.android.features.messages.impl.timeline.model.AggregatedReaction
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,8 @@ import androidx.compose.ui.semantics.contentDescription
|
||||||
import androidx.compose.ui.semantics.semantics
|
import androidx.compose.ui.semantics.semantics
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import coil.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import coil.compose.AsyncImagePainter
|
import coil3.compose.AsyncImagePainter
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
|
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
|
||||||
import io.element.android.features.messages.impl.timeline.components.ATimelineItemEventRow
|
import io.element.android.features.messages.impl.timeline.components.ATimelineItemEventRow
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@ import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.semantics.contentDescription
|
import androidx.compose.ui.semantics.contentDescription
|
||||||
import androidx.compose.ui.semantics.semantics
|
import androidx.compose.ui.semantics.semantics
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import coil.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import coil.compose.AsyncImagePainter
|
import coil3.compose.AsyncImagePainter
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStickerContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStickerContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStickerContentProvider
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStickerContentProvider
|
||||||
import io.element.android.features.messages.impl.timeline.protection.ProtectedView
|
import io.element.android.features.messages.impl.timeline.protection.ProtectedView
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,8 @@ import androidx.compose.ui.semantics.contentDescription
|
||||||
import androidx.compose.ui.semantics.semantics
|
import androidx.compose.ui.semantics.semantics
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import coil.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import coil.compose.AsyncImagePainter
|
import coil3.compose.AsyncImagePainter
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||||
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
|
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import coil.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.features.messages.impl.timeline.components.REACTION_IMAGE_ASPECT_RATIO
|
import io.element.android.features.messages.impl.timeline.components.REACTION_IMAGE_ASPECT_RATIO
|
||||||
import io.element.android.features.messages.impl.timeline.model.AggregatedReaction
|
import io.element.android.features.messages.impl.timeline.model.AggregatedReaction
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,9 @@ dependencies {
|
||||||
implementation(libs.androidx.datastore.preferences)
|
implementation(libs.androidx.datastore.preferences)
|
||||||
api(projects.features.preferences.api)
|
api(projects.features.preferences.api)
|
||||||
|
|
||||||
|
implementation(platform(libs.network.okhttp.bom))
|
||||||
|
implementation(libs.network.okhttp)
|
||||||
|
|
||||||
testImplementation(libs.test.junit)
|
testImplementation(libs.test.junit)
|
||||||
testImplementation(libs.coroutines.test)
|
testImplementation(libs.coroutines.test)
|
||||||
testImplementation(libs.molecule.runtime)
|
testImplementation(libs.molecule.runtime)
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,10 @@
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@file:OptIn(ExperimentalCoilApi::class)
|
|
||||||
|
|
||||||
package io.element.android.features.preferences.impl.tasks
|
package io.element.android.features.preferences.impl.tasks
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import coil.Coil
|
import coil3.SingletonImageLoader
|
||||||
import coil.annotation.ExperimentalCoilApi
|
|
||||||
import com.squareup.anvil.annotations.ContributesBinding
|
import com.squareup.anvil.annotations.ContributesBinding
|
||||||
import io.element.android.features.ftue.api.state.FtueService
|
import io.element.android.features.ftue.api.state.FtueService
|
||||||
import io.element.android.features.preferences.impl.DefaultCacheService
|
import io.element.android.features.preferences.impl.DefaultCacheService
|
||||||
|
|
@ -43,7 +40,7 @@ class DefaultClearCacheUseCase @Inject constructor(
|
||||||
// Clear Matrix cache
|
// Clear Matrix cache
|
||||||
matrixClient.clearCache()
|
matrixClient.clearCache()
|
||||||
// Clear Coil cache
|
// Clear Coil cache
|
||||||
Coil.imageLoader(context).let {
|
SingletonImageLoader.get(context).let {
|
||||||
it.diskCache?.clear()
|
it.diskCache?.clear()
|
||||||
it.memoryCache?.clear()
|
it.memoryCache?.clear()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,8 @@ import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import coil.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import coil.request.ImageRequest
|
import coil3.request.ImageRequest
|
||||||
import io.element.android.features.rageshake.impl.R
|
import io.element.android.features.rageshake.impl.R
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.designsystem.components.async.AsyncActionView
|
import io.element.android.libraries.designsystem.components.async.AsyncActionView
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ datetime = "0.6.2"
|
||||||
serialization_json = "1.8.0"
|
serialization_json = "1.8.0"
|
||||||
|
|
||||||
#other
|
#other
|
||||||
coil = "2.7.0"
|
coil = "3.1.0"
|
||||||
showkase = "1.0.3"
|
showkase = "1.0.3"
|
||||||
appyx = "1.6.0"
|
appyx = "1.6.0"
|
||||||
sqldelight = "2.0.2"
|
sqldelight = "2.0.2"
|
||||||
|
|
@ -159,10 +159,11 @@ test_appyx_junit = { module = "com.bumble.appyx:testing-junit4", version.ref = "
|
||||||
test_composable_preview_scanner = "io.github.sergio-sastre.ComposablePreviewScanner:android:0.5.1"
|
test_composable_preview_scanner = "io.github.sergio-sastre.ComposablePreviewScanner:android:0.5.1"
|
||||||
|
|
||||||
# Others
|
# Others
|
||||||
coil = { module = "io.coil-kt:coil", version.ref = "coil" }
|
coil = { module = "io.coil-kt.coil3:coil", version.ref = "coil" }
|
||||||
coil_compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
|
coil_network_okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coil" }
|
||||||
coil_gif = { module = "io.coil-kt:coil-gif", version.ref = "coil" }
|
coil_compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" }
|
||||||
coil_test = { module = "io.coil-kt:coil-test", version.ref = "coil" }
|
coil_gif = { module = "io.coil-kt.coil3:coil-gif", version.ref = "coil" }
|
||||||
|
coil_test = { module = "io.coil-kt.coil3:coil-test", version.ref = "coil" }
|
||||||
compound = { module = "io.element.android:compound-android", version = "25.2.26" }
|
compound = { module = "io.element.android:compound-android", version = "25.2.26" }
|
||||||
datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "datetime" }
|
datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "datetime" }
|
||||||
serialization_json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization_json" }
|
serialization_json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization_json" }
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ package io.element.android.libraries.designsystem.components
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
import android.graphics.drawable.BitmapDrawable
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.text.TextPaint
|
import android.text.TextPaint
|
||||||
import androidx.annotation.FloatRange
|
import androidx.annotation.FloatRange
|
||||||
|
|
@ -85,9 +84,10 @@ import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.isSpecified
|
import androidx.compose.ui.unit.isSpecified
|
||||||
import androidx.compose.ui.unit.toOffset
|
import androidx.compose.ui.unit.toOffset
|
||||||
import androidx.compose.ui.unit.toSize
|
import androidx.compose.ui.unit.toSize
|
||||||
import coil.imageLoader
|
import coil3.SingletonImageLoader
|
||||||
import coil.request.DefaultRequestOptions
|
import coil3.request.ImageRequest
|
||||||
import coil.request.ImageRequest
|
import coil3.request.allowHardware
|
||||||
|
import coil3.toBitmap
|
||||||
import com.airbnb.android.showkase.annotation.ShowkaseComposable
|
import com.airbnb.android.showkase.annotation.ShowkaseComposable
|
||||||
import com.vanniktech.blurhash.BlurHash
|
import com.vanniktech.blurhash.BlurHash
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
|
|
@ -328,7 +328,7 @@ fun Modifier.avatarBloom(
|
||||||
ImageRequest.Builder(context)
|
ImageRequest.Builder(context)
|
||||||
.data(avatarData)
|
.data(avatarData)
|
||||||
// Allow cache and default dispatchers
|
// Allow cache and default dispatchers
|
||||||
.defaults(DefaultRequestOptions())
|
.defaults(ImageRequest.Defaults())
|
||||||
// Needed to be able to read pixels from the Bitmap for the hash
|
// Needed to be able to read pixels from the Bitmap for the hash
|
||||||
.allowHardware(false)
|
.allowHardware(false)
|
||||||
// Reduce size so it loads faster for large avatars
|
// Reduce size so it loads faster for large avatars
|
||||||
|
|
@ -340,9 +340,11 @@ fun Modifier.avatarBloom(
|
||||||
var blurHash by rememberSaveable(avatarData) { mutableStateOf<String?>(null) }
|
var blurHash by rememberSaveable(avatarData) { mutableStateOf<String?>(null) }
|
||||||
LaunchedEffect(avatarData) {
|
LaunchedEffect(avatarData) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
val drawable =
|
val bitmap = SingletonImageLoader.get(context)
|
||||||
context.imageLoader.execute(painterRequest).drawable ?: return@withContext
|
.execute(painterRequest)
|
||||||
val bitmap = (drawable as? BitmapDrawable)?.bitmap ?: return@withContext
|
.image
|
||||||
|
?.toBitmap()
|
||||||
|
?: return@withContext
|
||||||
blurHash = BlurHash.encode(
|
blurHash = BlurHash.encode(
|
||||||
bitmap = bitmap,
|
bitmap = bitmap,
|
||||||
componentX = BloomDefaults.HASH_COMPONENTS,
|
componentX = BloomDefaults.HASH_COMPONENTS,
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.SideEffect
|
import androidx.compose.runtime.SideEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
|
|
@ -26,10 +28,10 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import coil.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import coil.compose.AsyncImagePainter
|
import coil3.compose.AsyncImagePainter
|
||||||
import coil.compose.SubcomposeAsyncImage
|
import coil3.compose.SubcomposeAsyncImage
|
||||||
import coil.compose.SubcomposeAsyncImageContent
|
import coil3.compose.SubcomposeAsyncImageContent
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.libraries.designsystem.colors.AvatarColorsProvider
|
import io.element.android.libraries.designsystem.colors.AvatarColorsProvider
|
||||||
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
||||||
|
|
@ -89,7 +91,8 @@ private fun ImageAvatar(
|
||||||
contentScale = ContentScale.Crop,
|
contentScale = ContentScale.Crop,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
) {
|
) {
|
||||||
when (val state = painter.state) {
|
val collectedState by painter.state.collectAsState()
|
||||||
|
when (val state = collectedState) {
|
||||||
is AsyncImagePainter.State.Success -> SubcomposeAsyncImageContent()
|
is AsyncImagePainter.State.Success -> SubcomposeAsyncImageContent()
|
||||||
is AsyncImagePainter.State.Error -> {
|
is AsyncImagePainter.State.Error -> {
|
||||||
SideEffect {
|
SideEffect {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import coil.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BlurHashAsyncImage(
|
fun BlurHashAsyncImage(
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,14 @@ package io.element.android.libraries.designsystem.preview
|
||||||
|
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
|
import coil3.annotation.ExperimentalCoilApi
|
||||||
|
import coil3.compose.AsyncImagePreviewHandler
|
||||||
|
import coil3.compose.LocalAsyncImagePreviewHandler
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.libraries.designsystem.theme.components.Surface
|
import io.element.android.libraries.designsystem.theme.components.Surface
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoilApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
@Suppress("ModifierMissing")
|
@Suppress("ModifierMissing")
|
||||||
fun ElementPreview(
|
fun ElementPreview(
|
||||||
|
|
@ -19,12 +24,14 @@ fun ElementPreview(
|
||||||
showBackground: Boolean = true,
|
showBackground: Boolean = true,
|
||||||
content: @Composable () -> Unit
|
content: @Composable () -> Unit
|
||||||
) {
|
) {
|
||||||
ElementTheme(darkTheme = darkTheme) {
|
CompositionLocalProvider(LocalAsyncImagePreviewHandler provides AsyncImagePreviewHandler { null }) {
|
||||||
if (showBackground) {
|
ElementTheme(darkTheme = darkTheme) {
|
||||||
// If we have a proper contentColor applied we need a Surface instead of a Box
|
if (showBackground) {
|
||||||
Surface(content = content)
|
// If we have a proper contentColor applied we need a Surface instead of a Box
|
||||||
} else {
|
Surface(content = content)
|
||||||
content()
|
} else {
|
||||||
|
content()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ dependencies {
|
||||||
implementation(projects.libraries.testtags)
|
implementation(projects.libraries.testtags)
|
||||||
implementation(libs.coil.compose)
|
implementation(libs.coil.compose)
|
||||||
implementation(libs.coil.gif)
|
implementation(libs.coil.gif)
|
||||||
|
implementation(libs.coil.network.okhttp)
|
||||||
implementation(libs.jsoup)
|
implementation(libs.jsoup)
|
||||||
|
|
||||||
testImplementation(libs.coroutines.test)
|
testImplementation(libs.coroutines.test)
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,8 @@ import androidx.compose.ui.graphics.painter.ColorPainter
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import coil.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import coil.request.ImageRequest
|
import coil3.request.ImageRequest
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.ui.media
|
package io.element.android.libraries.matrix.ui.media
|
||||||
|
|
||||||
import coil.ImageLoader
|
import coil3.ImageLoader
|
||||||
import coil.fetch.Fetcher
|
import coil3.fetch.Fetcher
|
||||||
import coil.request.Options
|
import coil3.request.Options
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
|
|
||||||
|
|
@ -24,7 +24,6 @@ internal class AvatarDataFetcherFactory(
|
||||||
return CoilMediaFetcher(
|
return CoilMediaFetcher(
|
||||||
mediaLoader = client.mediaLoader,
|
mediaLoader = client.mediaLoader,
|
||||||
mediaData = data.toMediaRequestData(),
|
mediaData = data.toMediaRequestData(),
|
||||||
options = options
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,16 @@
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.ui.media
|
package io.element.android.libraries.matrix.ui.media
|
||||||
|
|
||||||
import coil.decode.DataSource
|
import coil3.decode.DataSource
|
||||||
import coil.decode.ImageSource
|
import coil3.decode.ImageSource
|
||||||
import coil.fetch.FetchResult
|
import coil3.fetch.FetchResult
|
||||||
import coil.fetch.Fetcher
|
import coil3.fetch.Fetcher
|
||||||
import coil.fetch.SourceResult
|
import coil3.fetch.SourceFetchResult
|
||||||
import coil.request.Options
|
|
||||||
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
|
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
|
||||||
import io.element.android.libraries.matrix.api.media.MediaSource
|
import io.element.android.libraries.matrix.api.media.MediaSource
|
||||||
import io.element.android.libraries.matrix.api.media.toFile
|
import io.element.android.libraries.matrix.api.media.toFile
|
||||||
import okio.Buffer
|
import okio.Buffer
|
||||||
|
import okio.FileSystem
|
||||||
import okio.Path.Companion.toOkioPath
|
import okio.Path.Companion.toOkioPath
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
|
|
@ -24,7 +24,6 @@ import java.nio.ByteBuffer
|
||||||
internal class CoilMediaFetcher(
|
internal class CoilMediaFetcher(
|
||||||
private val mediaLoader: MatrixMediaLoader,
|
private val mediaLoader: MatrixMediaLoader,
|
||||||
private val mediaData: MediaRequestData,
|
private val mediaData: MediaRequestData,
|
||||||
private val options: Options
|
|
||||||
) : Fetcher {
|
) : Fetcher {
|
||||||
override suspend fun fetch(): FetchResult? {
|
override suspend fun fetch(): FetchResult? {
|
||||||
if (mediaData.source == null) {
|
if (mediaData.source == null) {
|
||||||
|
|
@ -32,8 +31,8 @@ internal class CoilMediaFetcher(
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return when (mediaData.kind) {
|
return when (mediaData.kind) {
|
||||||
is MediaRequestData.Kind.Content -> fetchContent(mediaData.source, options)
|
is MediaRequestData.Kind.Content -> fetchContent(mediaData.source)
|
||||||
is MediaRequestData.Kind.Thumbnail -> fetchThumbnail(mediaData.source, mediaData.kind, options)
|
is MediaRequestData.Kind.Thumbnail -> fetchThumbnail(mediaData.source, mediaData.kind)
|
||||||
is MediaRequestData.Kind.File -> fetchFile(mediaData.source, mediaData.kind)
|
is MediaRequestData.Kind.File -> fetchFile(mediaData.source, mediaData.kind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -47,8 +46,12 @@ internal class CoilMediaFetcher(
|
||||||
return mediaLoader.downloadMediaFile(mediaSource, kind.mimeType, kind.fileName)
|
return mediaLoader.downloadMediaFile(mediaSource, kind.mimeType, kind.fileName)
|
||||||
.map { mediaFile ->
|
.map { mediaFile ->
|
||||||
val file = mediaFile.toFile()
|
val file = mediaFile.toFile()
|
||||||
SourceResult(
|
SourceFetchResult(
|
||||||
source = ImageSource(file = file.toOkioPath(), closeable = mediaFile),
|
source = ImageSource(
|
||||||
|
file = file.toOkioPath(),
|
||||||
|
fileSystem = FileSystem.SYSTEM,
|
||||||
|
closeable = mediaFile,
|
||||||
|
),
|
||||||
mimeType = null,
|
mimeType = null,
|
||||||
dataSource = DataSource.DISK
|
dataSource = DataSource.DISK
|
||||||
)
|
)
|
||||||
|
|
@ -59,37 +62,40 @@ internal class CoilMediaFetcher(
|
||||||
.getOrNull()
|
.getOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun fetchContent(mediaSource: MediaSource, options: Options): FetchResult? {
|
private suspend fun fetchContent(mediaSource: MediaSource): FetchResult? {
|
||||||
return mediaLoader.loadMediaContent(
|
return mediaLoader.loadMediaContent(
|
||||||
source = mediaSource,
|
source = mediaSource,
|
||||||
).map { byteArray ->
|
).map { byteArray ->
|
||||||
byteArray.asSourceResult(options)
|
byteArray.asSourceResult()
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
Timber.e(it)
|
Timber.e(it)
|
||||||
}.getOrNull()
|
}.getOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun fetchThumbnail(mediaSource: MediaSource, kind: MediaRequestData.Kind.Thumbnail, options: Options): FetchResult? {
|
private suspend fun fetchThumbnail(mediaSource: MediaSource, kind: MediaRequestData.Kind.Thumbnail): FetchResult? {
|
||||||
return mediaLoader.loadMediaThumbnail(
|
return mediaLoader.loadMediaThumbnail(
|
||||||
source = mediaSource,
|
source = mediaSource,
|
||||||
width = kind.width,
|
width = kind.width,
|
||||||
height = kind.height,
|
height = kind.height,
|
||||||
).map { byteArray ->
|
).map { byteArray ->
|
||||||
byteArray.asSourceResult(options)
|
byteArray.asSourceResult()
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
Timber.e(it)
|
Timber.e(it)
|
||||||
}.getOrNull()
|
}.getOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ByteArray.asSourceResult(options: Options): SourceResult {
|
private fun ByteArray.asSourceResult(): SourceFetchResult {
|
||||||
val byteBuffer = ByteBuffer.wrap(this)
|
val byteBuffer = ByteBuffer.wrap(this)
|
||||||
val bufferedSource = try {
|
val bufferedSource = try {
|
||||||
Buffer().apply { write(byteBuffer) }
|
Buffer().apply { write(byteBuffer) }
|
||||||
} finally {
|
} finally {
|
||||||
byteBuffer.position(0)
|
byteBuffer.position(0)
|
||||||
}
|
}
|
||||||
return SourceResult(
|
return SourceFetchResult(
|
||||||
source = ImageSource(bufferedSource, options.context),
|
source = ImageSource(
|
||||||
|
source = bufferedSource,
|
||||||
|
fileSystem = FileSystem.SYSTEM,
|
||||||
|
),
|
||||||
mimeType = null,
|
mimeType = null,
|
||||||
dataSource = DataSource.MEMORY
|
dataSource = DataSource.MEMORY
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,10 @@ package io.element.android.libraries.matrix.ui.media
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import coil.ImageLoader
|
import coil3.ImageLoader
|
||||||
import coil.ImageLoaderFactory
|
import coil3.gif.AnimatedImageDecoder
|
||||||
import coil.decode.GifDecoder
|
import coil3.gif.GifDecoder
|
||||||
import coil.decode.ImageDecoderDecoder
|
import coil3.network.okhttp.OkHttpNetworkFetcherFactory
|
||||||
import com.squareup.anvil.annotations.ContributesBinding
|
import com.squareup.anvil.annotations.ContributesBinding
|
||||||
import io.element.android.libraries.di.AppScope
|
import io.element.android.libraries.di.AppScope
|
||||||
import io.element.android.libraries.di.ApplicationContext
|
import io.element.android.libraries.di.ApplicationContext
|
||||||
|
|
@ -31,13 +31,19 @@ class DefaultLoggedInImageLoaderFactory @Inject constructor(
|
||||||
private val okHttpClient: Provider<OkHttpClient>,
|
private val okHttpClient: Provider<OkHttpClient>,
|
||||||
) : LoggedInImageLoaderFactory {
|
) : LoggedInImageLoaderFactory {
|
||||||
override fun newImageLoader(matrixClient: MatrixClient): ImageLoader {
|
override fun newImageLoader(matrixClient: MatrixClient): ImageLoader {
|
||||||
return ImageLoader
|
return ImageLoader.Builder(context)
|
||||||
.Builder(context)
|
|
||||||
.okHttpClient { okHttpClient.get() }
|
|
||||||
.components {
|
.components {
|
||||||
|
add(
|
||||||
|
OkHttpNetworkFetcherFactory(
|
||||||
|
callFactory = {
|
||||||
|
// Use newBuilder, see https://coil-kt.github.io/coil/network/#using-a-custom-okhttpclient
|
||||||
|
okHttpClient.get().newBuilder().build()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
// Add gif support
|
// Add gif support
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
add(ImageDecoderDecoder.Factory())
|
add(AnimatedImageDecoder.Factory())
|
||||||
} else {
|
} else {
|
||||||
add(GifDecoder.Factory())
|
add(GifDecoder.Factory())
|
||||||
}
|
}
|
||||||
|
|
@ -53,11 +59,19 @@ class DefaultLoggedInImageLoaderFactory @Inject constructor(
|
||||||
class NotLoggedInImageLoaderFactory @Inject constructor(
|
class NotLoggedInImageLoaderFactory @Inject constructor(
|
||||||
@ApplicationContext private val context: Context,
|
@ApplicationContext private val context: Context,
|
||||||
private val okHttpClient: Provider<OkHttpClient>,
|
private val okHttpClient: Provider<OkHttpClient>,
|
||||||
) : ImageLoaderFactory {
|
) {
|
||||||
override fun newImageLoader(): ImageLoader {
|
fun newImageLoader(): ImageLoader {
|
||||||
return ImageLoader
|
return ImageLoader.Builder(context)
|
||||||
.Builder(context)
|
.components {
|
||||||
.okHttpClient { okHttpClient.get() }
|
add(
|
||||||
|
OkHttpNetworkFetcherFactory(
|
||||||
|
callFactory = {
|
||||||
|
// Use newBuilder, see https://coil-kt.github.io/coil/network/#using-a-custom-okhttpclient
|
||||||
|
okHttpClient.get().newBuilder().build()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.ui.media
|
package io.element.android.libraries.matrix.ui.media
|
||||||
|
|
||||||
import coil.ImageLoader
|
import coil3.ImageLoader
|
||||||
import com.squareup.anvil.annotations.ContributesBinding
|
import com.squareup.anvil.annotations.ContributesBinding
|
||||||
import io.element.android.libraries.di.AppScope
|
import io.element.android.libraries.di.AppScope
|
||||||
import io.element.android.libraries.di.SingleIn
|
import io.element.android.libraries.di.SingleIn
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ package io.element.android.libraries.matrix.ui.media
|
||||||
import io.element.android.libraries.matrix.api.media.MediaSource
|
import io.element.android.libraries.matrix.api.media.MediaSource
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Can be use with [coil.compose.AsyncImage] to load a [MediaSource].
|
* Can be use with [coil3.compose.AsyncImage] to load a [MediaSource].
|
||||||
* This will go internally through our [CoilMediaFetcher].
|
* This will go internally through our [CoilMediaFetcher].
|
||||||
*
|
*
|
||||||
* Example of usage:
|
* Example of usage:
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.ui.media
|
package io.element.android.libraries.matrix.ui.media
|
||||||
|
|
||||||
import coil.ImageLoader
|
import coil3.ImageLoader
|
||||||
import coil.fetch.Fetcher
|
import coil3.fetch.Fetcher
|
||||||
import coil.request.Options
|
import coil3.request.Options
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
|
|
||||||
internal class MediaRequestDataFetcherFactory(
|
internal class MediaRequestDataFetcherFactory(
|
||||||
|
|
@ -23,7 +23,6 @@ internal class MediaRequestDataFetcherFactory(
|
||||||
return CoilMediaFetcher(
|
return CoilMediaFetcher(
|
||||||
mediaLoader = client.mediaLoader,
|
mediaLoader = client.mediaLoader,
|
||||||
mediaData = data,
|
mediaData = data,
|
||||||
options = options
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.ui.media
|
package io.element.android.libraries.matrix.ui.media
|
||||||
|
|
||||||
import coil.key.Keyer
|
import coil3.key.Keyer
|
||||||
import coil.request.Options
|
import coil3.request.Options
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||||
|
|
||||||
internal class AvatarDataKeyer : Keyer<AvatarData> {
|
internal class AvatarDataKeyer : Keyer<AvatarData> {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
package io.element.android.libraries.matrix.ui.media
|
package io.element.android.libraries.matrix.ui.media
|
||||||
|
|
||||||
import androidx.test.platform.app.InstrumentationRegistry
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
import coil.ImageLoader
|
import coil3.ImageLoader
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.ui.media
|
package io.element.android.libraries.matrix.ui.media
|
||||||
|
|
||||||
import coil.ImageLoader
|
import coil3.ImageLoader
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
|
|
||||||
class FakeLoggedInImageLoaderFactory(
|
class FakeLoggedInImageLoaderFactory(
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import coil.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||||
import io.element.android.libraries.designsystem.components.BigIcon
|
import io.element.android.libraries.designsystem.components.BigIcon
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,8 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.LocalInspectionMode
|
import androidx.compose.ui.platform.LocalInspectionMode
|
||||||
import coil.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import coil.compose.AsyncImagePainter
|
import coil3.compose.AsyncImagePainter
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,8 @@ import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.LocalInspectionMode
|
import androidx.compose.ui.platform.LocalInspectionMode
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import coil.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import coil.compose.AsyncImagePainter
|
import coil3.compose.AsyncImagePainter
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import coil.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ package io.element.android.libraries.push.api.notifications
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import androidx.core.graphics.drawable.IconCompat
|
import androidx.core.graphics.drawable.IconCompat
|
||||||
import coil.ImageLoader
|
import coil3.ImageLoader
|
||||||
|
|
||||||
interface NotificationBitmapLoader {
|
interface NotificationBitmapLoader {
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,11 @@ import android.content.Context
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.core.graphics.drawable.IconCompat
|
import androidx.core.graphics.drawable.IconCompat
|
||||||
import androidx.core.graphics.drawable.toBitmap
|
import coil3.ImageLoader
|
||||||
import coil.ImageLoader
|
import coil3.request.ImageRequest
|
||||||
import coil.request.ImageRequest
|
import coil3.request.transformations
|
||||||
import coil.transform.CircleCropTransformation
|
import coil3.toBitmap
|
||||||
|
import coil3.transform.CircleCropTransformation
|
||||||
import com.squareup.anvil.annotations.ContributesBinding
|
import com.squareup.anvil.annotations.ContributesBinding
|
||||||
import io.element.android.libraries.di.AppScope
|
import io.element.android.libraries.di.AppScope
|
||||||
import io.element.android.libraries.di.ApplicationContext
|
import io.element.android.libraries.di.ApplicationContext
|
||||||
|
|
@ -50,7 +51,7 @@ class DefaultNotificationBitmapLoader @Inject constructor(
|
||||||
.transformations(CircleCropTransformation())
|
.transformations(CircleCropTransformation())
|
||||||
.build()
|
.build()
|
||||||
val result = imageLoader.execute(imageRequest)
|
val result = imageLoader.execute(imageRequest)
|
||||||
result.drawable?.toBitmap()
|
result.image?.toBitmap()
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
Timber.e(e, "Unable to load room bitmap")
|
Timber.e(e, "Unable to load room bitmap")
|
||||||
null
|
null
|
||||||
|
|
@ -78,7 +79,7 @@ class DefaultNotificationBitmapLoader @Inject constructor(
|
||||||
.transformations(CircleCropTransformation())
|
.transformations(CircleCropTransformation())
|
||||||
.build()
|
.build()
|
||||||
val result = imageLoader.execute(imageRequest)
|
val result = imageLoader.execute(imageRequest)
|
||||||
val bitmap = result.drawable?.toBitmap()
|
val bitmap = result.image?.toBitmap()
|
||||||
return bitmap?.let { IconCompat.createWithBitmap(it) }
|
return bitmap?.let { IconCompat.createWithBitmap(it) }
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
Timber.e(e, "Unable to load user bitmap")
|
Timber.e(e, "Unable to load user bitmap")
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import android.graphics.Typeface
|
||||||
import android.text.style.StyleSpan
|
import android.text.style.StyleSpan
|
||||||
import androidx.core.text.buildSpannedString
|
import androidx.core.text.buildSpannedString
|
||||||
import androidx.core.text.inSpans
|
import androidx.core.text.inSpans
|
||||||
import coil.ImageLoader
|
import coil3.ImageLoader
|
||||||
import com.squareup.anvil.annotations.ContributesBinding
|
import com.squareup.anvil.annotations.ContributesBinding
|
||||||
import io.element.android.libraries.di.AppScope
|
import io.element.android.libraries.di.AppScope
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
package io.element.android.libraries.push.impl.notifications
|
package io.element.android.libraries.push.impl.notifications
|
||||||
|
|
||||||
import coil.ImageLoader
|
import coil3.ImageLoader
|
||||||
import io.element.android.libraries.core.log.logger.LoggerTag
|
import io.element.android.libraries.core.log.logger.LoggerTag
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
import io.element.android.libraries.push.api.notifications.NotificationIdProvider
|
import io.element.android.libraries.push.api.notifications.NotificationIdProvider
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ package io.element.android.libraries.push.impl.notifications
|
||||||
|
|
||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import coil.ImageLoader
|
import coil3.ImageLoader
|
||||||
import com.squareup.anvil.annotations.ContributesBinding
|
import com.squareup.anvil.annotations.ContributesBinding
|
||||||
import io.element.android.libraries.di.AppScope
|
import io.element.android.libraries.di.AppScope
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationCompat.MessagingStyle
|
import androidx.core.app.NotificationCompat.MessagingStyle
|
||||||
import androidx.core.app.Person
|
import androidx.core.app.Person
|
||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import coil.ImageLoader
|
import coil3.ImageLoader
|
||||||
import com.squareup.anvil.annotations.ContributesBinding
|
import com.squareup.anvil.annotations.ContributesBinding
|
||||||
import io.element.android.appconfig.NotificationConfig
|
import io.element.android.appconfig.NotificationConfig
|
||||||
import io.element.android.libraries.core.meta.BuildMeta
|
import io.element.android.libraries.core.meta.BuildMeta
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ package io.element.android.libraries.push.impl.notifications.fake
|
||||||
|
|
||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import coil.ImageLoader
|
import coil3.ImageLoader
|
||||||
import io.element.android.libraries.matrix.api.core.ThreadId
|
import io.element.android.libraries.matrix.api.core.ThreadId
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
import io.element.android.libraries.push.impl.notifications.RoomEventGroupInfo
|
import io.element.android.libraries.push.impl.notifications.RoomEventGroupInfo
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
package io.element.android.libraries.push.impl.notifications.fake
|
package io.element.android.libraries.push.impl.notifications.fake
|
||||||
|
|
||||||
import coil.ImageLoader
|
import coil3.ImageLoader
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
import io.element.android.libraries.push.impl.notifications.NotificationDataFactory
|
import io.element.android.libraries.push.impl.notifications.NotificationDataFactory
|
||||||
import io.element.android.libraries.push.impl.notifications.OneShotNotification
|
import io.element.android.libraries.push.impl.notifications.OneShotNotification
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
package io.element.android.libraries.push.impl.notifications.fake
|
package io.element.android.libraries.push.impl.notifications.fake
|
||||||
|
|
||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
import coil.ImageLoader
|
import coil3.ImageLoader
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
import io.element.android.libraries.push.impl.notifications.RoomGroupMessageCreator
|
import io.element.android.libraries.push.impl.notifications.RoomGroupMessageCreator
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,11 @@ package io.element.android.libraries.push.test.notifications
|
||||||
|
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.drawable.ColorDrawable
|
import android.graphics.drawable.ColorDrawable
|
||||||
import coil.ImageLoader
|
import coil3.ImageLoader
|
||||||
import coil.annotation.ExperimentalCoilApi
|
import coil3.test.FakeImageLoaderEngine
|
||||||
import coil.test.FakeImageLoaderEngine
|
import coil3.test.intercept
|
||||||
import org.robolectric.RuntimeEnvironment
|
import org.robolectric.RuntimeEnvironment
|
||||||
|
|
||||||
@OptIn(ExperimentalCoilApi::class)
|
|
||||||
class FakeImageLoader {
|
class FakeImageLoader {
|
||||||
private val coilRequests = mutableListOf<Any>()
|
private val coilRequests = mutableListOf<Any>()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
package io.element.android.libraries.push.test.notifications
|
package io.element.android.libraries.push.test.notifications
|
||||||
|
|
||||||
import coil.ImageLoader
|
import coil3.ImageLoader
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
import io.element.android.libraries.matrix.ui.media.ImageLoaderHolder
|
import io.element.android.libraries.matrix.ui.media.ImageLoaderHolder
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ package io.element.android.libraries.push.test.notifications.push
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import androidx.core.graphics.drawable.IconCompat
|
import androidx.core.graphics.drawable.IconCompat
|
||||||
import coil.ImageLoader
|
import coil3.ImageLoader
|
||||||
import io.element.android.libraries.push.api.notifications.NotificationBitmapLoader
|
import io.element.android.libraries.push.api.notifications.NotificationBitmapLoader
|
||||||
|
|
||||||
class FakeNotificationBitmapLoader(
|
class FakeNotificationBitmapLoader(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue