mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-17 03:18:06 +00:00
Filter participants list using user input after '@'
This commit is contained in:
parent
b88b6a8093
commit
50aa053c19
8 changed files with 102 additions and 17 deletions
|
|
@ -21,6 +21,7 @@ Group changes to describe their impact on the project, as follows:
|
|||
- Support right click on some items to open bottom sheet/menu
|
||||
- Added toggle speaker action in active call notification
|
||||
- Increased text size for chat messages that only contains emoji(s)
|
||||
- Use user-input to filter participants list after typing "@" in conversation send area
|
||||
- Handle read-only CardDAV address books, disable edit/delete menus for contacts in read-only FriendList
|
||||
- Added swipe/pull to refresh on contacts list of a CardDAV addressbook has been configured to force the synchronization
|
||||
- Show information to user when filtering contacts doesn't show them all and user may have to refine it's search
|
||||
|
|
@ -52,6 +53,9 @@ Group changes to describe their impact on the project, as follows:
|
|||
- Added more info into StartupListener logs
|
||||
- Updated password forgotten procedure, will use online account manager platform
|
||||
|
||||
### Fixed
|
||||
- Copy raw message content instead of modified one when it contains a participant mention ("@username")
|
||||
|
||||
## [6.0.21] - 2025-12-16
|
||||
|
||||
### Added
|
||||
|
|
|
|||
|
|
@ -283,13 +283,22 @@ open class ConversationFragment : SlidingPaneChildFragment() {
|
|||
|
||||
override fun afterTextChanged(editable: Editable?) {
|
||||
if (viewModel.isGroup.value == true) {
|
||||
sendMessageViewModel.closeParticipantsList()
|
||||
|
||||
val split = editable.toString().split(" ")
|
||||
for (part in split) {
|
||||
if (part == "@") {
|
||||
Log.i("$TAG '@' found, opening participants list")
|
||||
sendMessageViewModel.openParticipantsList()
|
||||
if (split.isNotEmpty()) {
|
||||
val lastPart = split.last()
|
||||
if (lastPart.isNotEmpty() && lastPart.startsWith("@")) {
|
||||
coreContext.postOnCoreThread {
|
||||
val filter = if (lastPart.length > 1) lastPart.substring(1) else ""
|
||||
sendMessageViewModel.filterParticipantsList(filter)
|
||||
}
|
||||
|
||||
if (sendMessageViewModel.isParticipantsListOpen.value == false) {
|
||||
Log.i("$TAG '@' found, opening participants list")
|
||||
sendMessageViewModel.openParticipantsList()
|
||||
}
|
||||
} else if (sendMessageViewModel.isParticipantsListOpen.value == true) {
|
||||
Log.i("$TAG Closing participants list")
|
||||
sendMessageViewModel.closeParticipantsList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import android.text.Spannable
|
|||
import android.text.SpannableStringBuilder
|
||||
import android.text.Spanned
|
||||
import android.text.style.StyleSpan
|
||||
import androidx.annotation.AnyThread
|
||||
import androidx.annotation.UiThread
|
||||
import androidx.annotation.WorkerThread
|
||||
import androidx.lifecycle.MediatorLiveData
|
||||
|
|
@ -151,6 +152,8 @@ class MessageModel
|
|||
|
||||
val isSelected = MutableLiveData<Boolean>()
|
||||
|
||||
private var rawTextContent: String = ""
|
||||
|
||||
// Below are for conferences info
|
||||
val meetingFound = MutableLiveData<Boolean>()
|
||||
|
||||
|
|
@ -436,6 +439,11 @@ class MessageModel
|
|||
avatarModel.postValue(avatar)
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
fun getRawTextContent(): String {
|
||||
return rawTextContent
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun computeContentsList() {
|
||||
Log.d("$TAG Computing message contents list")
|
||||
|
|
@ -686,10 +694,10 @@ class MessageModel
|
|||
|
||||
@WorkerThread
|
||||
private fun computeTextContent(content: Content, highlight: String) {
|
||||
val textContent = content.utf8Text.orEmpty().trim()
|
||||
val spannableBuilder = SpannableStringBuilder(textContent)
|
||||
rawTextContent = content.utf8Text.orEmpty().trim()
|
||||
val spannableBuilder = SpannableStringBuilder(rawTextContent)
|
||||
|
||||
val emojiOnly = AppUtils.isTextOnlyContainsEmoji(textContent)
|
||||
val emojiOnly = AppUtils.isTextOnlyContainsEmoji(rawTextContent)
|
||||
isTextEmoji.postValue(emojiOnly)
|
||||
if (emojiOnly) {
|
||||
text.postValue(spannableBuilder)
|
||||
|
|
@ -698,7 +706,7 @@ class MessageModel
|
|||
|
||||
// Check for search
|
||||
if (highlight.isNotEmpty()) {
|
||||
val indexStart = textContent.indexOf(highlight, 0, ignoreCase = true)
|
||||
val indexStart = rawTextContent.indexOf(highlight, 0, ignoreCase = true)
|
||||
if (indexStart >= 0) {
|
||||
isTextHighlighted = true
|
||||
val indexEnd = indexStart + highlight.length
|
||||
|
|
@ -713,12 +721,12 @@ class MessageModel
|
|||
|
||||
// Check for mentions
|
||||
val chatRoom = chatMessage.chatRoom
|
||||
val matcher = Pattern.compile(MENTION_REGEXP).matcher(textContent)
|
||||
val matcher = Pattern.compile(MENTION_REGEXP).matcher(rawTextContent)
|
||||
var offset = 0
|
||||
while (matcher.find()) {
|
||||
val start = matcher.start()
|
||||
val end = matcher.end()
|
||||
val source = textContent.subSequence(start + 1, end) // +1 to remove @
|
||||
val source = rawTextContent.subSequence(start + 1, end) // +1 to remove @
|
||||
Log.d("$TAG Found mention [$source]")
|
||||
|
||||
// Find address matching username
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ class ChatMessageLongPressViewModel : GenericViewModel() {
|
|||
fun copyClickListener() {
|
||||
Log.i("$TAG Copying message text into clipboard")
|
||||
|
||||
val text = messageModel.value?.text?.value?.toString()
|
||||
val text = messageModel.value?.getRawTextContent()
|
||||
val clipboard = coreContext.context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val label = "Message"
|
||||
clipboard.setPrimaryClip(ClipData.newPlainText(label, text))
|
||||
|
|
|
|||
|
|
@ -110,6 +110,8 @@ class SendMessageInConversationViewModel
|
|||
|
||||
val voiceRecordPlayerPosition = MutableLiveData<Int>()
|
||||
|
||||
val isComputingParticipantsList = MutableLiveData<Boolean>()
|
||||
|
||||
private lateinit var voiceRecordPlayer: Player
|
||||
|
||||
private val playerListener = PlayerListener {
|
||||
|
|
@ -143,6 +145,8 @@ class SendMessageInConversationViewModel
|
|||
|
||||
private var voiceRecordAudioFocusRequest: AudioFocusRequestCompat? = null
|
||||
|
||||
private var participantsListFilter = ""
|
||||
|
||||
private val chatRoomListener = object : ChatRoomListenerStub() {
|
||||
@WorkerThread
|
||||
override fun onParticipantAdded(chatRoom: ChatRoom, eventLog: EventLog) {
|
||||
|
|
@ -163,6 +167,7 @@ class SendMessageInConversationViewModel
|
|||
isKeyboardOpen.value = false
|
||||
isEmojiPickerOpen.value = false
|
||||
areFilePickersOpen.value = false
|
||||
isParticipantsListOpen.value = false
|
||||
isVoiceRecording.value = false
|
||||
isPlayingVoiceRecord.value = false
|
||||
isCallConversation.value = false
|
||||
|
|
@ -409,6 +414,10 @@ class SendMessageInConversationViewModel
|
|||
@UiThread
|
||||
fun closeParticipantsList() {
|
||||
isParticipantsListOpen.value = false
|
||||
coreContext.postOnCoreThread {
|
||||
participantsListFilter = ""
|
||||
computeParticipantsList()
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
|
|
@ -571,7 +580,32 @@ class SendMessageInConversationViewModel
|
|||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun computeParticipantsList() {
|
||||
fun filterParticipantsList(filter: String) {
|
||||
Log.i("$TAG Filtering participants list using user-input [$filter]")
|
||||
if (filter.isEmpty() && participantsListFilter.isNotEmpty()) {
|
||||
participantsListFilter = ""
|
||||
computeParticipantsList()
|
||||
return
|
||||
}
|
||||
|
||||
if (filter.length >= participantsListFilter.length) {
|
||||
isComputingParticipantsList.postValue(true)
|
||||
participantsListFilter = filter
|
||||
val currentList = participants.value.orEmpty()
|
||||
val newList = currentList.filter {
|
||||
it.address.asStringUriOnly().contains(filter) || it.avatarModel.contactName?.contains(filter) == true
|
||||
}
|
||||
participants.postValue(newList as ArrayList<ParticipantModel>)
|
||||
isComputingParticipantsList.postValue(false)
|
||||
} else {
|
||||
participantsListFilter = filter
|
||||
computeParticipantsList(filter)
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun computeParticipantsList(filter: String = "") {
|
||||
isComputingParticipantsList.postValue(true)
|
||||
val participantsList = arrayListOf<ParticipantModel>()
|
||||
|
||||
for (participant in chatRoom.participants) {
|
||||
|
|
@ -580,14 +614,18 @@ class SendMessageInConversationViewModel
|
|||
coreContext.postOnCoreThread {
|
||||
val username = clicked.address.username
|
||||
if (!username.isNullOrEmpty()) {
|
||||
participantUsernameToAddEvent.postValue(Event(username))
|
||||
participantUsernameToAddEvent.postValue(Event(username.substring(participantsListFilter.length)))
|
||||
}
|
||||
}
|
||||
})
|
||||
participantsList.add(model)
|
||||
|
||||
if (filter.isEmpty() || participant.address.asStringUriOnly().contains(filter) || model.avatarModel.contactName?.contains(filter) == true) {
|
||||
participantsList.add(model)
|
||||
}
|
||||
}
|
||||
|
||||
participants.postValue(participantsList)
|
||||
isComputingParticipantsList.postValue(false)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
|
|
|
|||
|
|
@ -22,6 +22,18 @@
|
|||
android:importantForAccessibility="no"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/default_text_style"
|
||||
android:id="@+id/participants_header"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:text="@string/conversation_participants_list_header"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?attr/color_main2_500"
|
||||
app:layout_constraintTop_toBottomOf="@id/participants_separator"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/participants"
|
||||
android:layout_width="0dp"
|
||||
|
|
@ -29,7 +41,7 @@
|
|||
app:layout_constraintHeight_max="@dimen/chat_room_participants_list_max_height"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/participants_separator">
|
||||
app:layout_constraintTop_toBottomOf="@id/participants_header">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -41,6 +53,18 @@
|
|||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<com.google.android.material.progressindicator.CircularProgressIndicator
|
||||
android:id="@+id/fetch_in_progress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:indeterminate="true"
|
||||
android:visibility="@{viewModel.isComputingParticipantsList ? View.VISIBLE : View.GONE}"
|
||||
app:indicatorColor="?attr/color_main1_500"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/participants"
|
||||
app:layout_constraintBottom_toBottomOf="@id/participants" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/participants_close"
|
||||
android:onClick="@{() -> viewModel.closeParticipantsList()}"
|
||||
|
|
|
|||
|
|
@ -566,6 +566,7 @@
|
|||
<string name="conversation_dialog_delete_for_everyone_label">Pour tout le monde</string>
|
||||
<string name="conversation_message_content_deleted_label"><i>Le message a été supprimé</i></string>
|
||||
<string name="conversation_message_content_deleted_by_us_label"><i>Vous avez supprimé le message</i></string>
|
||||
<string name="conversation_participants_list_header">Participants</string>
|
||||
|
||||
<string name="conversation_info_participants_list_title">Participants (%s)</string>
|
||||
<string name="conversation_info_add_participants_label">Ajouter des participants</string>
|
||||
|
|
|
|||
|
|
@ -609,6 +609,7 @@
|
|||
<string name="conversation_dialog_delete_for_everyone_label">For everyone</string>
|
||||
<string name="conversation_message_content_deleted_label"><i>This message has been deleted</i></string>
|
||||
<string name="conversation_message_content_deleted_by_us_label"><i>You have deleted this message</i></string>
|
||||
<string name="conversation_participants_list_header">Participants</string>
|
||||
|
||||
<string name="conversation_info_participants_list_title">Group members (%s)</string>
|
||||
<string name="conversation_info_add_participants_label">Add participants</string>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue