diff --git a/app/build.gradle.kts b/app/build.gradle.kts index da0e37b591..7130b5693b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -406,6 +406,7 @@ dependencies { implementation(libs.glide.compose) implementation(libs.coil.compose) implementation(libs.coil.gif) + implementation(libs.coil.video) implementation(libs.android.image.cropper) implementation(libs.subsampling.scale.image.view) { exclude(group = "com.android.support", module = "support-annotations") diff --git a/app/src/main/java/org/thoughtcrime/securesms/coil/CoilModule.kt b/app/src/main/java/org/thoughtcrime/securesms/coil/CoilModule.kt index 99089c45cb..dc64ac9cc3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/coil/CoilModule.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/coil/CoilModule.kt @@ -9,6 +9,7 @@ import coil3.gif.AnimatedImageDecoder import coil3.gif.GifDecoder import coil3.memory.MemoryCache import coil3.request.crossfade +import coil3.video.VideoFrameDecoder import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -50,6 +51,7 @@ class CoilModule { } add(BitmapFactoryDecoder.Factory()) + add(VideoFrameDecoder.Factory()) } .build() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendViewModel.kt index 32336fbd5b..3115302e1b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendViewModel.kt @@ -69,9 +69,6 @@ class MediaSendViewModel @Inject constructor( private val positionLiveData: LiveData = uiState.map { it.position }.asLiveData() - private val foldersLiveData: LiveData> = - uiState.map { it.folders }.asLiveData() - private val countButtonStateLiveData: LiveData = uiState.map { CountButtonState(it.count, it.countVisibility) } .asLiveData() @@ -378,13 +375,6 @@ class MediaSendViewModel @Inject constructor( return uiState.map { it.bucketMedia }.asLiveData() } - fun getFolders(): LiveData> { - repository.getFolders(context) { value -> - _uiState.update { it.copy(folders = value) } - } - return foldersLiveData - } - fun getCountButtonState(): LiveData { return countButtonStateLiveData } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/compose/Components.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/compose/Components.kt index 187cfcaeaa..f3627e211b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/compose/Components.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/compose/Components.kt @@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -20,6 +21,7 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -40,6 +42,7 @@ import androidx.compose.ui.unit.dp import androidx.core.net.toUri import coil3.compose.AsyncImage import coil3.request.ImageRequest +import coil3.video.VideoFrameDecoder import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi import network.loki.messenger.R import org.thoughtcrime.securesms.mediasend.Media @@ -58,6 +61,24 @@ fun MediaFolderCell( qaTag : String, onClick: () -> Unit ) { + val context = LocalContext.current + val thumbnailMimeType = thumbnailUri?.let { MediaUtil.getMimeType(context, it) } + + val videoDecoderFactory = remember { VideoFrameDecoder.Factory() } + + // our URI does not have a file extension so we need to check for the mimetype + // then explicitly set the decoder for the request + val folderThumbnailRequest = remember(context, thumbnailUri) { + ImageRequest.Builder(context) + .data(thumbnailUri) + .apply { + if (MediaUtil.isVideoType(thumbnailMimeType)) { + decoderFactory(videoDecoderFactory) + } + } + .build() + } + Box( modifier = Modifier .fillMaxWidth() @@ -68,9 +89,7 @@ fun MediaFolderCell( AsyncImage( modifier = Modifier.fillMaxWidth(), contentScale = ContentScale.Crop, - model = ImageRequest.Builder(LocalContext.current) - .data(thumbnailUri) - .build(), + model = folderThumbnailRequest, contentDescription = null, ) @@ -142,6 +161,21 @@ fun MediaPickerItemCell( showSelectionOn: Boolean = false, canLongPress: Boolean = true ) { + val context = LocalContext.current + + val videoDecoderFactory = remember { VideoFrameDecoder.Factory() } + + val mediaRequest = remember(context, media.uri, media.mimeType) { + ImageRequest.Builder(context) + .data(media.uri) + .apply { + if (MediaUtil.isVideoType(media.mimeType)) { + decoderFactory(videoDecoderFactory) + } + } + .build() + } + Box( modifier = modifier .aspectRatio(1f) @@ -167,9 +201,7 @@ fun MediaPickerItemCell( AsyncImage( modifier = Modifier.fillMaxSize(), contentScale = ContentScale.Crop, - model = ImageRequest.Builder(LocalContext.current) - .data(media.uri) - .build(), + model = mediaRequest, contentDescription = null, ) @@ -180,13 +212,14 @@ fun MediaPickerItemCell( .align(Alignment.Center) .size(36.dp) .clip(CircleShape) - .background(Color.White), + .background(Color.White) + .padding(start = LocalDimensions.current.xxxsSpacing), contentAlignment = Alignment.Center ) { Image( painter = painterResource(R.drawable.triangle_right), contentDescription = null, - modifier = Modifier.size(LocalDimensions.current.iconMedium), + modifier = Modifier.size(LocalDimensions.current.iconXSmall), colorFilter = ColorFilter.tint(LocalColors.current.accent) // match @color/core_blue-ish ) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b68f4a8a16..1ad3d5d6ab 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -173,6 +173,7 @@ google-play-review-ktx = { module = "com.google.android.play:review-ktx", versio sqlite-web-viewer = { module = "io.github.simophin:sqlite-web-viewer", version = "0.2.0" } coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coilVersion" } coil-gif = { module = "io.coil-kt.coil3:coil-gif", version.ref = "coilVersion" } +coil-video = {module = "io.coil-kt.coil3:coil-video", version.ref = "coilVersion" } android-billing = { module = "com.android.billingclient:billing", version.ref = "billingVersion" } android-billing-ktx = { module = "com.android.billingclient:billing-ktx", version.ref = "billingVersion" } mockk = { module = "io.mockk:mockk", version = "1.14.9" }