mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-08-06 05:14:50 +01:00
Add support for non-video gifs in v3 media send flow.
This commit is contained in:
@@ -244,7 +244,7 @@ object MediaSendV3Repository : MediaSendRepository {
|
||||
val legacyState: Any? = when (state) {
|
||||
is EditorState.Image -> ImageEditorFragment.Data().apply { writeModel(state.model) }
|
||||
is EditorState.VideoTrim -> state.videoTrimData
|
||||
EditorState.VideoGif -> null
|
||||
EditorState.VideoGif, EditorState.Gif -> null
|
||||
}
|
||||
legacyState?.let { uri to it }
|
||||
}.toMap()
|
||||
|
||||
@@ -100,6 +100,13 @@ sealed interface EditorState : Parcelable {
|
||||
@Parcelize
|
||||
data object VideoGif : EditorState
|
||||
|
||||
/**
|
||||
* Animated gif state. Gifs are played back as-is rather than being routed through the image editor, so there is
|
||||
* nothing to track beyond the type itself.
|
||||
*/
|
||||
@Parcelize
|
||||
data object Gif : EditorState
|
||||
|
||||
/**
|
||||
* Image editor state.
|
||||
*/
|
||||
|
||||
@@ -456,9 +456,14 @@ class MediaSendViewModel(
|
||||
.filter { isGifVideo(it) }
|
||||
.associate { it.uri to EditorState.VideoGif }
|
||||
|
||||
val initializedGifEditorStates = filterResult.filteredMedia
|
||||
.filterNot { snapshot.editorStateMap.containsKey(it.uri) }
|
||||
.filter { ContentTypeUtil.isGif(it.contentType) }
|
||||
.associate { it.uri to EditorState.Gif }
|
||||
|
||||
val initializedImageEditorStates = filterResult.filteredMedia
|
||||
.filterNot { snapshot.editorStateMap.containsKey(it.uri) }
|
||||
.filter { ContentTypeUtil.isImageType(it.contentType) }
|
||||
.filter { ContentTypeUtil.isImageAndNotGif(it.contentType) }
|
||||
.associate { image ->
|
||||
// TODO - this should likely be in a repository?
|
||||
val editorModel = EditorModel.create(0x0)
|
||||
@@ -495,7 +500,7 @@ class MediaSendViewModel(
|
||||
copy(
|
||||
selectedMedia = filterResult.filteredMedia,
|
||||
focusedMedia = newFocus,
|
||||
editorStateMap = editorStateMap + initializedVideoEditorStates + initializedVideoGifEditorStates + initializedImageEditorStates,
|
||||
editorStateMap = editorStateMap + initializedVideoEditorStates + initializedVideoGifEditorStates + initializedGifEditorStates + initializedImageEditorStates,
|
||||
// Re-bind to the populated instance by URI: population fills in a video's 0x0 dimensions, producing a new
|
||||
// Media that no longer equals the pre-population capture, which would otherwise leak past equality-based
|
||||
// removal on back. Cleared once more than the capture is selected.
|
||||
|
||||
@@ -30,6 +30,7 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalInspectionMode
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.fragment.compose.AndroidFragment
|
||||
@@ -40,6 +41,9 @@ import org.signal.core.ui.WindowBreakpoint
|
||||
import org.signal.core.ui.compose.AllDevicePreviews
|
||||
import org.signal.core.ui.compose.Previews
|
||||
import org.signal.core.ui.rememberWindowBreakpoint
|
||||
import org.signal.glide.compose.GlideImage
|
||||
import org.signal.glide.compose.GlideImageScaleType
|
||||
import org.signal.glide.decryptableuri.DecryptableUri
|
||||
import org.signal.imageeditor.core.model.EditorModel
|
||||
import org.signal.mediasend.EditorState
|
||||
import org.signal.mediasend.MediaSendDependencies
|
||||
@@ -117,6 +121,17 @@ fun MediaEditScreen(
|
||||
)
|
||||
}
|
||||
|
||||
EditorState.Gif -> {
|
||||
if (!LocalInspectionMode.current) {
|
||||
GlideImage(
|
||||
model = DecryptableUri(uri),
|
||||
scaleType = GlideImageScaleType.FIT_CENTER,
|
||||
contentScale = ContentScale.Fit,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is EditorState.VideoTrim, EditorState.VideoGif -> {
|
||||
val media = state.selectedMedia[index]
|
||||
var videoEditorFragment by remember(media.uri) { mutableStateOf<VideoEditorFragment?>(null) }
|
||||
@@ -238,7 +253,7 @@ fun MediaEditScreen(
|
||||
)
|
||||
}
|
||||
|
||||
EditorState.VideoGif, null -> Unit
|
||||
EditorState.VideoGif, EditorState.Gif, null -> Unit
|
||||
}
|
||||
|
||||
AddAMessageRow(
|
||||
|
||||
@@ -31,6 +31,9 @@ import org.signal.glide.apng.ApngOptions
|
||||
|
||||
/**
|
||||
* Our very own GlideImage. The GlideImage composable provided by the bumptech library is not suitable because it was is using our encrypted cache decoder/encoder.
|
||||
*
|
||||
* @param contentScale How the loaded drawable is scaled into the available space. Ignored when [enableApngAnimation] is
|
||||
* set, as that path hands scaling to the underlying [ImageView] via [scaleType].
|
||||
*/
|
||||
@Composable
|
||||
fun <T> GlideImage(
|
||||
@@ -42,6 +45,7 @@ fun <T> GlideImage(
|
||||
error: Drawable? = fallback,
|
||||
transition: TransitionOptions<*, Drawable>? = null,
|
||||
diskCacheStrategy: DiskCacheStrategy = DiskCacheStrategy.ALL,
|
||||
contentScale: ContentScale = ContentScale.Crop,
|
||||
enableApngAnimation: Boolean = false
|
||||
) {
|
||||
if (enableApngAnimation) {
|
||||
@@ -82,6 +86,7 @@ fun <T> GlideImage(
|
||||
error = error,
|
||||
transition = transition,
|
||||
diskCacheStrategy = diskCacheStrategy,
|
||||
contentScale = contentScale,
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
@@ -96,7 +101,8 @@ private fun <T> GlideImage(
|
||||
fallback: Drawable? = null,
|
||||
error: Drawable? = fallback,
|
||||
transition: TransitionOptions<*, Drawable>? = null,
|
||||
diskCacheStrategy: DiskCacheStrategy = DiskCacheStrategy.ALL
|
||||
diskCacheStrategy: DiskCacheStrategy = DiskCacheStrategy.ALL,
|
||||
contentScale: ContentScale = ContentScale.Crop
|
||||
) {
|
||||
var drawable by remember {
|
||||
mutableStateOf<Drawable?>(null)
|
||||
@@ -148,7 +154,7 @@ private fun <T> GlideImage(
|
||||
Image(
|
||||
painter = rememberDrawablePainter(drawable),
|
||||
contentDescription = null,
|
||||
contentScale = if (model == null) ContentScale.Inside else ContentScale.Crop,
|
||||
contentScale = if (model == null) ContentScale.Inside else contentScale,
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user