Merge pull request #3816 from element-hq/feature/bma/moreCaptionWork
Iteration on caption
This commit is contained in:
commit
b23ec7e0bd
11 changed files with 84 additions and 29 deletions
|
|
@ -70,14 +70,14 @@ fun TimelineItemVideoView(
|
||||||
onContentLayoutChange: (ContentAvoidingLayoutData) -> Unit,
|
onContentLayoutChange: (ContentAvoidingLayoutData) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val description = stringResource(CommonStrings.common_image)
|
val description = stringResource(CommonStrings.common_video)
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier.semantics { contentDescription = description }
|
modifier = modifier.semantics { contentDescription = description }
|
||||||
) {
|
) {
|
||||||
val containerModifier = if (content.showCaption) {
|
val containerModifier = if (content.showCaption) {
|
||||||
Modifier
|
Modifier
|
||||||
.padding(top = 6.dp)
|
.padding(top = 6.dp)
|
||||||
.clip(RoundedCornerShape(6.dp))
|
.clip(RoundedCornerShape(6.dp))
|
||||||
} else {
|
} else {
|
||||||
Modifier
|
Modifier
|
||||||
}
|
}
|
||||||
|
|
@ -93,8 +93,8 @@ fun TimelineItemVideoView(
|
||||||
var isLoaded by remember { mutableStateOf(false) }
|
var isLoaded by remember { mutableStateOf(false) }
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.then(if (isLoaded) Modifier.background(Color.White) else Modifier),
|
.then(if (isLoaded) Modifier.background(Color.White) else Modifier),
|
||||||
model = MediaRequestData(
|
model = MediaRequestData(
|
||||||
source = content.thumbnailSource,
|
source = content.thumbnailSource,
|
||||||
kind = MediaRequestData.Kind.File(
|
kind = MediaRequestData.Kind.File(
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,9 @@ class DefaultPinnedMessagesBannerFormatter @Inject constructor(
|
||||||
messageType.bestDescription.prefixWith(CommonStrings.common_audio)
|
messageType.bestDescription.prefixWith(CommonStrings.common_audio)
|
||||||
}
|
}
|
||||||
is VoiceMessageType -> {
|
is VoiceMessageType -> {
|
||||||
messageType.bestDescription.prefixWith(CommonStrings.common_voice_message)
|
// In this case, do not use bestDescription, because the filename is useless, only use the caption if available.
|
||||||
|
messageType.caption?.prefixWith(sp.getString(CommonStrings.common_voice_message))
|
||||||
|
?: sp.getString(CommonStrings.common_voice_message)
|
||||||
}
|
}
|
||||||
is OtherMessageType -> {
|
is OtherMessageType -> {
|
||||||
messageType.body
|
messageType.body
|
||||||
|
|
|
||||||
|
|
@ -110,25 +110,27 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
|
||||||
messageType.toPlainText(permalinkParser)
|
messageType.toPlainText(permalinkParser)
|
||||||
}
|
}
|
||||||
is VideoMessageType -> {
|
is VideoMessageType -> {
|
||||||
sp.getString(CommonStrings.common_video)
|
messageType.bestDescription.prefixWith(sp.getString(CommonStrings.common_video))
|
||||||
}
|
}
|
||||||
is ImageMessageType -> {
|
is ImageMessageType -> {
|
||||||
sp.getString(CommonStrings.common_image)
|
messageType.bestDescription.prefixWith(sp.getString(CommonStrings.common_image))
|
||||||
}
|
}
|
||||||
is StickerMessageType -> {
|
is StickerMessageType -> {
|
||||||
sp.getString(CommonStrings.common_sticker)
|
messageType.bestDescription.prefixWith(sp.getString(CommonStrings.common_sticker))
|
||||||
}
|
}
|
||||||
is LocationMessageType -> {
|
is LocationMessageType -> {
|
||||||
sp.getString(CommonStrings.common_shared_location)
|
sp.getString(CommonStrings.common_shared_location)
|
||||||
}
|
}
|
||||||
is FileMessageType -> {
|
is FileMessageType -> {
|
||||||
sp.getString(CommonStrings.common_file)
|
messageType.bestDescription.prefixWith(sp.getString(CommonStrings.common_file))
|
||||||
}
|
}
|
||||||
is AudioMessageType -> {
|
is AudioMessageType -> {
|
||||||
sp.getString(CommonStrings.common_audio)
|
messageType.bestDescription.prefixWith(sp.getString(CommonStrings.common_audio))
|
||||||
}
|
}
|
||||||
is VoiceMessageType -> {
|
is VoiceMessageType -> {
|
||||||
sp.getString(CommonStrings.common_voice_message)
|
// In this case, do not use bestDescription, because the filename is useless, only use the caption if available.
|
||||||
|
messageType.caption?.prefixWith(sp.getString(CommonStrings.common_voice_message))
|
||||||
|
?: sp.getString(CommonStrings.common_voice_message)
|
||||||
}
|
}
|
||||||
is OtherMessageType -> {
|
is OtherMessageType -> {
|
||||||
messageType.body
|
messageType.body
|
||||||
|
|
@ -140,7 +142,7 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
|
||||||
return message.prefixIfNeeded(senderDisambiguatedDisplayName, isDmRoom, isOutgoing)
|
return message.prefixIfNeeded(senderDisambiguatedDisplayName, isDmRoom, isOutgoing)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun String.prefixIfNeeded(
|
private fun CharSequence.prefixIfNeeded(
|
||||||
senderDisambiguatedDisplayName: String,
|
senderDisambiguatedDisplayName: String,
|
||||||
isDmRoom: Boolean,
|
isDmRoom: Boolean,
|
||||||
isOutgoing: Boolean,
|
isOutgoing: Boolean,
|
||||||
|
|
|
||||||
|
|
@ -159,11 +159,11 @@ class DefaultPinnedMessagesBannerFormatterTest {
|
||||||
val expectedResult = when (type) {
|
val expectedResult = when (type) {
|
||||||
is VideoMessageType,
|
is VideoMessageType,
|
||||||
is AudioMessageType,
|
is AudioMessageType,
|
||||||
is VoiceMessageType,
|
|
||||||
is ImageMessageType,
|
is ImageMessageType,
|
||||||
is StickerMessageType,
|
is StickerMessageType,
|
||||||
is FileMessageType,
|
is FileMessageType,
|
||||||
is LocationMessageType -> AnnotatedString::class.java
|
is LocationMessageType -> AnnotatedString::class.java
|
||||||
|
is VoiceMessageType,
|
||||||
is EmoteMessageType,
|
is EmoteMessageType,
|
||||||
is TextMessageType,
|
is TextMessageType,
|
||||||
is NoticeMessageType,
|
is NoticeMessageType,
|
||||||
|
|
@ -176,7 +176,7 @@ class DefaultPinnedMessagesBannerFormatterTest {
|
||||||
val expectedResult = when (type) {
|
val expectedResult = when (type) {
|
||||||
is VideoMessageType -> "Video: Shared body"
|
is VideoMessageType -> "Video: Shared body"
|
||||||
is AudioMessageType -> "Audio: Shared body"
|
is AudioMessageType -> "Audio: Shared body"
|
||||||
is VoiceMessageType -> "Voice message: Shared body"
|
is VoiceMessageType -> "Voice message"
|
||||||
is ImageMessageType -> "Image: Shared body"
|
is ImageMessageType -> "Image: Shared body"
|
||||||
is StickerMessageType -> "Sticker: Shared body"
|
is StickerMessageType -> "Sticker: Shared body"
|
||||||
is FileMessageType -> "File: Shared body"
|
is FileMessageType -> "File: Shared body"
|
||||||
|
|
|
||||||
|
|
@ -208,32 +208,51 @@ class DefaultRoomLastMessageFormatterTest {
|
||||||
|
|
||||||
// Verify results of DM mode
|
// Verify results of DM mode
|
||||||
for ((type, result) in resultsInDm) {
|
for ((type, result) in resultsInDm) {
|
||||||
|
val string = result.toString()
|
||||||
val expectedResult = when (type) {
|
val expectedResult = when (type) {
|
||||||
is VideoMessageType -> "Video"
|
is VideoMessageType -> "Video: Shared body"
|
||||||
is AudioMessageType -> "Audio"
|
is AudioMessageType -> "Audio: Shared body"
|
||||||
is VoiceMessageType -> "Voice message"
|
is VoiceMessageType -> "Voice message"
|
||||||
is ImageMessageType -> "Image"
|
is ImageMessageType -> "Image: Shared body"
|
||||||
is StickerMessageType -> "Sticker"
|
is StickerMessageType -> "Sticker: Shared body"
|
||||||
is FileMessageType -> "File"
|
is FileMessageType -> "File: Shared body"
|
||||||
is LocationMessageType -> "Shared location"
|
is LocationMessageType -> "Shared location"
|
||||||
is EmoteMessageType -> "* $senderName ${type.body}"
|
is EmoteMessageType -> "* $senderName ${type.body}"
|
||||||
is TextMessageType,
|
is TextMessageType,
|
||||||
is NoticeMessageType,
|
is NoticeMessageType,
|
||||||
is OtherMessageType -> body
|
is OtherMessageType -> body
|
||||||
}
|
}
|
||||||
assertWithMessage("$type was not properly handled for DM").that(result).isEqualTo(expectedResult)
|
val shouldCreateAnnotatedString = when (type) {
|
||||||
|
is VideoMessageType -> true
|
||||||
|
is AudioMessageType -> true
|
||||||
|
is VoiceMessageType -> false
|
||||||
|
is ImageMessageType -> true
|
||||||
|
is StickerMessageType -> true
|
||||||
|
is FileMessageType -> true
|
||||||
|
is LocationMessageType -> false
|
||||||
|
is EmoteMessageType -> false
|
||||||
|
is TextMessageType -> false
|
||||||
|
is NoticeMessageType -> false
|
||||||
|
is OtherMessageType -> false
|
||||||
|
}
|
||||||
|
if (shouldCreateAnnotatedString) {
|
||||||
|
assertWithMessage("$type doesn't produce an AnnotatedString")
|
||||||
|
.that(result)
|
||||||
|
.isInstanceOf(AnnotatedString::class.java)
|
||||||
|
}
|
||||||
|
assertWithMessage("$type was not properly handled for DM").that(string).isEqualTo(expectedResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify results of Room mode
|
// Verify results of Room mode
|
||||||
for ((type, result) in resultsInRoom) {
|
for ((type, result) in resultsInRoom) {
|
||||||
val string = result.toString()
|
val string = result.toString()
|
||||||
val expectedResult = when (type) {
|
val expectedResult = when (type) {
|
||||||
is VideoMessageType -> "$expectedPrefix: Video"
|
is VideoMessageType -> "$expectedPrefix: Video: Shared body"
|
||||||
is AudioMessageType -> "$expectedPrefix: Audio"
|
is AudioMessageType -> "$expectedPrefix: Audio: Shared body"
|
||||||
is VoiceMessageType -> "$expectedPrefix: Voice message"
|
is VoiceMessageType -> "$expectedPrefix: Voice message"
|
||||||
is ImageMessageType -> "$expectedPrefix: Image"
|
is ImageMessageType -> "$expectedPrefix: Image: Shared body"
|
||||||
is StickerMessageType -> "$expectedPrefix: Sticker"
|
is StickerMessageType -> "$expectedPrefix: Sticker: Shared body"
|
||||||
is FileMessageType -> "$expectedPrefix: File"
|
is FileMessageType -> "$expectedPrefix: File: Shared body"
|
||||||
is LocationMessageType -> "$expectedPrefix: Shared location"
|
is LocationMessageType -> "$expectedPrefix: Shared location"
|
||||||
is TextMessageType,
|
is TextMessageType,
|
||||||
is NoticeMessageType,
|
is NoticeMessageType,
|
||||||
|
|
@ -249,7 +268,8 @@ class DefaultRoomLastMessageFormatterTest {
|
||||||
is FileMessageType -> true
|
is FileMessageType -> true
|
||||||
is LocationMessageType -> false
|
is LocationMessageType -> false
|
||||||
is EmoteMessageType -> false
|
is EmoteMessageType -> false
|
||||||
is TextMessageType, is NoticeMessageType -> true
|
is TextMessageType -> true
|
||||||
|
is NoticeMessageType -> true
|
||||||
is OtherMessageType -> true
|
is OtherMessageType -> true
|
||||||
}
|
}
|
||||||
if (shouldCreateAnnotatedString) {
|
if (shouldCreateAnnotatedString) {
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,7 @@ class DefaultNotifiableEventResolver @Inject constructor(
|
||||||
senderDisambiguatedDisplayName = senderDisambiguatedDisplayName,
|
senderDisambiguatedDisplayName = senderDisambiguatedDisplayName,
|
||||||
body = messageBody,
|
body = messageBody,
|
||||||
imageUriString = content.fetchImageIfPresent(client)?.toString(),
|
imageUriString = content.fetchImageIfPresent(client)?.toString(),
|
||||||
|
imageMimeType = content.getImageMimetype(),
|
||||||
roomName = roomDisplayName,
|
roomName = roomDisplayName,
|
||||||
roomIsDm = isDm,
|
roomIsDm = isDm,
|
||||||
roomAvatarPath = roomAvatarUrl,
|
roomAvatarPath = roomAvatarUrl,
|
||||||
|
|
@ -316,6 +317,17 @@ class DefaultNotifiableEventResolver @Inject constructor(
|
||||||
}
|
}
|
||||||
.getOrNull()
|
.getOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun NotificationContent.MessageLike.RoomMessage.getImageMimetype(): String? {
|
||||||
|
if (appPreferencesStore.doesHideImagesAndVideosFlow().first()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return when (val messageType = messageType) {
|
||||||
|
is ImageMessageType -> messageType.info?.mimetype
|
||||||
|
is VideoMessageType -> null // Use the thumbnail here?
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
|
|
@ -333,6 +345,7 @@ internal fun buildNotifiableMessageEvent(
|
||||||
// We cannot use Uri? type here, as that could trigger a
|
// We cannot use Uri? type here, as that could trigger a
|
||||||
// NotSerializableException when persisting this to storage
|
// NotSerializableException when persisting this to storage
|
||||||
imageUriString: String? = null,
|
imageUriString: String? = null,
|
||||||
|
imageMimeType: String? = null,
|
||||||
threadId: ThreadId? = null,
|
threadId: ThreadId? = null,
|
||||||
roomName: String? = null,
|
roomName: String? = null,
|
||||||
roomIsDm: Boolean = false,
|
roomIsDm: Boolean = false,
|
||||||
|
|
@ -358,6 +371,7 @@ internal fun buildNotifiableMessageEvent(
|
||||||
senderDisambiguatedDisplayName = senderDisambiguatedDisplayName,
|
senderDisambiguatedDisplayName = senderDisambiguatedDisplayName,
|
||||||
body = body,
|
body = body,
|
||||||
imageUriString = imageUriString,
|
imageUriString = imageUriString,
|
||||||
|
imageMimeType = imageMimeType,
|
||||||
threadId = threadId,
|
threadId = threadId,
|
||||||
roomName = roomName,
|
roomName = roomName,
|
||||||
roomIsDm = roomIsDm,
|
roomIsDm = roomIsDm,
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,7 @@ class NotificationBroadcastReceiverHandler @Inject constructor(
|
||||||
?: stringProvider.getString(R.string.notification_sender_me),
|
?: stringProvider.getString(R.string.notification_sender_me),
|
||||||
body = message,
|
body = message,
|
||||||
imageUriString = null,
|
imageUriString = null,
|
||||||
|
imageMimeType = null,
|
||||||
threadId = threadId,
|
threadId = threadId,
|
||||||
roomName = room.displayName,
|
roomName = room.displayName,
|
||||||
roomIsDm = room.isDm,
|
roomIsDm = room.isDm,
|
||||||
|
|
|
||||||
|
|
@ -419,11 +419,22 @@ class DefaultNotificationCreator @Inject constructor(
|
||||||
senderPerson
|
senderPerson
|
||||||
).also { message ->
|
).also { message ->
|
||||||
event.imageUri?.let {
|
event.imageUri?.let {
|
||||||
message.setData("image/", it)
|
message.setData(event.imageMimeType ?: "image/", it)
|
||||||
}
|
}
|
||||||
message.extras.putString(MESSAGE_EVENT_ID, event.eventId.value)
|
message.extras.putString(MESSAGE_EVENT_ID, event.eventId.value)
|
||||||
}
|
}
|
||||||
addMessage(message)
|
addMessage(message)
|
||||||
|
|
||||||
|
// Add additional message for captions
|
||||||
|
if (event.imageUri != null && event.body != null) {
|
||||||
|
addMessage(
|
||||||
|
MessagingStyle.Message(
|
||||||
|
event.body,
|
||||||
|
event.timestamp,
|
||||||
|
senderPerson,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,8 @@ data class NotifiableMessageEvent(
|
||||||
val body: String?,
|
val body: String?,
|
||||||
// We cannot use Uri? type here, as that could trigger a
|
// We cannot use Uri? type here, as that could trigger a
|
||||||
// NotSerializableException when persisting this to storage
|
// NotSerializableException when persisting this to storage
|
||||||
val imageUriString: String?,
|
private val imageUriString: String?,
|
||||||
|
val imageMimeType: String?,
|
||||||
val threadId: ThreadId?,
|
val threadId: ThreadId?,
|
||||||
val roomName: String?,
|
val roomName: String?,
|
||||||
val roomIsDm: Boolean = false,
|
val roomIsDm: Boolean = false,
|
||||||
|
|
|
||||||
|
|
@ -590,6 +590,7 @@ class DefaultNotifiableEventResolverTest {
|
||||||
senderDisambiguatedDisplayName = A_USER_NAME_2,
|
senderDisambiguatedDisplayName = A_USER_NAME_2,
|
||||||
body = "Call in progress (unsupported)",
|
body = "Call in progress (unsupported)",
|
||||||
imageUriString = null,
|
imageUriString = null,
|
||||||
|
imageMimeType = null,
|
||||||
threadId = null,
|
threadId = null,
|
||||||
roomName = A_ROOM_NAME,
|
roomName = A_ROOM_NAME,
|
||||||
roomAvatarPath = null,
|
roomAvatarPath = null,
|
||||||
|
|
@ -669,6 +670,7 @@ class DefaultNotifiableEventResolverTest {
|
||||||
canBeReplaced = false,
|
canBeReplaced = false,
|
||||||
isRedacted = false,
|
isRedacted = false,
|
||||||
imageUriString = null,
|
imageUriString = null,
|
||||||
|
imageMimeType = null,
|
||||||
type = EventType.CALL_NOTIFY,
|
type = EventType.CALL_NOTIFY,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -704,6 +706,7 @@ class DefaultNotifiableEventResolverTest {
|
||||||
canBeReplaced = false,
|
canBeReplaced = false,
|
||||||
isRedacted = false,
|
isRedacted = false,
|
||||||
imageUriString = null,
|
imageUriString = null,
|
||||||
|
imageMimeType = null,
|
||||||
type = EventType.CALL_NOTIFY,
|
type = EventType.CALL_NOTIFY,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@ fun aNotifiableMessageEvent(
|
||||||
canBeReplaced = false,
|
canBeReplaced = false,
|
||||||
isRedacted = isRedacted,
|
isRedacted = isRedacted,
|
||||||
imageUriString = null,
|
imageUriString = null,
|
||||||
|
imageMimeType = null,
|
||||||
roomAvatarPath = null,
|
roomAvatarPath = null,
|
||||||
senderAvatarPath = null,
|
senderAvatarPath = null,
|
||||||
soundName = null,
|
soundName = null,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue