mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-17 11:28:06 +00:00
Fixed chat rooms list sort order + added mentions menu when typing '@' + hide participants in non-group conversation
This commit is contained in:
parent
d895fc6a09
commit
dab462de35
8 changed files with 120 additions and 8 deletions
|
|
@ -30,6 +30,8 @@ import android.graphics.drawable.ColorDrawable
|
|||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
|
@ -39,6 +41,7 @@ import androidx.activity.result.PickVisualMediaRequest
|
|||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.annotation.UiThread
|
||||
import androidx.core.view.doOnPreDraw
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
|
|
@ -70,6 +73,7 @@ import org.linphone.ui.main.fragment.GenericFragment
|
|||
import org.linphone.utils.AppUtils
|
||||
import org.linphone.utils.Event
|
||||
import org.linphone.utils.LinphoneUtils
|
||||
import org.linphone.utils.addCharacterAtPosition
|
||||
import org.linphone.utils.hideKeyboard
|
||||
import org.linphone.utils.setKeyboardInsetListener
|
||||
import org.linphone.utils.showKeyboard
|
||||
|
|
@ -114,6 +118,25 @@ class ConversationFragment : GenericFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private val textObserver = object : TextWatcher {
|
||||
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
|
||||
}
|
||||
|
||||
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
|
||||
}
|
||||
|
||||
override fun afterTextChanged(p0: Editable?) {
|
||||
viewModel.isParticipantsListOpen.value = false
|
||||
|
||||
val split = p0.toString().split(" ")
|
||||
for (part in split) {
|
||||
if (part == "@") {
|
||||
viewModel.isParticipantsListOpen.value = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
|
|
@ -262,6 +285,14 @@ class ConversationFragment : GenericFragment() {
|
|||
findNavController().navigate(action)
|
||||
}
|
||||
|
||||
viewModel.participantUsernameToAddEvent.observe(viewLifecycleOwner) {
|
||||
it.consume { username ->
|
||||
Log.i("$TAG Adding username [$username] after '@'")
|
||||
// Also add a space for convenience
|
||||
binding.sendArea.messageToSend.addCharacterAtPosition("$username ")
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.searchFilter.observe(viewLifecycleOwner) { filter ->
|
||||
viewModel.applyFilter(filter.trim())
|
||||
}
|
||||
|
|
@ -337,9 +368,17 @@ class ConversationFragment : GenericFragment() {
|
|||
} catch (e: IllegalStateException) {
|
||||
Log.e("$TAG Failed to register data observer to adapter: $e")
|
||||
}
|
||||
|
||||
if (viewModel.isGroup.value == true) {
|
||||
binding.sendArea.messageToSend.addTextChangedListener(textObserver)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
if (viewModel.isGroup.value == true) {
|
||||
binding.sendArea.messageToSend.removeTextChangedListener(textObserver)
|
||||
}
|
||||
|
||||
try {
|
||||
adapter.unregisterAdapterDataObserver(dataObserver)
|
||||
} catch (e: IllegalStateException) {
|
||||
|
|
|
|||
|
|
@ -27,14 +27,20 @@ import org.linphone.core.Address
|
|||
|
||||
class ParticipantModel @WorkerThread constructor(
|
||||
val address: Address,
|
||||
val isMyselfAdmin: Boolean,
|
||||
val isParticipantAdmin: Boolean,
|
||||
val isMyselfAdmin: Boolean = false,
|
||||
val isParticipantAdmin: Boolean = false,
|
||||
private val onClicked: ((model: ParticipantModel) -> Unit)? = null,
|
||||
private val onMenuClicked: ((view: View, model: ParticipantModel) -> Unit)? = null
|
||||
) {
|
||||
val sipUri = address.asStringUriOnly()
|
||||
|
||||
val avatarModel = coreContext.contactsManager.getContactAvatarModelForAddress(address)
|
||||
|
||||
@UiThread
|
||||
fun onClicked() {
|
||||
onClicked?.invoke(this)
|
||||
}
|
||||
|
||||
@UiThread
|
||||
fun openMenu(view: View) {
|
||||
onMenuClicked?.invoke(view, this)
|
||||
|
|
|
|||
|
|
@ -362,10 +362,10 @@ class ConversationInfoViewModel @UiThread constructor() : ViewModel() {
|
|||
} else {
|
||||
for (participant in chatRoom.participants) {
|
||||
val isParticipantAdmin = if (groupChatRoom) participant.isAdmin else false
|
||||
val model = ParticipantModel(participant.address, selfAdmin, isParticipantAdmin) { view, model ->
|
||||
val model = ParticipantModel(participant.address, selfAdmin, isParticipantAdmin, onMenuClicked = { view, model ->
|
||||
// openMenu
|
||||
showParticipantAdminPopupMenuEvent.postValue(Event(Pair(view, model)))
|
||||
}
|
||||
})
|
||||
friends.add(model.avatarModel.friend)
|
||||
participantsList.add(model)
|
||||
}
|
||||
|
|
@ -374,6 +374,7 @@ class ConversationInfoViewModel @UiThread constructor() : ViewModel() {
|
|||
val avatar = if (groupChatRoom) {
|
||||
val fakeFriend = coreContext.core.createFriend()
|
||||
val model = ContactAvatarModel(fakeFriend)
|
||||
model.defaultToConferenceIcon.postValue(true)
|
||||
model.setPicturesFromFriends(friends)
|
||||
model
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import org.linphone.core.Friend
|
|||
import org.linphone.core.tools.Log
|
||||
import org.linphone.ui.main.chat.model.ChatMessageModel
|
||||
import org.linphone.ui.main.chat.model.EventLogModel
|
||||
import org.linphone.ui.main.chat.model.ParticipantModel
|
||||
import org.linphone.ui.main.contacts.model.ContactAvatarModel
|
||||
import org.linphone.utils.AppUtils
|
||||
import org.linphone.utils.Event
|
||||
|
|
@ -70,6 +71,14 @@ class ConversationViewModel @UiThread constructor() : ViewModel() {
|
|||
|
||||
val isEmojiPickerOpen = MutableLiveData<Boolean>()
|
||||
|
||||
val isParticipantsListOpen = MutableLiveData<Boolean>()
|
||||
|
||||
val participants = MutableLiveData<ArrayList<ParticipantModel>>()
|
||||
|
||||
val participantUsernameToAddEvent: MutableLiveData<Event<String>> by lazy {
|
||||
MutableLiveData<Event<String>>()
|
||||
}
|
||||
|
||||
val isReplying = MutableLiveData<Boolean>()
|
||||
|
||||
val isReplyingTo = MutableLiveData<String>()
|
||||
|
|
@ -181,6 +190,16 @@ class ConversationViewModel @UiThread constructor() : ViewModel() {
|
|||
|
||||
events.postValue(list)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
override fun onParticipantAdded(chatRoom: ChatRoom, eventLog: EventLog) {
|
||||
computeParticipantsList()
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
override fun onParticipantRemoved(chatRoom: ChatRoom, eventLog: EventLog) {
|
||||
computeParticipantsList()
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
|
|
@ -403,6 +422,7 @@ class ConversationViewModel @UiThread constructor() : ViewModel() {
|
|||
|
||||
computeEvents()
|
||||
chatRoom.markAsRead()
|
||||
computeParticipantsList()
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
|
|
@ -538,4 +558,24 @@ class ConversationViewModel @UiThread constructor() : ViewModel() {
|
|||
composingLabel.postValue("")
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun computeParticipantsList() {
|
||||
val participantsList = arrayListOf<ParticipantModel>()
|
||||
|
||||
for (participant in chatRoom.participants) {
|
||||
val model = ParticipantModel(participant.address, onClicked = { clicked ->
|
||||
Log.i("$TAG Clicked on participant [${clicked.sipUri}]")
|
||||
coreContext.postOnCoreThread {
|
||||
val username = clicked.address.username
|
||||
if (!username.isNullOrEmpty()) {
|
||||
participantUsernameToAddEvent.postValue(Event(username))
|
||||
}
|
||||
}
|
||||
})
|
||||
participantsList.add(model)
|
||||
}
|
||||
|
||||
participants.postValue(participantsList)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ class ConversationsListViewModel @UiThread constructor() : AbstractTopBarViewMod
|
|||
Log.i("$TAG Re-ordering chat rooms")
|
||||
val sortedList = arrayListOf<ConversationModel>()
|
||||
sortedList.addAll(conversations.value.orEmpty())
|
||||
sortedList.sortBy {
|
||||
sortedList.sortByDescending {
|
||||
it.lastUpdateTime.value
|
||||
}
|
||||
conversations.postValue(sortedList)
|
||||
|
|
|
|||
|
|
@ -223,10 +223,31 @@
|
|||
android:textColor="@color/gray_main2_400"
|
||||
android:visibility="@{viewModel.composingLabel.length() == 0 ? View.GONE : View.VISIBLE}"
|
||||
app:layout_constraintTop_toBottomOf="@id/events_list"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/participants"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/participants"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/shape_squircle_gray_100_background"
|
||||
android:visibility="@{viewModel.isParticipantsListOpen ? View.VISIBLE : View.GONE, default=gone}"
|
||||
app:layout_constraintHeight_max="300dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dp"
|
||||
android:orientation="vertical"
|
||||
entries="@{viewModel.participants}"
|
||||
layout="@{@layout/chat_participant_list_cell}"/>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<include
|
||||
|
|
|
|||
|
|
@ -204,6 +204,7 @@
|
|||
android:text="@string/conversation_info_participants_list_title"
|
||||
android:drawableEnd="@{viewModel.expandParticipants ? @drawable/caret_up : @drawable/caret_down, default=@drawable/caret_up}"
|
||||
android:drawableTint="@color/gray_main2_600"
|
||||
android:visibility="@{viewModel.isGroup ? View.VISIBLE : View.GONE}"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/mute_label"/>
|
||||
|
|
@ -215,6 +216,7 @@
|
|||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:src="@drawable/shape_squircle_white_background"
|
||||
android:visibility="@{viewModel.isGroup ? View.VISIBLE : View.GONE}"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/participants"
|
||||
|
|
@ -222,13 +224,14 @@
|
|||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/participants"
|
||||
android:visibility="@{viewModel.expandParticipants ? View.VISIBLE : View.GONE}"
|
||||
android:visibility="@{viewModel.expandParticipants && viewModel.isGroup ? View.VISIBLE : View.GONE}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:layout_constraintHeight_max="300dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/participants_label"
|
||||
|
|
@ -240,6 +243,7 @@
|
|||
android:onClick="@{addParticipantsClickListener}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/tertiary_button_background"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:onClick="@{() -> model.onClicked()}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/primary_cell_background"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue