Reworked how chat rooms list works, should speed first display but slow down a bit the scrolling
This commit is contained in:
parent
0e0fd6106c
commit
c5a34363a3
9 changed files with 289 additions and 159 deletions
|
@ -28,17 +28,16 @@ import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import org.linphone.R
|
import org.linphone.R
|
||||||
import org.linphone.activities.main.adapters.SelectionListAdapter
|
import org.linphone.activities.main.adapters.SelectionListAdapter
|
||||||
import org.linphone.activities.main.chat.viewmodels.ChatRoomViewModel
|
import org.linphone.activities.main.chat.data.ChatRoomData
|
||||||
import org.linphone.activities.main.viewmodels.ListTopBarViewModel
|
import org.linphone.activities.main.viewmodels.ListTopBarViewModel
|
||||||
import org.linphone.core.ChatRoom
|
import org.linphone.core.ChatRoom
|
||||||
import org.linphone.databinding.ChatRoomListCellBinding
|
import org.linphone.databinding.ChatRoomListCellBinding
|
||||||
import org.linphone.utils.Event
|
import org.linphone.utils.Event
|
||||||
import org.linphone.utils.LinphoneUtils
|
|
||||||
|
|
||||||
class ChatRoomsListAdapter(
|
class ChatRoomsListAdapter(
|
||||||
selectionVM: ListTopBarViewModel,
|
selectionVM: ListTopBarViewModel,
|
||||||
private val viewLifecycleOwner: LifecycleOwner
|
private val viewLifecycleOwner: LifecycleOwner
|
||||||
) : SelectionListAdapter<ChatRoomViewModel, RecyclerView.ViewHolder>(selectionVM, ChatRoomDiffCallback()) {
|
) : SelectionListAdapter<ChatRoom, RecyclerView.ViewHolder>(selectionVM, ChatRoomDiffCallback()) {
|
||||||
val selectedChatRoomEvent: MutableLiveData<Event<ChatRoom>> by lazy {
|
val selectedChatRoomEvent: MutableLiveData<Event<ChatRoom>> by lazy {
|
||||||
MutableLiveData<Event<ChatRoom>>()
|
MutableLiveData<Event<ChatRoom>>()
|
||||||
}
|
}
|
||||||
|
@ -65,9 +64,9 @@ class ChatRoomsListAdapter(
|
||||||
inner class ViewHolder(
|
inner class ViewHolder(
|
||||||
private val binding: ChatRoomListCellBinding
|
private val binding: ChatRoomListCellBinding
|
||||||
) : RecyclerView.ViewHolder(binding.root) {
|
) : RecyclerView.ViewHolder(binding.root) {
|
||||||
fun bind(chatRoomViewModel: ChatRoomViewModel) {
|
fun bind(chatRoom: ChatRoom) {
|
||||||
with(binding) {
|
with(binding) {
|
||||||
viewModel = chatRoomViewModel
|
data = ChatRoomData(chatRoom)
|
||||||
|
|
||||||
lifecycleOwner = viewLifecycleOwner
|
lifecycleOwner = viewLifecycleOwner
|
||||||
|
|
||||||
|
@ -85,7 +84,7 @@ class ChatRoomsListAdapter(
|
||||||
if (selectionViewModel.isEditionEnabled.value == true) {
|
if (selectionViewModel.isEditionEnabled.value == true) {
|
||||||
selectionViewModel.onToggleSelect(bindingAdapterPosition)
|
selectionViewModel.onToggleSelect(bindingAdapterPosition)
|
||||||
} else {
|
} else {
|
||||||
selectedChatRoomEvent.value = Event(chatRoomViewModel.chatRoom)
|
selectedChatRoomEvent.value = Event(chatRoom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,18 +103,18 @@ class ChatRoomsListAdapter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ChatRoomDiffCallback : DiffUtil.ItemCallback<ChatRoomViewModel>() {
|
private class ChatRoomDiffCallback : DiffUtil.ItemCallback<ChatRoom>() {
|
||||||
override fun areItemsTheSame(
|
override fun areItemsTheSame(
|
||||||
oldItem: ChatRoomViewModel,
|
oldItem: ChatRoom,
|
||||||
newItem: ChatRoomViewModel
|
newItem: ChatRoom
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return LinphoneUtils.areChatRoomsTheSame(oldItem.chatRoom, newItem.chatRoom)
|
return oldItem == newItem
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun areContentsTheSame(
|
override fun areContentsTheSame(
|
||||||
oldItem: ChatRoomViewModel,
|
oldItem: ChatRoom,
|
||||||
newItem: ChatRoomViewModel
|
newItem: ChatRoom
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return true
|
return false // To force redraw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,182 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2022 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This file is part of linphone-android
|
||||||
|
* (see https://www.linphone.org).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.linphone.activities.main.chat.data
|
||||||
|
|
||||||
|
import android.graphics.Typeface
|
||||||
|
import android.text.SpannableStringBuilder
|
||||||
|
import android.text.style.StyleSpan
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||||
|
import org.linphone.LinphoneApplication.Companion.corePreferences
|
||||||
|
import org.linphone.R
|
||||||
|
import org.linphone.contact.ContactDataInterface
|
||||||
|
import org.linphone.core.*
|
||||||
|
import org.linphone.core.tools.Log
|
||||||
|
import org.linphone.utils.AppUtils
|
||||||
|
import org.linphone.utils.LinphoneUtils
|
||||||
|
import org.linphone.utils.TimestampUtils
|
||||||
|
|
||||||
|
class ChatRoomData(private val chatRoom: ChatRoom) : ContactDataInterface {
|
||||||
|
override val contact: MutableLiveData<Friend> = MutableLiveData<Friend>()
|
||||||
|
override val displayName: MutableLiveData<String> = MutableLiveData<String>()
|
||||||
|
override val securityLevel: MutableLiveData<ChatRoomSecurityLevel> = MutableLiveData<ChatRoomSecurityLevel>()
|
||||||
|
override val showGroupChatAvatar: Boolean
|
||||||
|
get() = conferenceChatRoom && !oneToOneChatRoom
|
||||||
|
override val coroutineScope: CoroutineScope = coreContext.coroutineScope
|
||||||
|
|
||||||
|
val unreadMessagesCount = MutableLiveData<Int>()
|
||||||
|
|
||||||
|
val subject = MutableLiveData<String>()
|
||||||
|
|
||||||
|
val securityLevelIcon = MutableLiveData<Int>()
|
||||||
|
|
||||||
|
val securityLevelContentDescription = MutableLiveData<Int>()
|
||||||
|
|
||||||
|
val ephemeralEnabled = MutableLiveData<Boolean>()
|
||||||
|
|
||||||
|
val lastUpdate = MutableLiveData<String>()
|
||||||
|
|
||||||
|
val lastMessageText = MutableLiveData<SpannableStringBuilder>()
|
||||||
|
|
||||||
|
val notificationsMuted = MutableLiveData<Boolean>()
|
||||||
|
|
||||||
|
private val basicChatRoom: Boolean by lazy {
|
||||||
|
chatRoom.hasCapability(ChatRoomCapabilities.Basic.toInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
val oneToOneChatRoom: Boolean by lazy {
|
||||||
|
chatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
private val conferenceChatRoom: Boolean by lazy {
|
||||||
|
chatRoom.hasCapability(ChatRoomCapabilities.Conference.toInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
val encryptedChatRoom: Boolean by lazy {
|
||||||
|
chatRoom.hasCapability(ChatRoomCapabilities.Encrypted.toInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
unreadMessagesCount.value = chatRoom.unreadMessagesCount
|
||||||
|
|
||||||
|
subject.value = chatRoom.subject
|
||||||
|
updateSecurityIcon()
|
||||||
|
ephemeralEnabled.value = chatRoom.isEphemeralEnabled
|
||||||
|
|
||||||
|
contactLookup()
|
||||||
|
formatLastMessage(chatRoom.lastMessageInHistory)
|
||||||
|
|
||||||
|
notificationsMuted.value = areNotificationsMuted()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateSecurityIcon() {
|
||||||
|
val level = chatRoom.securityLevel
|
||||||
|
securityLevel.value = level
|
||||||
|
|
||||||
|
securityLevelIcon.value = when (level) {
|
||||||
|
ChatRoomSecurityLevel.Safe -> R.drawable.security_2_indicator
|
||||||
|
ChatRoomSecurityLevel.Encrypted -> R.drawable.security_1_indicator
|
||||||
|
else -> R.drawable.security_alert_indicator
|
||||||
|
}
|
||||||
|
securityLevelContentDescription.value = when (level) {
|
||||||
|
ChatRoomSecurityLevel.Safe -> R.string.content_description_security_level_safe
|
||||||
|
ChatRoomSecurityLevel.Encrypted -> R.string.content_description_security_level_encrypted
|
||||||
|
else -> R.string.content_description_security_level_unsafe
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun contactLookup() {
|
||||||
|
displayName.value = when {
|
||||||
|
basicChatRoom -> LinphoneUtils.getDisplayName(
|
||||||
|
chatRoom.peerAddress
|
||||||
|
)
|
||||||
|
oneToOneChatRoom -> LinphoneUtils.getDisplayName(
|
||||||
|
chatRoom.participants.firstOrNull()?.address ?: chatRoom.peerAddress
|
||||||
|
)
|
||||||
|
conferenceChatRoom -> chatRoom.subject.orEmpty()
|
||||||
|
else -> chatRoom.peerAddress.asStringUriOnly()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oneToOneChatRoom) {
|
||||||
|
searchMatchingContact()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun searchMatchingContact() {
|
||||||
|
val remoteAddress = if (basicChatRoom) {
|
||||||
|
chatRoom.peerAddress
|
||||||
|
} else {
|
||||||
|
if (chatRoom.participants.isNotEmpty()) {
|
||||||
|
chatRoom.participants[0].address
|
||||||
|
} else {
|
||||||
|
Log.e("[Chat Room] $chatRoom doesn't have any participant in state ${chatRoom.state}!")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (remoteAddress != null) {
|
||||||
|
contact.value = coreContext.contactsManager.findContactByAddress(remoteAddress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun formatLastMessage(msg: ChatMessage?) {
|
||||||
|
val lastUpdateTime = chatRoom.lastUpdateTime
|
||||||
|
coroutineScope.launch {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
lastUpdate.postValue(TimestampUtils.toString(lastUpdateTime, true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val builder = SpannableStringBuilder()
|
||||||
|
if (msg == null) {
|
||||||
|
lastMessageText.value = builder
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val sender: String =
|
||||||
|
coreContext.contactsManager.findContactByAddress(msg.fromAddress)?.name
|
||||||
|
?: LinphoneUtils.getDisplayName(msg.fromAddress)
|
||||||
|
builder.append(sender)
|
||||||
|
builder.append(": ")
|
||||||
|
|
||||||
|
for (content in msg.contents) {
|
||||||
|
if (content.isIcalendar) {
|
||||||
|
val body = AppUtils.getString(R.string.conference_invitation)
|
||||||
|
builder.append(body)
|
||||||
|
builder.setSpan(StyleSpan(Typeface.ITALIC), builder.length - body.length, builder.length, 0)
|
||||||
|
} else if (content.isFile || content.isFileTransfer) {
|
||||||
|
builder.append(content.name + " ")
|
||||||
|
} else if (content.isText) {
|
||||||
|
builder.append(content.utf8Text + " ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.trim()
|
||||||
|
lastMessageText.value = builder
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun areNotificationsMuted(): Boolean {
|
||||||
|
val id = LinphoneUtils.getChatRoomId(chatRoom.localAddress, chatRoom.peerAddress)
|
||||||
|
return corePreferences.chatRoomMuted(id)
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,7 +19,6 @@
|
||||||
*/
|
*/
|
||||||
package org.linphone.activities.main.chat.fragments
|
package org.linphone.activities.main.chat.fragments
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
@ -175,16 +174,7 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
|
||||||
|
|
||||||
isSecure = chatRoom.currentParams.isEncryptionEnabled
|
isSecure = chatRoom.currentParams.isEncryptionEnabled
|
||||||
|
|
||||||
val chatRoomsListViewModel: ChatRoomsListViewModel = requireActivity().run {
|
viewModel = ViewModelProvider(
|
||||||
ViewModelProvider(this)[ChatRoomsListViewModel::class.java]
|
|
||||||
}
|
|
||||||
val chatRoomViewModel = chatRoomsListViewModel.chatRooms.value.orEmpty().find {
|
|
||||||
it.chatRoom == chatRoom
|
|
||||||
}
|
|
||||||
if (chatRoomViewModel == null) {
|
|
||||||
Log.w("[Chat Room] Couldn't find existing view model, will create a new one!")
|
|
||||||
}
|
|
||||||
viewModel = chatRoomViewModel ?: ViewModelProvider(
|
|
||||||
this,
|
this,
|
||||||
ChatRoomViewModelFactory(chatRoom)
|
ChatRoomViewModelFactory(chatRoom)
|
||||||
)[ChatRoomViewModel::class.java]
|
)[ChatRoomViewModel::class.java]
|
||||||
|
@ -360,7 +350,7 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
|
||||||
) {
|
) {
|
||||||
it.consume { chatMessage ->
|
it.consume { chatMessage ->
|
||||||
listViewModel.deleteMessage(chatMessage)
|
listViewModel.deleteMessage(chatMessage)
|
||||||
viewModel.updateLastMessageToDisplay()
|
sharedViewModel.refreshChatRoomInListEvent.value = Event(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -677,7 +667,7 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
|
||||||
list.add(eventLog)
|
list.add(eventLog)
|
||||||
}
|
}
|
||||||
listViewModel.deleteEventLogs(list)
|
listViewModel.deleteEventLogs(list)
|
||||||
viewModel.updateLastMessageToDisplay()
|
sharedViewModel.refreshChatRoomInListEvent.value = Event(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(
|
override fun onRequestPermissionsResult(
|
||||||
|
@ -934,10 +924,12 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
|
||||||
}
|
}
|
||||||
popupView.setMuteListener {
|
popupView.setMuteListener {
|
||||||
viewModel.muteNotifications(true)
|
viewModel.muteNotifications(true)
|
||||||
|
sharedViewModel.refreshChatRoomInListEvent.value = Event(true)
|
||||||
popupWindow.dismiss()
|
popupWindow.dismiss()
|
||||||
}
|
}
|
||||||
popupView.setUnmuteListener {
|
popupView.setUnmuteListener {
|
||||||
viewModel.muteNotifications(false)
|
viewModel.muteNotifications(false)
|
||||||
|
sharedViewModel.refreshChatRoomInListEvent.value = Event(true)
|
||||||
popupWindow.dismiss()
|
popupWindow.dismiss()
|
||||||
}
|
}
|
||||||
popupView.setAddToContactsListener {
|
popupView.setAddToContactsListener {
|
||||||
|
@ -984,7 +976,7 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
|
||||||
Log.i("[Chat Room] Deleting event $eventLog at position $position")
|
Log.i("[Chat Room] Deleting event $eventLog at position $position")
|
||||||
listViewModel.deleteEventLogs(arrayListOf(eventLog))
|
listViewModel.deleteEventLogs(arrayListOf(eventLog))
|
||||||
}
|
}
|
||||||
viewModel.updateLastMessageToDisplay()
|
sharedViewModel.refreshChatRoomInListEvent.value = Event(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import org.linphone.activities.main.fragments.SecureFragment
|
||||||
import org.linphone.activities.main.viewmodels.SharedMainViewModel
|
import org.linphone.activities.main.viewmodels.SharedMainViewModel
|
||||||
import org.linphone.core.tools.Log
|
import org.linphone.core.tools.Log
|
||||||
import org.linphone.databinding.ChatRoomEphemeralFragmentBinding
|
import org.linphone.databinding.ChatRoomEphemeralFragmentBinding
|
||||||
|
import org.linphone.utils.Event
|
||||||
|
|
||||||
class EphemeralFragment : SecureFragment<ChatRoomEphemeralFragmentBinding>() {
|
class EphemeralFragment : SecureFragment<ChatRoomEphemeralFragmentBinding>() {
|
||||||
private lateinit var viewModel: EphemeralViewModel
|
private lateinit var viewModel: EphemeralViewModel
|
||||||
|
@ -68,6 +69,7 @@ class EphemeralFragment : SecureFragment<ChatRoomEphemeralFragmentBinding>() {
|
||||||
|
|
||||||
binding.setValidClickListener {
|
binding.setValidClickListener {
|
||||||
viewModel.updateChatRoomEphemeralDuration()
|
viewModel.updateChatRoomEphemeralDuration()
|
||||||
|
sharedViewModel.refreshChatRoomInListEvent.value = Event(true)
|
||||||
goBack()
|
goBack()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,6 +130,7 @@ class MasterChatRoomsFragment : MasterFragment<ChatRoomMasterFragmentBinding, Ch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sharedViewModel.layoutChangedEvent.observe(
|
sharedViewModel.layoutChangedEvent.observe(
|
||||||
viewLifecycleOwner
|
viewLifecycleOwner
|
||||||
) {
|
) {
|
||||||
|
@ -145,6 +146,18 @@ class MasterChatRoomsFragment : MasterFragment<ChatRoomMasterFragmentBinding, Ch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sharedViewModel.refreshChatRoomInListEvent.observe(
|
||||||
|
viewLifecycleOwner
|
||||||
|
) {
|
||||||
|
it.consume {
|
||||||
|
val chatRoom = sharedViewModel.selectedChatRoom.value
|
||||||
|
if (chatRoom != null) {
|
||||||
|
listViewModel.notifyChatRoomUpdate(chatRoom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
binding.slidingPane.lockMode = SlidingPaneLayout.LOCK_MODE_LOCKED
|
binding.slidingPane.lockMode = SlidingPaneLayout.LOCK_MODE_LOCKED
|
||||||
|
|
||||||
/* End of shared view model & sliding pane related */
|
/* End of shared view model & sliding pane related */
|
||||||
|
@ -175,8 +188,8 @@ class MasterChatRoomsFragment : MasterFragment<ChatRoomMasterFragmentBinding, Ch
|
||||||
)
|
)
|
||||||
val swipeListener = object : RecyclerViewSwipeListener {
|
val swipeListener = object : RecyclerViewSwipeListener {
|
||||||
override fun onLeftToRightSwipe(viewHolder: RecyclerView.ViewHolder) {
|
override fun onLeftToRightSwipe(viewHolder: RecyclerView.ViewHolder) {
|
||||||
val chatRoomViewModel = adapter.currentList[viewHolder.bindingAdapterPosition]
|
val chatRoom = adapter.currentList[viewHolder.bindingAdapterPosition]
|
||||||
chatRoomViewModel.chatRoom.markAsRead()
|
chatRoom.markAsRead()
|
||||||
adapter.notifyItemChanged(viewHolder.bindingAdapterPosition)
|
adapter.notifyItemChanged(viewHolder.bindingAdapterPosition)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,7 +204,7 @@ class MasterChatRoomsFragment : MasterFragment<ChatRoomMasterFragmentBinding, Ch
|
||||||
|
|
||||||
viewModel.showDeleteButton(
|
viewModel.showDeleteButton(
|
||||||
{
|
{
|
||||||
val deletedChatRoom = adapter.currentList[viewHolder.bindingAdapterPosition].chatRoom
|
val deletedChatRoom = adapter.currentList[viewHolder.bindingAdapterPosition]
|
||||||
listViewModel.deleteChatRoom(deletedChatRoom)
|
listViewModel.deleteChatRoom(deletedChatRoom)
|
||||||
if (!binding.slidingPane.isSlideable &&
|
if (!binding.slidingPane.isSlideable &&
|
||||||
deletedChatRoom == sharedViewModel.selectedChatRoom.value
|
deletedChatRoom == sharedViewModel.selectedChatRoom.value
|
||||||
|
@ -219,6 +232,14 @@ class MasterChatRoomsFragment : MasterFragment<ChatRoomMasterFragmentBinding, Ch
|
||||||
adapter.submitList(chatRooms)
|
adapter.submitList(chatRooms)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
listViewModel.chatRoomIndexUpdatedEvent.observe(
|
||||||
|
viewLifecycleOwner
|
||||||
|
) {
|
||||||
|
it.consume { index ->
|
||||||
|
adapter.notifyItemChanged(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
adapter.selectedChatRoomEvent.observe(
|
adapter.selectedChatRoomEvent.observe(
|
||||||
viewLifecycleOwner
|
viewLifecycleOwner
|
||||||
) {
|
) {
|
||||||
|
@ -349,10 +370,10 @@ class MasterChatRoomsFragment : MasterFragment<ChatRoomMasterFragmentBinding, Ch
|
||||||
val list = ArrayList<ChatRoom>()
|
val list = ArrayList<ChatRoom>()
|
||||||
var closeSlidingPane = false
|
var closeSlidingPane = false
|
||||||
for (index in indexesOfItemToDelete) {
|
for (index in indexesOfItemToDelete) {
|
||||||
val chatRoomViewModel = adapter.currentList[index]
|
val chatRoom = adapter.currentList[index]
|
||||||
list.add(chatRoomViewModel.chatRoom)
|
list.add(chatRoom)
|
||||||
|
|
||||||
if (chatRoomViewModel.chatRoom == sharedViewModel.selectedChatRoom.value) {
|
if (chatRoom == sharedViewModel.selectedChatRoom.value) {
|
||||||
closeSlidingPane = true
|
closeSlidingPane = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,6 @@
|
||||||
package org.linphone.activities.main.chat.viewmodels
|
package org.linphone.activities.main.chat.viewmodels
|
||||||
|
|
||||||
import android.animation.ValueAnimator
|
import android.animation.ValueAnimator
|
||||||
import android.graphics.Typeface
|
|
||||||
import android.text.SpannableStringBuilder
|
|
||||||
import android.text.style.StyleSpan
|
|
||||||
import android.view.animation.LinearInterpolator
|
import android.view.animation.LinearInterpolator
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
@ -38,7 +35,6 @@ import org.linphone.core.*
|
||||||
import org.linphone.core.tools.Log
|
import org.linphone.core.tools.Log
|
||||||
import org.linphone.utils.AppUtils
|
import org.linphone.utils.AppUtils
|
||||||
import org.linphone.utils.LinphoneUtils
|
import org.linphone.utils.LinphoneUtils
|
||||||
import org.linphone.utils.TimestampUtils
|
|
||||||
|
|
||||||
class ChatRoomViewModelFactory(private val chatRoom: ChatRoom) :
|
class ChatRoomViewModelFactory(private val chatRoom: ChatRoom) :
|
||||||
ViewModelProvider.NewInstanceFactory() {
|
ViewModelProvider.NewInstanceFactory() {
|
||||||
|
@ -63,10 +59,6 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
|
||||||
|
|
||||||
val unreadMessagesCount = MutableLiveData<Int>()
|
val unreadMessagesCount = MutableLiveData<Int>()
|
||||||
|
|
||||||
val lastUpdate = MutableLiveData<String>()
|
|
||||||
|
|
||||||
val lastMessageText = MutableLiveData<SpannableStringBuilder>()
|
|
||||||
|
|
||||||
val remoteIsComposing = MutableLiveData<Boolean>()
|
val remoteIsComposing = MutableLiveData<Boolean>()
|
||||||
|
|
||||||
val composingList = MutableLiveData<String>()
|
val composingList = MutableLiveData<String>()
|
||||||
|
@ -114,8 +106,6 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
|
||||||
val groupCallAvailable: Boolean
|
val groupCallAvailable: Boolean
|
||||||
get() = LinphoneUtils.isRemoteConferencingAvailable()
|
get() = LinphoneUtils.isRemoteConferencingAvailable()
|
||||||
|
|
||||||
val notificationsMuted = MutableLiveData<Boolean>()
|
|
||||||
|
|
||||||
private var addressToCall: Address? = null
|
private var addressToCall: Address? = null
|
||||||
|
|
||||||
private val bounceAnimator: ValueAnimator by lazy {
|
private val bounceAnimator: ValueAnimator by lazy {
|
||||||
|
@ -135,7 +125,6 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
|
||||||
override fun onContactsUpdated() {
|
override fun onContactsUpdated() {
|
||||||
Log.d("[Chat Room] Contacts have changed")
|
Log.d("[Chat Room] Contacts have changed")
|
||||||
contactLookup()
|
contactLookup()
|
||||||
updateLastMessageToDisplay()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,11 +152,6 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
|
||||||
|
|
||||||
override fun onChatMessageReceived(chatRoom: ChatRoom, eventLog: EventLog) {
|
override fun onChatMessageReceived(chatRoom: ChatRoom, eventLog: EventLog) {
|
||||||
updateUnreadMessageCount()
|
updateUnreadMessageCount()
|
||||||
formatLastMessage(eventLog.chatMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onChatMessageSent(chatRoom: ChatRoom, eventLog: EventLog) {
|
|
||||||
formatLastMessage(eventLog.chatMessage)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onParticipantAdded(chatRoom: ChatRoom, eventLog: EventLog) {
|
override fun onParticipantAdded(chatRoom: ChatRoom, eventLog: EventLog) {
|
||||||
|
@ -210,11 +194,6 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
|
||||||
updateParticipants()
|
updateParticipants()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onEphemeralMessageDeleted(chatRoom: ChatRoom, eventLog: EventLog) {
|
|
||||||
Log.i("[Chat Room] Ephemeral message deleted, updated last message displayed")
|
|
||||||
updateLastMessageToDisplay()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onEphemeralEvent(chatRoom: ChatRoom, eventLog: EventLog) {
|
override fun onEphemeralEvent(chatRoom: ChatRoom, eventLog: EventLog) {
|
||||||
ephemeralEnabled.value = chatRoom.isEphemeralEnabled
|
ephemeralEnabled.value = chatRoom.isEphemeralEnabled
|
||||||
}
|
}
|
||||||
|
@ -238,23 +217,16 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
|
||||||
|
|
||||||
contactLookup()
|
contactLookup()
|
||||||
updateParticipants()
|
updateParticipants()
|
||||||
updateLastMessageToDisplay()
|
|
||||||
|
|
||||||
updateRemotesComposing()
|
updateRemotesComposing()
|
||||||
|
|
||||||
notificationsMuted.value = areNotificationsMuted()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
destroy()
|
|
||||||
super.onCleared()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun destroy() {
|
|
||||||
coreContext.contactsManager.removeListener(contactsUpdatedListener)
|
coreContext.contactsManager.removeListener(contactsUpdatedListener)
|
||||||
chatRoom.removeListener(chatRoomListener)
|
chatRoom.removeListener(chatRoomListener)
|
||||||
chatRoom.core.removeListener(coreListener)
|
chatRoom.core.removeListener(coreListener)
|
||||||
if (corePreferences.enableAnimations) bounceAnimator.end()
|
if (corePreferences.enableAnimations) bounceAnimator.end()
|
||||||
|
super.onCleared()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun contactLookup() {
|
fun contactLookup() {
|
||||||
|
@ -306,10 +278,6 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
|
||||||
conferenceScheduler.info = conferenceInfo
|
conferenceScheduler.info = conferenceInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateLastMessageToDisplay() {
|
|
||||||
formatLastMessage(chatRoom.lastMessageInHistory)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun areNotificationsMuted(): Boolean {
|
fun areNotificationsMuted(): Boolean {
|
||||||
val id = LinphoneUtils.getChatRoomId(chatRoom.localAddress, chatRoom.peerAddress)
|
val id = LinphoneUtils.getChatRoomId(chatRoom.localAddress, chatRoom.peerAddress)
|
||||||
return corePreferences.chatRoomMuted(id)
|
return corePreferences.chatRoomMuted(id)
|
||||||
|
@ -318,43 +286,6 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
|
||||||
fun muteNotifications(mute: Boolean) {
|
fun muteNotifications(mute: Boolean) {
|
||||||
val id = LinphoneUtils.getChatRoomId(chatRoom.localAddress, chatRoom.peerAddress)
|
val id = LinphoneUtils.getChatRoomId(chatRoom.localAddress, chatRoom.peerAddress)
|
||||||
corePreferences.muteChatRoom(id, mute)
|
corePreferences.muteChatRoom(id, mute)
|
||||||
notificationsMuted.value = mute
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun formatLastMessage(msg: ChatMessage?) {
|
|
||||||
val lastUpdateTime = chatRoom.lastUpdateTime
|
|
||||||
viewModelScope.launch {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
lastUpdate.postValue(TimestampUtils.toString(lastUpdateTime, true))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val builder = SpannableStringBuilder()
|
|
||||||
if (msg == null) {
|
|
||||||
lastMessageText.value = builder
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val sender: String =
|
|
||||||
coreContext.contactsManager.findContactByAddress(msg.fromAddress)?.name
|
|
||||||
?: LinphoneUtils.getDisplayName(msg.fromAddress)
|
|
||||||
builder.append(sender)
|
|
||||||
builder.append(": ")
|
|
||||||
|
|
||||||
for (content in msg.contents) {
|
|
||||||
if (content.isIcalendar) {
|
|
||||||
val body = AppUtils.getString(R.string.conference_invitation)
|
|
||||||
builder.append(body)
|
|
||||||
builder.setSpan(StyleSpan(Typeface.ITALIC), builder.length - body.length, builder.length, 0)
|
|
||||||
} else if (content.isFile || content.isFileTransfer) {
|
|
||||||
builder.append(content.name + " ")
|
|
||||||
} else if (content.isText) {
|
|
||||||
builder.append(content.utf8Text + " ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.trim()
|
|
||||||
lastMessageText.value = builder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getRemoteAddress(): Address? {
|
fun getRemoteAddress(): Address? {
|
||||||
|
|
|
@ -24,13 +24,14 @@ import org.linphone.LinphoneApplication.Companion.coreContext
|
||||||
import org.linphone.R
|
import org.linphone.R
|
||||||
import org.linphone.activities.main.viewmodels.MessageNotifierViewModel
|
import org.linphone.activities.main.viewmodels.MessageNotifierViewModel
|
||||||
import org.linphone.compatibility.Compatibility
|
import org.linphone.compatibility.Compatibility
|
||||||
|
import org.linphone.contact.ContactsUpdatedListenerStub
|
||||||
import org.linphone.core.*
|
import org.linphone.core.*
|
||||||
import org.linphone.core.tools.Log
|
import org.linphone.core.tools.Log
|
||||||
import org.linphone.utils.Event
|
import org.linphone.utils.Event
|
||||||
import org.linphone.utils.LinphoneUtils
|
import org.linphone.utils.LinphoneUtils
|
||||||
|
|
||||||
class ChatRoomsListViewModel : MessageNotifierViewModel() {
|
class ChatRoomsListViewModel : MessageNotifierViewModel() {
|
||||||
val chatRooms = MutableLiveData<ArrayList<ChatRoomViewModel>>()
|
val chatRooms = MutableLiveData<ArrayList<ChatRoom>>()
|
||||||
|
|
||||||
val fileSharingPending = MutableLiveData<Boolean>()
|
val fileSharingPending = MutableLiveData<Boolean>()
|
||||||
|
|
||||||
|
@ -40,12 +41,14 @@ class ChatRoomsListViewModel : MessageNotifierViewModel() {
|
||||||
|
|
||||||
val groupChatAvailable: Boolean = LinphoneUtils.isGroupChatAvailable()
|
val groupChatAvailable: Boolean = LinphoneUtils.isGroupChatAvailable()
|
||||||
|
|
||||||
|
val chatRoomIndexUpdatedEvent: MutableLiveData<Event<Int>> by lazy {
|
||||||
|
MutableLiveData<Event<Int>>()
|
||||||
|
}
|
||||||
|
|
||||||
private val listener: CoreListenerStub = object : CoreListenerStub() {
|
private val listener: CoreListenerStub = object : CoreListenerStub() {
|
||||||
override fun onChatRoomStateChanged(core: Core, chatRoom: ChatRoom, state: ChatRoom.State) {
|
override fun onChatRoomStateChanged(core: Core, chatRoom: ChatRoom, state: ChatRoom.State) {
|
||||||
if (state == ChatRoom.State.Created) {
|
if (state == ChatRoom.State.Created) {
|
||||||
if (chatRoom in core.chatRooms) { // Don't add empty chat room if 1-1 depending on policy
|
updateChatRooms()
|
||||||
addChatRoom(chatRoom)
|
|
||||||
}
|
|
||||||
} else if (state == ChatRoom.State.TerminationFailed) {
|
} else if (state == ChatRoom.State.TerminationFailed) {
|
||||||
Log.e("[Chat Rooms] Group chat room removal for address ${chatRoom.peerAddress.asStringUriOnly()} has failed !")
|
Log.e("[Chat Rooms] Group chat room removal for address ${chatRoom.peerAddress.asStringUriOnly()} has failed !")
|
||||||
onMessageToNotifyEvent.value = Event(R.string.chat_room_removal_failed_snack)
|
onMessageToNotifyEvent.value = Event(R.string.chat_room_removal_failed_snack)
|
||||||
|
@ -54,34 +57,44 @@ class ChatRoomsListViewModel : MessageNotifierViewModel() {
|
||||||
|
|
||||||
override fun onMessageSent(core: Core, chatRoom: ChatRoom, message: ChatMessage) {
|
override fun onMessageSent(core: Core, chatRoom: ChatRoom, message: ChatMessage) {
|
||||||
when (findChatRoomIndex(chatRoom)) {
|
when (findChatRoomIndex(chatRoom)) {
|
||||||
-1 -> addChatRoom(chatRoom)
|
-1 -> updateChatRooms()
|
||||||
0 -> {}
|
0 -> chatRoomIndexUpdatedEvent.value = Event(0)
|
||||||
else -> reorderChatRooms()
|
else -> reorderChatRooms()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMessageReceived(core: Core, chatRoom: ChatRoom, message: ChatMessage) {
|
override fun onMessageReceived(core: Core, chatRoom: ChatRoom, message: ChatMessage) {
|
||||||
when (findChatRoomIndex(chatRoom)) {
|
when (findChatRoomIndex(chatRoom)) {
|
||||||
-1 -> addChatRoom(chatRoom)
|
-1 -> updateChatRooms()
|
||||||
0 -> {}
|
0 -> chatRoomIndexUpdatedEvent.value = Event(0)
|
||||||
else -> reorderChatRooms()
|
else -> reorderChatRooms()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onChatRoomRead(core: Core, chatRoom: ChatRoom) {
|
||||||
|
notifyChatRoomUpdate(chatRoom)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onChatRoomEphemeralMessageDeleted(core: Core, chatRoom: ChatRoom) {
|
||||||
|
notifyChatRoomUpdate(chatRoom)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onChatRoomSubjectChanged(core: Core, chatRoom: ChatRoom) {
|
||||||
|
notifyChatRoomUpdate(chatRoom)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val chatRoomListener = object : ChatRoomListenerStub() {
|
private val chatRoomListener = object : ChatRoomListenerStub() {
|
||||||
override fun onStateChanged(chatRoom: ChatRoom, newState: ChatRoom.State) {
|
override fun onStateChanged(chatRoom: ChatRoom, newState: ChatRoom.State) {
|
||||||
if (newState == ChatRoom.State.Deleted) {
|
if (newState == ChatRoom.State.Deleted) {
|
||||||
val list = arrayListOf<ChatRoomViewModel>()
|
updateChatRooms()
|
||||||
for (chatRoomViewModel in chatRooms.value.orEmpty()) {
|
|
||||||
if (chatRoomViewModel.chatRoom != chatRoom) {
|
|
||||||
list.add(chatRoomViewModel)
|
|
||||||
} else {
|
|
||||||
chatRoomViewModel.destroy()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chatRooms.value = list
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val contactsListener = object : ContactsUpdatedListenerStub() {
|
||||||
|
override fun onContactUpdated(friend: Friend) {
|
||||||
|
updateChatRooms()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,10 +103,11 @@ class ChatRoomsListViewModel : MessageNotifierViewModel() {
|
||||||
init {
|
init {
|
||||||
updateChatRooms()
|
updateChatRooms()
|
||||||
coreContext.core.addListener(listener)
|
coreContext.core.addListener(listener)
|
||||||
|
coreContext.contactsManager.addListener(contactsListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
chatRooms.value.orEmpty().forEach(ChatRoomViewModel::destroy)
|
coreContext.contactsManager.removeListener(contactsListener)
|
||||||
coreContext.core.removeListener(listener)
|
coreContext.core.removeListener(listener)
|
||||||
|
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
|
@ -128,42 +142,27 @@ class ChatRoomsListViewModel : MessageNotifierViewModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateChatRooms() {
|
fun updateChatRooms() {
|
||||||
chatRooms.value.orEmpty().forEach(ChatRoomViewModel::destroy)
|
val list = arrayListOf<ChatRoom>()
|
||||||
|
list.addAll(coreContext.core.chatRooms)
|
||||||
val list = arrayListOf<ChatRoomViewModel>()
|
|
||||||
for (chatRoom in coreContext.core.chatRooms) {
|
|
||||||
val viewModel = ChatRoomViewModel(chatRoom)
|
|
||||||
list.add(viewModel)
|
|
||||||
}
|
|
||||||
chatRooms.value = list
|
chatRooms.value = list
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addChatRoom(chatRoom: ChatRoom) {
|
fun notifyChatRoomUpdate(chatRoom: ChatRoom) {
|
||||||
val exists = chatRooms.value.orEmpty().find {
|
val index = findChatRoomIndex(chatRoom)
|
||||||
it.chatRoom.localAddress.weakEqual(chatRoom.localAddress) && it.chatRoom.peerAddress.weakEqual(chatRoom.peerAddress)
|
if (index == -1) updateChatRooms()
|
||||||
}
|
else chatRoomIndexUpdatedEvent.value = Event(index)
|
||||||
if (exists != null) {
|
|
||||||
Log.w("[Chat Rooms] Do not add chat room to list, it's already here")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val list = arrayListOf<ChatRoomViewModel>()
|
|
||||||
val viewModel = ChatRoomViewModel(chatRoom)
|
|
||||||
list.add(viewModel)
|
|
||||||
list.addAll(chatRooms.value.orEmpty())
|
|
||||||
chatRooms.value = list
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reorderChatRooms() {
|
private fun reorderChatRooms() {
|
||||||
val list = arrayListOf<ChatRoomViewModel>()
|
val list = arrayListOf<ChatRoom>()
|
||||||
list.addAll(chatRooms.value.orEmpty())
|
list.addAll(chatRooms.value.orEmpty())
|
||||||
list.sortByDescending { chatRoomViewModel -> chatRoomViewModel.chatRoom.lastUpdateTime }
|
list.sortByDescending { chatRoom -> chatRoom.lastUpdateTime }
|
||||||
chatRooms.value = list
|
chatRooms.value = list
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findChatRoomIndex(chatRoom: ChatRoom): Int {
|
private fun findChatRoomIndex(chatRoom: ChatRoom): Int {
|
||||||
for ((index, chatRoomViewModel) in chatRooms.value.orEmpty().withIndex()) {
|
for ((index, cr) in chatRooms.value.orEmpty().withIndex()) {
|
||||||
if (chatRoomViewModel.chatRoom == chatRoom) {
|
if (LinphoneUtils.areChatRoomsTheSame(cr, chatRoom)) {
|
||||||
return index
|
return index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,10 @@ class SharedMainViewModel : ViewModel() {
|
||||||
// When using keyboard to share gif or other, see RichContentReceiver & RichEditText classes
|
// When using keyboard to share gif or other, see RichContentReceiver & RichEditText classes
|
||||||
val richContentUri = MutableLiveData<Event<Uri>>()
|
val richContentUri = MutableLiveData<Event<Uri>>()
|
||||||
|
|
||||||
|
val refreshChatRoomInListEvent: MutableLiveData<Event<Boolean>> by lazy {
|
||||||
|
MutableLiveData<Event<Boolean>>()
|
||||||
|
}
|
||||||
|
|
||||||
/* Contacts */
|
/* Contacts */
|
||||||
|
|
||||||
val contactFragmentOpenedEvent: MutableLiveData<Event<Boolean>> by lazy {
|
val contactFragmentOpenedEvent: MutableLiveData<Event<Boolean>> by lazy {
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
name="forwardPending"
|
name="forwardPending"
|
||||||
type="Boolean" />
|
type="Boolean" />
|
||||||
<variable
|
<variable
|
||||||
name="viewModel"
|
name="data"
|
||||||
type="org.linphone.activities.main.chat.viewmodels.ChatRoomViewModel" />
|
type="org.linphone.activities.main.chat.data.ChatRoomData" />
|
||||||
<variable
|
<variable
|
||||||
name="selectionListViewModel"
|
name="selectionListViewModel"
|
||||||
type="org.linphone.activities.main.viewmodels.ListTopBarViewModel" />
|
type="org.linphone.activities.main.viewmodels.ListTopBarViewModel" />
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/avatar"
|
android:id="@+id/avatar"
|
||||||
coilContact="@{viewModel}"
|
coilContact="@{data}"
|
||||||
android:layout_width="@dimen/contact_avatar_size"
|
android:layout_width="@dimen/contact_avatar_size"
|
||||||
android:layout_height="@dimen/contact_avatar_size"
|
android:layout_height="@dimen/contact_avatar_size"
|
||||||
android:layout_marginLeft="5dp"
|
android:layout_marginLeft="5dp"
|
||||||
|
@ -54,9 +54,9 @@
|
||||||
android:layout_width="20dp"
|
android:layout_width="20dp"
|
||||||
android:layout_height="20dp"
|
android:layout_height="20dp"
|
||||||
android:layout_marginLeft="25dp"
|
android:layout_marginLeft="25dp"
|
||||||
android:contentDescription="@{viewModel.securityLevelContentDescription}"
|
android:contentDescription="@{data.securityLevelContentDescription}"
|
||||||
android:src="@{viewModel.securityLevelIcon, default=@drawable/security_alert_indicator}"
|
android:src="@{data.securityLevelIcon, default=@drawable/security_alert_indicator}"
|
||||||
android:visibility="@{viewModel.encryptedChatRoom ? View.VISIBLE : View.INVISIBLE, default=invisible}"
|
android:visibility="@{data.encryptedChatRoom ? View.VISIBLE : View.INVISIBLE, default=invisible}"
|
||||||
app:layout_constraintStart_toStartOf="@id/avatar"
|
app:layout_constraintStart_toStartOf="@id/avatar"
|
||||||
app:layout_constraintTop_toTopOf="@id/avatar" />
|
app:layout_constraintTop_toTopOf="@id/avatar" />
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@
|
||||||
android:layout_marginTop="5dp"
|
android:layout_marginTop="5dp"
|
||||||
android:paddingRight="10dp"
|
android:paddingRight="10dp"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:text="@{viewModel.lastUpdate, default=`12:03`}"
|
android:text="@{data.lastUpdate, default=`12:03`}"
|
||||||
android:textColor="?attr/accentColor"
|
android:textColor="?attr/accentColor"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
@ -84,7 +84,7 @@
|
||||||
android:layout_marginRight="5dp"
|
android:layout_marginRight="5dp"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:text="@{viewModel.oneToOneChatRoom ? (viewModel.contact.name ?? viewModel.displayName) : viewModel.subject, default=`Lorem Ipsum`}"
|
android:text="@{data.oneToOneChatRoom ? (data.contact.name ?? data.displayName) : data.subject, default=`Lorem Ipsum`}"
|
||||||
app:layout_constraintStart_toEndOf="@id/date"
|
app:layout_constraintStart_toEndOf="@id/date"
|
||||||
app:layout_constraintEnd_toStartOf="@id/unread"
|
app:layout_constraintEnd_toStartOf="@id/unread"
|
||||||
app:layout_constraintTop_toTopOf="@id/avatar" />
|
app:layout_constraintTop_toTopOf="@id/avatar" />
|
||||||
|
@ -98,7 +98,7 @@
|
||||||
android:layout_marginRight="5dp"
|
android:layout_marginRight="5dp"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
android:text="@{viewModel.lastMessageText, default=`Lorem ipsum dolor sit amet`}"
|
android:text="@{data.lastMessageText, default=`Lorem ipsum dolor sit amet`}"
|
||||||
app:layout_constraintStart_toEndOf="@id/date"
|
app:layout_constraintStart_toEndOf="@id/date"
|
||||||
app:layout_constraintEnd_toStartOf="@id/endBarrier"
|
app:layout_constraintEnd_toStartOf="@id/endBarrier"
|
||||||
app:layout_constraintTop_toBottomOf="@id/title" />
|
app:layout_constraintTop_toBottomOf="@id/title" />
|
||||||
|
@ -121,7 +121,7 @@
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
android:contentDescription="@string/content_description_muted_chat_room"
|
android:contentDescription="@string/content_description_muted_chat_room"
|
||||||
android:src="@drawable/chat_room_menu_mute_notifications"
|
android:src="@drawable/chat_room_menu_mute_notifications"
|
||||||
android:visibility="@{viewModel.notificationsMuted ? View.VISIBLE : View.GONE, default=gone}"
|
android:visibility="@{data.notificationsMuted ? View.VISIBLE : View.GONE, default=gone}"
|
||||||
app:layout_constraintEnd_toStartOf="@id/endBarrier3"
|
app:layout_constraintEnd_toStartOf="@id/endBarrier3"
|
||||||
app:layout_constraintTop_toTopOf="@id/title" />
|
app:layout_constraintTop_toTopOf="@id/title" />
|
||||||
|
|
||||||
|
@ -135,8 +135,8 @@
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:text="@{String.valueOf(viewModel.unreadMessagesCount)}"
|
android:text="@{String.valueOf(data.unreadMessagesCount)}"
|
||||||
android:visibility="@{viewModel.unreadMessagesCount == 0 ? View.GONE : View.VISIBLE, default=gone}"
|
android:visibility="@{data.unreadMessagesCount == 0 ? View.GONE : View.VISIBLE, default=gone}"
|
||||||
app:layout_constraintEnd_toStartOf="@id/endBarrier2"
|
app:layout_constraintEnd_toStartOf="@id/endBarrier2"
|
||||||
app:layout_constraintTop_toTopOf="@id/title" />
|
app:layout_constraintTop_toTopOf="@id/title" />
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
android:contentDescription="@string/content_description_ephemeral_chat_room"
|
android:contentDescription="@string/content_description_ephemeral_chat_room"
|
||||||
android:src="@drawable/ephemeral_messages"
|
android:src="@drawable/ephemeral_messages"
|
||||||
android:visibility="@{viewModel.ephemeralEnabled ? View.VISIBLE : View.GONE, default=gone}"
|
android:visibility="@{data.ephemeralEnabled ? View.VISIBLE : View.GONE, default=gone}"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@id/forward" />
|
app:layout_constraintEnd_toStartOf="@id/forward" />
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue