Merge pull request #1774 from vector-im/feature/bma/imageThumbnail

Improve rendering of `AttachmentThumbnail` when thumbnailSource is not provided
This commit is contained in:
Benoit Marty 2023-11-09 14:30:29 +01:00 committed by GitHub
commit cba5b1cfd3
50 changed files with 151 additions and 12 deletions

View file

@ -311,7 +311,7 @@ class MessagesPresenter @AssistedInject constructor(
val textContent = messageSummaryFormatter.format(targetEvent) val textContent = messageSummaryFormatter.format(targetEvent)
val attachmentThumbnailInfo = when (targetEvent.content) { val attachmentThumbnailInfo = when (targetEvent.content) {
is TimelineItemImageContent -> AttachmentThumbnailInfo( is TimelineItemImageContent -> AttachmentThumbnailInfo(
thumbnailSource = targetEvent.content.thumbnailSource, thumbnailSource = targetEvent.content.thumbnailSource ?: targetEvent.content.mediaSource,
textContent = targetEvent.content.body, textContent = targetEvent.content.body,
type = AttachmentThumbnailType.Image, type = AttachmentThumbnailType.Image,
blurHash = targetEvent.content.blurhash, blurHash = targetEvent.content.blurhash,

View file

@ -260,7 +260,7 @@ private fun MessageSummary(event: TimelineItem.Event, modifier: Modifier = Modif
AttachmentThumbnail( AttachmentThumbnail(
modifier = imageModifier, modifier = imageModifier,
info = AttachmentThumbnailInfo( info = AttachmentThumbnailInfo(
thumbnailSource = event.content.mediaSource, thumbnailSource = event.content.thumbnailSource ?: event.content.mediaSource,
textContent = textContent, textContent = textContent,
type = AttachmentThumbnailType.Image, type = AttachmentThumbnailType.Image,
blurHash = event.content.blurhash, blurHash = event.content.blurhash,

View file

@ -595,7 +595,7 @@ private fun attachmentThumbnailInfoForInReplyTo(inReplyTo: InReplyTo.Ready): Att
val messageContent = inReplyTo.content as? MessageContent ?: return null val messageContent = inReplyTo.content as? MessageContent ?: return null
return when (val type = messageContent.type) { return when (val type = messageContent.type) {
is ImageMessageType -> AttachmentThumbnailInfo( is ImageMessageType -> AttachmentThumbnailInfo(
thumbnailSource = type.info?.thumbnailSource, thumbnailSource = type.info?.thumbnailSource ?: type.source,
textContent = messageContent.body, textContent = messageContent.body,
type = AttachmentThumbnailType.Image, type = AttachmentThumbnailType.Image,
blurHash = type.info?.blurhash, blurHash = type.info?.blurhash,

View file

@ -19,6 +19,7 @@ package io.element.android.features.messages.impl.timeline.model.event
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.media3.common.MimeTypes import androidx.media3.common.MimeTypes
import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.ui.components.A_BLUR_HASH
open class TimelineItemImageContentProvider : PreviewParameterProvider<TimelineItemImageContent> { open class TimelineItemImageContentProvider : PreviewParameterProvider<TimelineItemImageContent> {
override val values: Sequence<TimelineItemImageContent> override val values: Sequence<TimelineItemImageContent>
@ -34,7 +35,7 @@ fun aTimelineItemImageContent() = TimelineItemImageContent(
mediaSource = MediaSource(""), mediaSource = MediaSource(""),
thumbnailSource = null, thumbnailSource = null,
mimeType = MimeTypes.IMAGE_JPEG, mimeType = MimeTypes.IMAGE_JPEG,
blurhash = "TQF5:I_NtRE4kXt7Z#MwkCIARPjr", blurhash = A_BLUR_HASH,
width = null, width = null,
height = 300, height = 300,
aspectRatio = 0.5f, aspectRatio = 0.5f,

View file

@ -19,6 +19,7 @@ package io.element.android.features.messages.impl.timeline.model.event
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.ui.components.A_BLUR_HASH
open class TimelineItemVideoContentProvider : PreviewParameterProvider<TimelineItemVideoContent> { open class TimelineItemVideoContentProvider : PreviewParameterProvider<TimelineItemVideoContent> {
override val values: Sequence<TimelineItemVideoContent> override val values: Sequence<TimelineItemVideoContent>
@ -32,7 +33,7 @@ open class TimelineItemVideoContentProvider : PreviewParameterProvider<TimelineI
fun aTimelineItemVideoContent() = TimelineItemVideoContent( fun aTimelineItemVideoContent() = TimelineItemVideoContent(
body = "Video.mp4", body = "Video.mp4",
thumbnailSource = null, thumbnailSource = null,
blurHash = "TQF5:I_NtRE4kXt7Z#MwkCIARPjr", blurHash = A_BLUR_HASH,
aspectRatio = 0.5f, aspectRatio = 0.5f,
duration = 100, duration = 100,
videoSource = MediaSource(""), videoSource = MediaSource(""),

View file

@ -20,18 +20,26 @@ import android.os.Parcelable
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.GraphicEq import androidx.compose.material.icons.outlined.GraphicEq
import androidx.compose.material.icons.outlined.Image
import androidx.compose.material.icons.outlined.VideoCameraBack import androidx.compose.material.icons.outlined.VideoCameraBack
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
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.rotate import androidx.compose.ui.draw.rotate
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.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.components.BlurHashAsyncImage import io.element.android.libraries.designsystem.components.BlurHashAsyncImage
import io.element.android.libraries.designsystem.components.PinIcon import io.element.android.libraries.designsystem.components.PinIcon
import io.element.android.libraries.designsystem.preview.ElementPreview
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 io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.media.MediaSource
@ -57,12 +65,26 @@ fun AttachmentThumbnail(
contentScale = ContentScale.Crop, contentScale = ContentScale.Crop,
modifier = modifier, modifier = modifier,
) )
} else if (info.blurHash != null) {
BlurHashAsyncImage(
model = null,
blurHash = info.blurHash,
contentDescription = info.textContent,
contentScale = ContentScale.Crop,
modifier = modifier,
)
} else { } else {
Box( Box(
modifier = modifier.background(backgroundColor), modifier = modifier.background(backgroundColor),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
when (info.type) { when (info.type) {
AttachmentThumbnailType.Image -> {
Icon(
imageVector = Icons.Outlined.Image,
contentDescription = info.textContent,
)
}
AttachmentThumbnailType.Video -> { AttachmentThumbnailType.Video -> {
Icon( Icon(
imageVector = Icons.Outlined.VideoCameraBack, imageVector = Icons.Outlined.VideoCameraBack,
@ -92,8 +114,14 @@ fun AttachmentThumbnail(
PinIcon( PinIcon(
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
) )
/*
// For coherency across the app, we should us this instead. Waiting for design decision.
Icon(
resourceId = R.drawable.ic_september_location,
contentDescription = info.textContent,
)
*/
} }
else -> Unit
} }
} }
} }
@ -111,3 +139,14 @@ data class AttachmentThumbnailInfo(
val textContent: String? = null, val textContent: String? = null,
val blurHash: String? = null, val blurHash: String? = null,
) : Parcelable ) : Parcelable
@PreviewsDayNight
@Composable
internal fun AttachmentThumbnailPreview(@PreviewParameter(AttachmentThumbnailInfoProvider::class) data: AttachmentThumbnailInfo) = ElementPreview {
AttachmentThumbnail(
data,
modifier = Modifier
.size(36.dp)
.clip(RoundedCornerShape(4.dp))
)
}

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* 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
*
* http://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 io.element.android.libraries.matrix.ui.components
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.matrix.api.media.MediaSource
open class AttachmentThumbnailInfoProvider : PreviewParameterProvider<AttachmentThumbnailInfo> {
override val values: Sequence<AttachmentThumbnailInfo>
get() = sequenceOf(
anAttachmentThumbnailInfo(type = AttachmentThumbnailType.Image),
anAttachmentThumbnailInfo(type = AttachmentThumbnailType.Image, blurHash = A_BLUR_HASH),
anAttachmentThumbnailInfo(type = AttachmentThumbnailType.Video),
anAttachmentThumbnailInfo(type = AttachmentThumbnailType.Video, blurHash = A_BLUR_HASH),
anAttachmentThumbnailInfo(type = AttachmentThumbnailType.Audio),
anAttachmentThumbnailInfo(type = AttachmentThumbnailType.File),
anAttachmentThumbnailInfo(type = AttachmentThumbnailType.Location),
anAttachmentThumbnailInfo(type = AttachmentThumbnailType.Voice),
)
}
fun anAttachmentThumbnailInfo(
type: AttachmentThumbnailType,
thumbnailSource: MediaSource? = null,
textContent: String? = null,
blurHash: String? = null,
) =
AttachmentThumbnailInfo(
type = type,
thumbnailSource = thumbnailSource,
textContent = textContent,
blurHash = blurHash,
)
const val A_BLUR_HASH = "TQF5:I_NtRE4kXt7Z#MwkCIARPjr"

View file

@ -58,6 +58,7 @@ import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.core.TransactionId
import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.ui.components.A_BLUR_HASH
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnail import io.element.android.libraries.matrix.ui.components.AttachmentThumbnail
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType
@ -720,7 +721,7 @@ internal fun TextComposerReplyPreview() = ElementPreview {
thumbnailSource = MediaSource("https://domain.com/image.jpg"), thumbnailSource = MediaSource("https://domain.com/image.jpg"),
textContent = "image.jpg", textContent = "image.jpg",
type = AttachmentThumbnailType.Image, type = AttachmentThumbnailType.Image,
blurHash = "TQF5:I_NtRE4kXt7Z#MwkCIARPjr", blurHash = A_BLUR_HASH,
), ),
defaultContent = "image.jpg" defaultContent = "image.jpg"
), ),
@ -741,7 +742,7 @@ internal fun TextComposerReplyPreview() = ElementPreview {
thumbnailSource = MediaSource("https://domain.com/video.mp4"), thumbnailSource = MediaSource("https://domain.com/video.mp4"),
textContent = "video.mp4", textContent = "video.mp4",
type = AttachmentThumbnailType.Video, type = AttachmentThumbnailType.Video,
blurHash = "TQF5:I_NtRE4kXt7Z#MwkCIARPjr", blurHash = A_BLUR_HASH,
), ),
defaultContent = "video.mp4" defaultContent = "video.mp4"
), ),

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:5554661aeb29ec0d95464942da03df5e83dc78762bc08e2f57851a8f7e811a5a oid sha256:2f594c95c1de47b3fa74add85f87b70005c54ed9230d29967742a8a8291b6844
size 39438 size 46255

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:5cd8fe02340aeb0edb116c0f3d8f07efc1d331849688718782db25e17bb6e1ab oid sha256:bd4fad16706caabe092b40fc2ffbc748db5b763ba649f88fe7463ab17f73ca4e
size 37766 size 44329

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:23e51866da525f58b9f34e9fb3a5f29e741d1d3cf99396a6d914461d4eeb5b98
size 4904

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b2bdd2e9f721cd248894ea575259c89a96e59a350aecc5ee5f59d0064591aae3
size 14164

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:08b43b181bbeec3d32afc3cab225f84c573be7bc39157148c5daaa339cc74aae
size 5067

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b2bdd2e9f721cd248894ea575259c89a96e59a350aecc5ee5f59d0064591aae3
size 14164

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8c8d5f5ba674c54b215b049db27f6cea339698cd2a17308ac74676d99b7f04ca
size 4632

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1ef9dec75321bc209c6eee98f77e258de7673ba2b6b9916694affd7d0d758c9e
size 6012

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:48a3f6894357ec085d80374fbf2d423b714babfb79516dbd68f0e4af5d4120ba
size 5972

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4e623fb91286b7d1219f72e644d76c93f3c47ebe4ceb71201ed13d571554fc94
size 5167

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a9cef0395b912768a7ea2d41f95389a16590dda052e999594f402d1c8a872b1d
size 4897

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:12fc3a2e96344f77b1d708b74161418e470e40f41e473002e23ab296502c4250
size 14125

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cfe7c55577bd5fc46f3da775e5e7ac957e56170f12327584c309012031f1804b
size 5072

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:12fc3a2e96344f77b1d708b74161418e470e40f41e473002e23ab296502c4250
size 14125

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:833c38047423a468fdb12e1cdd821e68bd3258dac4ed9b49c74924a7ef447791
size 4649

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f6638aa7103b2f0ec0df4f1cac33651a56c362acd19b6f4d7a385a6801a55cd0
size 5835

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:419b563666e521193b8c6785e6e4274db6938476f1c4d36872914ad017cb6383
size 6003

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7ad31e157489fa27dda1fdb5804aab5e3906868fe4871c55eaf2336a0ee826bd
size 5168