Use newly added chat room compose APIs to notify wether text or voice recording is being composed

This commit is contained in:
Sylvain Berfini 2025-06-20 16:03:43 +02:00
parent 6cdcdec373
commit 595ff96d50
10 changed files with 97 additions and 60 deletions

View file

@ -280,11 +280,11 @@ open class ConversationFragment : SlidingPaneChildFragment() {
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun afterTextChanged(p0: Editable?) {
override fun afterTextChanged(editable: Editable?) {
if (viewModel.isGroup.value == true) {
sendMessageViewModel.closeParticipantsList()
val split = p0.toString().split(" ")
val split = editable.toString().split(" ")
for (part in split) {
if (part == "@") {
Log.i("$TAG '@' found, opening participants list")
@ -293,9 +293,7 @@ open class ConversationFragment : SlidingPaneChildFragment() {
}
}
if (p0.toString().isNotEmpty()) {
sendMessageViewModel.notifyChatMessageIsBeingComposed()
}
sendMessageViewModel.notifyComposing(editable.toString().isNotEmpty())
}
}

View file

@ -82,6 +82,8 @@ class ConversationModel
val lastMessageContentIcon = MutableLiveData<Int>()
val composingIcon = MutableLiveData<Int>()
val isLastMessageOutgoing = MutableLiveData<Boolean>()
val dateTime = MutableLiveData<String>()
@ -428,30 +430,10 @@ class ConversationModel
private fun computeComposingLabel() {
val composing = chatRoom.isRemoteComposing
isComposing.postValue(composing)
if (!composing) {
composingLabel.postValue("")
return
}
val composingFriends = arrayListOf<String>()
var label = ""
for (address in chatRoom.composingAddresses) {
val avatar = coreContext.contactsManager.getContactAvatarModelForAddress(address)
val name = avatar.name.value ?: LinphoneUtils.getDisplayName(address)
composingFriends.add(name)
label += "$name, "
}
if (composingFriends.isNotEmpty()) {
label = label.dropLast(2)
val format = AppUtils.getStringWithPlural(
R.plurals.conversation_composing_label,
composingFriends.size,
label
)
composingLabel.postValue(format)
} else {
composingLabel.postValue("")
}
val pair = LinphoneUtils.getComposingIconAndText(chatRoom)
val icon = pair.first
composingIcon.postValue(icon)
val label = pair.second
composingLabel.postValue(label)
}
}

View file

@ -44,7 +44,6 @@ import org.linphone.ui.main.chat.model.EventLogModel
import org.linphone.ui.main.chat.model.FileModel
import org.linphone.ui.main.chat.model.MessageModel
import org.linphone.ui.main.contacts.model.ContactAvatarModel
import org.linphone.utils.AppUtils
import org.linphone.utils.Event
import org.linphone.utils.FileUtils
import org.linphone.utils.LinphoneUtils
@ -89,6 +88,8 @@ class ConversationViewModel
val composingLabel = MutableLiveData<String>()
val composingIcon = MutableLiveData<Int>()
val searchBarVisible = MutableLiveData<Boolean>()
val searchFilter = MutableLiveData<String>()
@ -906,26 +907,12 @@ class ConversationViewModel
@WorkerThread
private fun computeComposingLabel() {
if (!isChatRoomInitialized()) return
val composingFriends = arrayListOf<String>()
var label = ""
for (address in chatRoom.composingAddresses) {
val avatar = coreContext.contactsManager.getContactAvatarModelForAddress(address)
val name = avatar.name.value ?: LinphoneUtils.getDisplayName(address)
composingFriends.add(name)
label += "$name, "
}
if (composingFriends.isNotEmpty()) {
label = label.dropLast(2)
val format = AppUtils.getStringWithPlural(
R.plurals.conversation_composing_label,
composingFriends.size,
label
)
composingLabel.postValue(format)
} else {
composingLabel.postValue("")
}
val pair = LinphoneUtils.getComposingIconAndText(chatRoom)
val icon = pair.first
composingIcon.postValue(icon)
val label = pair.second
composingLabel.postValue(label)
}
@WorkerThread

View file

@ -344,10 +344,14 @@ class SendMessageInConversationViewModel
}
@UiThread
fun notifyChatMessageIsBeingComposed() {
fun notifyComposing(composing: Boolean) {
coreContext.postOnCoreThread {
if (::chatRoom.isInitialized) {
chatRoom.compose()
if (composing) {
chatRoom.composeTextMessage()
} else {
chatRoom.stopComposing()
}
}
}
}
@ -489,6 +493,7 @@ class SendMessageInConversationViewModel
@UiThread
fun stopVoiceMessageRecording() {
coreContext.postOnCoreThread {
chatRoom.stopComposing()
stopVoiceRecorder()
}
}
@ -496,6 +501,7 @@ class SendMessageInConversationViewModel
@UiThread
fun cancelVoiceMessageRecording() {
coreContext.postOnCoreThread {
chatRoom.stopComposing()
stopVoiceRecorder()
val path = voiceMessageRecorder.file
@ -589,6 +595,7 @@ class SendMessageInConversationViewModel
}
else -> {}
}
chatRoom.composeVoiceMessage()
val duration = voiceMessageRecorder.duration
val formattedDuration = SimpleDateFormat("mm:ss", Locale.getDefault()).format(duration) // duration is in ms

View file

@ -470,6 +470,38 @@ class LinphoneUtils {
}
}
@WorkerThread
fun getComposingIconAndText(chatRoom: ChatRoom): Pair<Int, String> {
val composing = chatRoom.isRemoteComposing
if (!composing) {
return Pair(0, "")
}
var icon = R.drawable.chat_teardrop_dots
val composingFriends = arrayListOf<String>()
var label = ""
for (participant in chatRoom.composingParticipants) {
val address = participant.address
val avatar = coreContext.contactsManager.getContactAvatarModelForAddress(address)
val name = avatar.name.value ?: getDisplayName(address)
composingFriends.add(name)
label += "$name, "
}
if (composingFriends.isNotEmpty()) {
label = label.dropLast(2)
// TODO: use voice recording content type to change icon/text
val format = AppUtils.getStringWithPlural(
R.plurals.conversation_composing_label,
composingFriends.size,
label
)
return Pair(icon, format)
}
return Pair(icon, label)
}
@AnyThread
fun formatEphemeralExpiration(duration: Long): String {
return when (duration) {

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="256"
android:viewportHeight="256">
<path
android:pathData="M132,24A100.11,100.11 0,0 0,32 124v84a16,16 0,0 0,16 16h84a100,100 0,0 0,0 -200ZM132,208L48,208L48,124a84,84 0,1 1,84 84ZM144,128a12,12 0,1 1,-12 -12A12,12 0,0 1,144 128ZM100,128a12,12 0,1 1,-12 -12A12,12 0,0 1,100 128ZM188,128a12,12 0,1 1,-12 -12A12,12 0,0 1,188 128Z"
android:fillColor="#4e6074"/>
</vector>

View file

@ -326,19 +326,33 @@
app:layout_constraintStart_toStartOf="@id/events_list"
app:layout_constraintEnd_toEndOf="@id/events_list" />
<ImageView
android:id="@+id/composing_icon"
android:layout_width="@dimen/small_icon_size"
android:layout_height="@dimen/small_icon_size"
android:layout_marginStart="10dp"
android:src="@{viewModel.composingIcon, default=@drawable/microphone}"
android:visibility="@{viewModel.composingLabel.length() == 0 ? View.GONE : View.VISIBLE}"
android:contentDescription="@null"
app:tint="?attr/color_main2_600"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/composing"
app:layout_constraintTop_toTopOf="@id/composing"
app:layout_constraintBottom_toBottomOf="@id/composing"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_300"
android:id="@+id/composing"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginStart="5dp"
android:paddingBottom="5dp"
android:background="?attr/color_main2_000"
android:text="@{viewModel.composingLabel, default=`John Doe is composing...`}"
android:textSize="12sp"
android:visibility="@{viewModel.composingLabel.length() == 0 ? View.GONE : View.VISIBLE}"
app:layout_constraintBottom_toTopOf="@id/warning_disabled_not_secured"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintStart_toEndOf="@id/composing_icon"
app:layout_constraintEnd_toEndOf="parent" />
<include

View file

@ -92,21 +92,21 @@
android:textSize="14sp"
android:textColor="?attr/color_main2_500"
android:visibility="@{model.isBeingDeleted || model.lastMessageTextSender.length() == 0 || model.isComposing ? View.GONE : View.VISIBLE}"
textFont="@{model.isBeingDeleted || model.unreadMessageCount > 0 || model.isComposing ? NotoSansFont.NotoSansBold : NotoSansFont.NotoSansRegular}"
textFont="@{model.unreadMessageCount > 0 ? NotoSansFont.NotoSansBold : NotoSansFont.NotoSansRegular}"
app:layout_constraintStart_toStartOf="@id/title"
app:layout_constraintEnd_toStartOf="@id/last_message_forward"
app:layout_constraintEnd_toStartOf="@id/last_message_icon"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintBottom_toTopOf="@id/separator"/>
<ImageView
android:id="@+id/last_message_forward"
android:id="@+id/last_message_icon"
android:layout_width="@dimen/small_icon_size"
android:layout_height="@dimen/small_icon_size"
android:layout_marginStart="@{model.lastMessageTextSender.length() > 0 ? @dimen/five : @dimen/zero}"
android:layout_marginEnd="5dp"
android:layout_marginTop="3dp"
android:src="@{model.lastMessageContentIcon, default=@drawable/forward}"
android:visibility="@{model.lastMessageContentIcon > 0 &amp;&amp; !model.isComposing ? View.VISIBLE : View.GONE}"
android:src="@{model.isComposing ? model.composingIcon : model.lastMessageContentIcon, default=@drawable/forward}"
android:visibility="@{model.lastMessageContentIcon > 0 || model.isComposing ? View.VISIBLE : View.GONE}"
android:contentDescription="@null"
app:tint="?attr/color_main2_600"
app:layout_constraintStart_toEndOf="@id/last_message_sender"
@ -126,7 +126,7 @@
android:textSize="14sp"
android:textColor="?attr/color_main2_500"
textFont="@{model.isBeingDeleted || model.unreadMessageCount > 0 || model.isComposing ? NotoSansFont.NotoSansBold : NotoSansFont.NotoSansRegular}"
app:layout_constraintStart_toEndOf="@id/last_message_forward"
app:layout_constraintStart_toEndOf="@id/last_message_icon"
app:layout_constraintEnd_toStartOf="@id/right_border"
app:layout_constraintTop_toTopOf="@id/last_message_sender"
app:layout_constraintBottom_toBottomOf="@id/last_message_sender" />

View file

@ -490,6 +490,10 @@
<item quantity="one">%s est en train d\'écrire…</item>
<item quantity="other">%s sont en train d\'écrire…</item>
</plurals>
<plurals name="conversation_composing_voice_message_label">
<item quantity="one">%s est en train d\'enregistrer un message vocal…</item>
<item quantity="other">%s sont en train d\'enregistrer un message vocal…</item>
</plurals>
<string name="conversation_add_participants_title">Ajouter des participants</string>
<string name="conversation_reply_to_message_title">En réponse à :</string>
<string name="conversation_menu_search_in_messages">Chercher</string>

View file

@ -533,6 +533,10 @@
<item quantity="one">%s is composing…</item>
<item quantity="other">%s are composing…</item>
</plurals>
<plurals name="conversation_composing_voice_message_label">
<item quantity="one">%s is recording a voice message…</item>
<item quantity="other">%s are recording a voice message…</item>
</plurals>
<string name="conversation_add_participants_title">Add participants</string>
<string name="conversation_reply_to_message_title">Replying to:</string>
<string name="conversation_menu_search_in_messages">Search</string>