From 94f2c1cc98eabc5074532eb01a29cd392e7826ca Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Mon, 26 Aug 2024 16:41:45 +0200 Subject: [PATCH] Fixed text message description not being italic for some parts + improved description: added duration to voice message & replaced file name by image emoji for pictures --- .../notifications/NotificationsManager.kt | 4 +- .../ui/main/chat/model/ConversationModel.kt | 11 +-- .../ui/main/chat/model/EventLogModel.kt | 2 +- .../ui/main/chat/model/MessageModel.kt | 5 +- .../SendMessageInConversationViewModel.kt | 5 +- .../java/org/linphone/utils/LinphoneUtils.kt | 71 ++++++++++++++----- app/src/main/res/layout/chat_list_cell.xml | 23 +++++- app/src/main/res/values-fr/strings.xml | 11 ++- app/src/main/res/values/strings.xml | 11 ++- 9 files changed, 99 insertions(+), 44 deletions(-) diff --git a/app/src/main/java/org/linphone/notifications/NotificationsManager.kt b/app/src/main/java/org/linphone/notifications/NotificationsManager.kt index 010350ee6..845c3ab52 100644 --- a/app/src/main/java/org/linphone/notifications/NotificationsManager.kt +++ b/app/src/main/java/org/linphone/notifications/NotificationsManager.kt @@ -778,7 +778,7 @@ class NotificationsManager @MainThread constructor(private val context: Context) coreContext.contactsManager.findContactByAddress(address) val displayName = contact?.name ?: LinphoneUtils.getDisplayName(address) - val originalMessage = LinphoneUtils.getTextDescribingMessage(message) + val originalMessage = LinphoneUtils.getPlainTextDescribingMessage(message) val text = AppUtils.getString(R.string.notification_chat_message_reaction_received).format( reaction, originalMessage @@ -874,7 +874,7 @@ class NotificationsManager @MainThread constructor(private val context: Context) coreContext.contactsManager.findContactByAddress(message.fromAddress) val displayName = contact?.name ?: LinphoneUtils.getDisplayName(message.fromAddress) - val text = LinphoneUtils.getTextDescribingMessage(message) + val text = LinphoneUtils.getPlainTextDescribingMessage(message) val notifiableMessage = NotifiableMessage( text, contact, diff --git a/app/src/main/java/org/linphone/ui/main/chat/model/ConversationModel.kt b/app/src/main/java/org/linphone/ui/main/chat/model/ConversationModel.kt index 130cd9891..94cd159a7 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/model/ConversationModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/model/ConversationModel.kt @@ -19,6 +19,7 @@ */ package org.linphone.ui.main.chat.model +import android.text.Spannable import androidx.annotation.UiThread import androidx.annotation.WorkerThread import androidx.lifecycle.MutableLiveData @@ -70,7 +71,9 @@ class ConversationModel @WorkerThread constructor(val chatRoom: ChatRoom) { val isEphemeral = MutableLiveData() - val lastMessageText = MutableLiveData() + val lastMessageTextSender = MutableLiveData() + + val lastMessageText = MutableLiveData() val lastMessageIcon = MutableLiveData() @@ -254,7 +257,6 @@ class ConversationModel @WorkerThread constructor(val chatRoom: ChatRoom) { private fun updateLastMessageStatus(message: ChatMessage) { val isOutgoing = message.isOutgoing - val text = LinphoneUtils.getTextDescribingMessage(message) if (isGroup && !isOutgoing) { val fromAddress = message.fromAddress val sender = coreContext.contactsManager.findContactByAddress(fromAddress) @@ -263,10 +265,11 @@ class ConversationModel @WorkerThread constructor(val chatRoom: ChatRoom) { R.string.conversations_last_message_format, name ) - lastMessageText.postValue("$senderName $text") + lastMessageTextSender.postValue(senderName) } else { - lastMessageText.postValue(text) + lastMessageTextSender.postValue("") } + lastMessageText.postValue(LinphoneUtils.getFormattedTextDescribingMessage(message)) isLastMessageOutgoing.postValue(isOutgoing) if (isOutgoing) { diff --git a/app/src/main/java/org/linphone/ui/main/chat/model/EventLogModel.kt b/app/src/main/java/org/linphone/ui/main/chat/model/EventLogModel.kt index b0ca39f3b..c01913fb0 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/model/EventLogModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/model/EventLogModel.kt @@ -58,7 +58,7 @@ class EventLogModel @WorkerThread constructor( val avatarModel = coreContext.contactsManager.getContactAvatarModelForAddress(from) replyTo = avatarModel.contactName ?: LinphoneUtils.getDisplayName(from) - LinphoneUtils.getTextDescribingMessage(replyMessage) + LinphoneUtils.getPlainTextDescribingMessage(replyMessage) } else { Log.e( "$TAG Failed to find the reply message from ID [${chatMessage.replyMessageId}]" diff --git a/app/src/main/java/org/linphone/ui/main/chat/model/MessageModel.kt b/app/src/main/java/org/linphone/ui/main/chat/model/MessageModel.kt index 39422d41c..8e57f502c 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/model/MessageModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/model/MessageModel.kt @@ -510,12 +510,11 @@ class MessageModel @WorkerThread constructor( filesList.postValue(filesPath) if (!displayableContentFound) { // Temporary workaround to prevent empty bubbles - val describe = LinphoneUtils.getTextDescribingMessage(chatMessage) + val describe = LinphoneUtils.getFormattedTextDescribingMessage(chatMessage) Log.w( "$TAG No displayable content found, generating text based description [$describe]" ) - val spannable = Spannable.Factory.getInstance().newSpannable(describe) - text.postValue(spannable) + text.postValue(describe) } } diff --git a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/SendMessageInConversationViewModel.kt b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/SendMessageInConversationViewModel.kt index 687c43626..470803d8f 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/SendMessageInConversationViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/SendMessageInConversationViewModel.kt @@ -21,6 +21,7 @@ package org.linphone.ui.main.chat.viewmodel import android.Manifest import android.content.pm.PackageManager +import android.text.Spannable import androidx.annotation.UiThread import androidx.annotation.WorkerThread import androidx.core.app.ActivityCompat @@ -82,7 +83,7 @@ class SendMessageInConversationViewModel @UiThread constructor() : GenericViewMo val isReplyingTo = MutableLiveData() - val isReplyingToMessage = MutableLiveData() + val isReplyingToMessage = MutableLiveData() val isKeyboardOpen = MutableLiveData() @@ -211,7 +212,7 @@ class SendMessageInConversationViewModel @UiThread constructor() : GenericViewMo Log.i("$TAG Pending reply to message [${message.messageId}]") chatMessageToReplyTo = message isReplyingTo.postValue(model.avatarModel.value?.friend?.name) - isReplyingToMessage.postValue(LinphoneUtils.getTextDescribingMessage(message)) + isReplyingToMessage.postValue(LinphoneUtils.getFormattedTextDescribingMessage(message)) isReplying.postValue(true) } } diff --git a/app/src/main/java/org/linphone/utils/LinphoneUtils.kt b/app/src/main/java/org/linphone/utils/LinphoneUtils.kt index 25faea4fb..9fbde15a6 100644 --- a/app/src/main/java/org/linphone/utils/LinphoneUtils.kt +++ b/app/src/main/java/org/linphone/utils/LinphoneUtils.kt @@ -19,10 +19,17 @@ */ package org.linphone.utils +import android.graphics.Typeface +import android.text.Spannable +import android.text.SpannableStringBuilder +import android.text.style.StyleSpan import androidx.annotation.AnyThread import androidx.annotation.DrawableRes import androidx.annotation.IntegerRes import androidx.annotation.WorkerThread +import androidx.core.text.toSpannable +import java.text.SimpleDateFormat +import java.util.Locale import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.R import org.linphone.contacts.getListOfSipAddresses @@ -391,9 +398,31 @@ class LinphoneUtils { } @WorkerThread - fun getTextDescribingMessage(message: ChatMessage): String { + fun getFormattedTextDescribingMessage(message: ChatMessage): Spannable { + val pair = getTextDescribingMessage(message) + val builder = SpannableStringBuilder( + "${pair.first} ${pair.second}".trim() + ) + builder.setSpan( + StyleSpan(Typeface.ITALIC), + 0, + pair.first.length, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE + ) + return builder.toSpannable() + } + + @WorkerThread + fun getPlainTextDescribingMessage(message: ChatMessage): String { + val pair = getTextDescribingMessage(message) + return "${pair.first} ${pair.second}".trim() + } + + @WorkerThread + private fun getTextDescribingMessage(message: ChatMessage): Pair { // If message contains text, then use that var text = message.contents.find { content -> content.isText }?.utf8Text ?: "" + var contentDescription = "" if (text.isEmpty()) { val firstContent = message.contents.firstOrNull() @@ -402,26 +431,21 @@ class LinphoneUtils { firstContent ) if (conferenceInfo != null) { - val subject = conferenceInfo.subject.orEmpty() - text = when (conferenceInfo.state) { + text = conferenceInfo.subject.orEmpty() + contentDescription = when (conferenceInfo.state) { ConferenceInfo.State.Cancelled -> { - AppUtils.getFormattedString( - R.string.message_meeting_invitation_cancelled_content_description, - subject + AppUtils.getString( + R.string.message_meeting_invitation_cancelled_content_description ) } - ConferenceInfo.State.Updated -> { - AppUtils.getFormattedString( - R.string.message_meeting_invitation_updated_content_description, - subject + AppUtils.getString( + R.string.message_meeting_invitation_updated_content_description ) } - else -> { - AppUtils.getFormattedString( - R.string.message_meeting_invitation_content_description, - subject + AppUtils.getString( + R.string.message_meeting_invitation_content_description ) } } @@ -432,18 +456,31 @@ class LinphoneUtils { text = firstContent.name.orEmpty() } } else if (firstContent?.isVoiceRecording == true) { - text = AppUtils.getString(R.string.message_voice_message_content_description) + val label = AppUtils.getString( + R.string.message_voice_message_content_description + ) + val formattedDuration = SimpleDateFormat( + "mm:ss", + Locale.getDefault() + ).format(firstContent.fileDuration) // duration is in ms + contentDescription = "$label ($formattedDuration)" } else { for (content in message.contents) { if (text.isNotEmpty()) { text += ", " } - text += content.name + val contentType = "${content.type}/${content.subtype}" + text += when (FileUtils.getMimeType(contentType)) { + FileUtils.MimeType.Image -> "\uD83D\uDDBC\uFE0F" + // FileUtils.MimeType.Video -> "\uD83C\uDF9E\uFE0F" + // FileUtils.MimeType.Audio -> "\uD83C\uDFB5" + else -> content.name + } } } } - return text + return Pair(contentDescription, text) } @WorkerThread diff --git a/app/src/main/res/layout/chat_list_cell.xml b/app/src/main/res/layout/chat_list_cell.xml index 67e7ba789..0e81426ae 100644 --- a/app/src/main/res/layout/chat_list_cell.xml +++ b/app/src/main/res/layout/chat_list_cell.xml @@ -80,6 +80,23 @@ app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toTopOf="@id/last_message_or_composing"/> + + + app:layout_constraintTop_toTopOf="@id/last_message_sender" + app:layout_constraintBottom_toBottomOf="@id/last_message_sender" /> Cliquez pour supprimer Transféré + invitation à une réunion : + réunion mise à jour : + réunion annulée : + message vocal + Aucune réunion pour le moment… Aucune réunion aujourd\'hui @@ -714,12 +719,6 @@ Mot de passe oublié ? Passer - - invitation à une réunion : %s - réunion mise à jour : %s - réunion annulée : %s - message vocal - La réunion a été mise à jour La réunion a été annulée diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 303b4c101..984cd6b60 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -540,6 +540,11 @@ Click to remove Forwarded + meeting invite: + meeting updated: + meeting cancelled: + voice message + No meeting for the moment… No meeting scheduled for today @@ -752,12 +757,6 @@ Forgotten password? Skip - - meeting invite: %s - meeting updated: %s - meeting cancelled: %s - voice message - Meeting has been updated Meeting has been cancelled!