Fixed bad design left overs

This commit is contained in:
Sylvain Berfini 2021-06-16 16:39:57 +02:00
parent 755639c827
commit 032e9fe8ec
20 changed files with 135 additions and 107 deletions

View file

@ -120,12 +120,6 @@ class ChatMessagesListAdapter(
} }
} }
override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
when (holder) {
is ChatMessageViewHolder -> holder.binding.data?.destroy()
}
}
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int {
val eventLog = getItem(position) val eventLog = getItem(position)
return eventLog.eventLog.type.toInt() return eventLog.eventLog.type.toInt()

View file

@ -36,7 +36,7 @@ import org.linphone.utils.Event
class GroupInfoParticipantsAdapter( class GroupInfoParticipantsAdapter(
private val viewLifecycleOwner: LifecycleOwner, private val viewLifecycleOwner: LifecycleOwner,
private val isEncryptionEnabled: Boolean private val isEncryptionEnabled: Boolean
) : ListAdapter<GroupChatRoomMember, RecyclerView.ViewHolder>(ParticipantDiffCallback()) { ) : ListAdapter<GroupInfoParticipantData, RecyclerView.ViewHolder>(ParticipantDiffCallback()) {
private var showAdmin: Boolean = false private var showAdmin: Boolean = false
val participantRemovedEvent: MutableLiveData<Event<GroupChatRoomMember>> by lazy { val participantRemovedEvent: MutableLiveData<Event<GroupChatRoomMember>> by lazy {
@ -55,10 +55,6 @@ class GroupInfoParticipantsAdapter(
(holder as ViewHolder).bind(getItem(position)) (holder as ViewHolder).bind(getItem(position))
} }
override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
(holder as ViewHolder).binding.data?.destroy()
}
fun showAdminControls(show: Boolean) { fun showAdminControls(show: Boolean) {
showAdmin = show showAdmin = show
notifyDataSetChanged() notifyDataSetChanged()
@ -67,16 +63,15 @@ class GroupInfoParticipantsAdapter(
inner class ViewHolder( inner class ViewHolder(
val binding: ChatRoomGroupInfoParticipantCellBinding val binding: ChatRoomGroupInfoParticipantCellBinding
) : RecyclerView.ViewHolder(binding.root) { ) : RecyclerView.ViewHolder(binding.root) {
fun bind(participant: GroupChatRoomMember) { fun bind(participantViewModel: GroupInfoParticipantData) {
with(binding) { with(binding) {
val participantViewModel = GroupInfoParticipantData(participant)
participantViewModel.showAdminControls.value = showAdmin participantViewModel.showAdminControls.value = showAdmin
data = participantViewModel data = participantViewModel
lifecycleOwner = viewLifecycleOwner lifecycleOwner = viewLifecycleOwner
setRemoveClickListener { setRemoveClickListener {
participantRemovedEvent.value = Event(participant) participantRemovedEvent.value = Event(participantViewModel.participant)
} }
isEncrypted = isEncryptionEnabled isEncrypted = isEncryptionEnabled
@ -86,17 +81,17 @@ class GroupInfoParticipantsAdapter(
} }
} }
private class ParticipantDiffCallback : DiffUtil.ItemCallback<GroupChatRoomMember>() { private class ParticipantDiffCallback : DiffUtil.ItemCallback<GroupInfoParticipantData>() {
override fun areItemsTheSame( override fun areItemsTheSame(
oldItem: GroupChatRoomMember, oldItem: GroupInfoParticipantData,
newItem: GroupChatRoomMember newItem: GroupInfoParticipantData
): Boolean { ): Boolean {
return oldItem.address.weakEqual(newItem.address) return oldItem.sipUri == newItem.sipUri
} }
override fun areContentsTheSame( override fun areContentsTheSame(
oldItem: GroupChatRoomMember, oldItem: GroupInfoParticipantData,
newItem: GroupChatRoomMember newItem: GroupInfoParticipantData
): Boolean { ): Boolean {
return false return false
} }

View file

@ -31,14 +31,13 @@ import androidx.recyclerview.widget.RecyclerView
import org.linphone.R import org.linphone.R
import org.linphone.activities.main.chat.data.ImdnParticipantData import org.linphone.activities.main.chat.data.ImdnParticipantData
import org.linphone.core.ChatMessage import org.linphone.core.ChatMessage
import org.linphone.core.ParticipantImdnState
import org.linphone.databinding.ChatRoomImdnParticipantCellBinding import org.linphone.databinding.ChatRoomImdnParticipantCellBinding
import org.linphone.databinding.ImdnListHeaderBinding import org.linphone.databinding.ImdnListHeaderBinding
import org.linphone.utils.HeaderAdapter import org.linphone.utils.HeaderAdapter
class ImdnAdapter( class ImdnAdapter(
private val viewLifecycleOwner: LifecycleOwner private val viewLifecycleOwner: LifecycleOwner
) : ListAdapter<ParticipantImdnState, RecyclerView.ViewHolder>(ParticipantImdnStateDiffCallback()), HeaderAdapter { ) : ListAdapter<ImdnParticipantData, RecyclerView.ViewHolder>(ParticipantImdnStateDiffCallback()), HeaderAdapter {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding: ChatRoomImdnParticipantCellBinding = DataBindingUtil.inflate( val binding: ChatRoomImdnParticipantCellBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context), LayoutInflater.from(parent.context),
@ -51,16 +50,12 @@ class ImdnAdapter(
(holder as ViewHolder).bind(getItem(position)) (holder as ViewHolder).bind(getItem(position))
} }
override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
(holder as ViewHolder).binding.data?.destroy()
}
inner class ViewHolder( inner class ViewHolder(
val binding: ChatRoomImdnParticipantCellBinding val binding: ChatRoomImdnParticipantCellBinding
) : RecyclerView.ViewHolder(binding.root) { ) : RecyclerView.ViewHolder(binding.root) {
fun bind(participantImdnState: ParticipantImdnState) { fun bind(participantImdnData: ImdnParticipantData) {
with(binding) { with(binding) {
data = ImdnParticipantData(participantImdnState) data = participantImdnData
lifecycleOwner = viewLifecycleOwner lifecycleOwner = viewLifecycleOwner
@ -74,12 +69,12 @@ class ImdnAdapter(
val participantImdnState = getItem(position) val participantImdnState = getItem(position)
val previousPosition = position - 1 val previousPosition = position - 1
return if (previousPosition >= 0) { return if (previousPosition >= 0) {
getItem(previousPosition).state != participantImdnState.state getItem(previousPosition).imdnState.state != participantImdnState.imdnState.state
} else true } else true
} }
override fun getHeaderViewForPosition(context: Context, position: Int): View { override fun getHeaderViewForPosition(context: Context, position: Int): View {
val participantImdnState = getItem(position) val participantImdnState = getItem(position).imdnState
val binding: ImdnListHeaderBinding = DataBindingUtil.inflate( val binding: ImdnListHeaderBinding = DataBindingUtil.inflate(
LayoutInflater.from(context), LayoutInflater.from(context),
R.layout.imdn_list_header, null, false R.layout.imdn_list_header, null, false
@ -111,17 +106,17 @@ class ImdnAdapter(
} }
} }
private class ParticipantImdnStateDiffCallback : DiffUtil.ItemCallback<ParticipantImdnState>() { private class ParticipantImdnStateDiffCallback : DiffUtil.ItemCallback<ImdnParticipantData>() {
override fun areItemsTheSame( override fun areItemsTheSame(
oldItem: ParticipantImdnState, oldItem: ImdnParticipantData,
newItem: ParticipantImdnState newItem: ImdnParticipantData
): Boolean { ): Boolean {
return oldItem.participant.address.weakEqual(newItem.participant.address) return oldItem.sipUri == newItem.sipUri
} }
override fun areContentsTheSame( override fun areContentsTheSame(
oldItem: ParticipantImdnState, oldItem: ImdnParticipantData,
newItem: ParticipantImdnState newItem: ImdnParticipantData
): Boolean { ): Boolean {
return false return false
} }

View file

@ -28,4 +28,8 @@ class EventLogData(val eventLog: EventLog) {
} else { } else {
EventData(eventLog) EventData(eventLog)
} }
fun destroy() {
data.destroy()
}
} }

View file

@ -25,7 +25,7 @@ import org.linphone.contact.GenericContactData
import org.linphone.core.ChatRoomSecurityLevel import org.linphone.core.ChatRoomSecurityLevel
import org.linphone.utils.LinphoneUtils import org.linphone.utils.LinphoneUtils
class GroupInfoParticipantData(private val participant: GroupChatRoomMember) : GenericContactData(participant.address) { class GroupInfoParticipantData(val participant: GroupChatRoomMember) : GenericContactData(participant.address) {
override val securityLevel: ChatRoomSecurityLevel override val securityLevel: ChatRoomSecurityLevel
get() = participant.securityLevel get() = participant.securityLevel
@ -44,6 +44,10 @@ class GroupInfoParticipantData(private val participant: GroupChatRoomMember) : G
canBeSetAdmin.value = participant.canBeSetAdmin canBeSetAdmin.value = participant.canBeSetAdmin
} }
override fun destroy() {
super.destroy()
}
fun setAdmin() { fun setAdmin() {
isAdmin.value = true isAdmin.value = true
participant.isAdmin = true participant.isAdmin = true

View file

@ -23,8 +23,12 @@ import org.linphone.contact.GenericContactData
import org.linphone.core.ParticipantImdnState import org.linphone.core.ParticipantImdnState
import org.linphone.utils.TimestampUtils import org.linphone.utils.TimestampUtils
class ImdnParticipantData(imdnState: ParticipantImdnState) : GenericContactData(imdnState.participant.address) { class ImdnParticipantData(val imdnState: ParticipantImdnState) : GenericContactData(imdnState.participant.address) {
val sipUri: String = imdnState.participant.address.asStringUriOnly() val sipUri: String = imdnState.participant.address.asStringUriOnly()
val time: String = TimestampUtils.toString(imdnState.stateChangeTime) val time: String = TimestampUtils.toString(imdnState.stateChangeTime)
override fun destroy() {
super.destroy()
}
} }

View file

@ -29,6 +29,7 @@ import org.linphone.R
import org.linphone.activities.main.MainActivity import org.linphone.activities.main.MainActivity
import org.linphone.activities.main.chat.GroupChatRoomMember import org.linphone.activities.main.chat.GroupChatRoomMember
import org.linphone.activities.main.chat.adapters.GroupInfoParticipantsAdapter import org.linphone.activities.main.chat.adapters.GroupInfoParticipantsAdapter
import org.linphone.activities.main.chat.data.GroupInfoParticipantData
import org.linphone.activities.main.chat.viewmodels.GroupInfoViewModel import org.linphone.activities.main.chat.viewmodels.GroupInfoViewModel
import org.linphone.activities.main.chat.viewmodels.GroupInfoViewModelFactory import org.linphone.activities.main.chat.viewmodels.GroupInfoViewModelFactory
import org.linphone.activities.main.fragments.SecureFragment import org.linphone.activities.main.fragments.SecureFragment
@ -129,7 +130,7 @@ class GroupInfoFragment : SecureFragment<ChatRoomGroupInfoFragmentBinding>() {
val list = arrayListOf<Address>() val list = arrayListOf<Address>()
for (participant in viewModel.participants.value.orEmpty()) { for (participant in viewModel.participants.value.orEmpty()) {
list.add(participant.address) list.add(participant.participant.address)
} }
sharedViewModel.chatRoomParticipants.value = list sharedViewModel.chatRoomParticipants.value = list
@ -164,17 +165,19 @@ class GroupInfoFragment : SecureFragment<ChatRoomGroupInfoFragmentBinding>() {
private fun addParticipantsFromSharedViewModel() { private fun addParticipantsFromSharedViewModel() {
val participants = sharedViewModel.chatRoomParticipants.value val participants = sharedViewModel.chatRoomParticipants.value
if (participants != null && participants.size > 0) { if (participants != null && participants.size > 0) {
val list = arrayListOf<GroupChatRoomMember>() val list = arrayListOf<GroupInfoParticipantData>()
for (address in participants) { for (address in participants) {
val exists = viewModel.participants.value?.find { val exists = viewModel.participants.value?.find {
it.address.weakEqual(address) it.participant.address.weakEqual(address)
} }
if (exists != null) { if (exists != null) {
list.add(exists) list.add(exists)
} else { } else {
list.add(GroupChatRoomMember(address, false, hasLimeX3DHCapability = viewModel.isEncrypted.value == true)) list.add(GroupInfoParticipantData(
GroupChatRoomMember(address, false, hasLimeX3DHCapability = viewModel.isEncrypted.value == true)
))
} }
} }

View file

@ -150,6 +150,7 @@ class ChatMessagesListViewModel(private val chatRoom: ChatRoom) : ViewModel() {
} }
override fun onCleared() { override fun onCleared() {
events.value.orEmpty().forEach(EventLogData::destroy)
chatRoom.removeListener(chatRoomListener) chatRoom.removeListener(chatRoomListener)
super.onCleared() super.onCleared()

View file

@ -96,6 +96,7 @@ class ChatRoomsListViewModel : ErrorReportingViewModel() {
} }
override fun onCleared() { override fun onCleared() {
chatRooms.value.orEmpty().forEach(ChatRoomViewModel::destroy)
coreContext.contactsManager.removeListener(contactsUpdatedListener) coreContext.contactsManager.removeListener(contactsUpdatedListener)
coreContext.core.removeListener(listener) coreContext.core.removeListener(listener)

View file

@ -25,6 +25,7 @@ import androidx.lifecycle.ViewModelProvider
import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R import org.linphone.R
import org.linphone.activities.main.chat.GroupChatRoomMember import org.linphone.activities.main.chat.GroupChatRoomMember
import org.linphone.activities.main.chat.data.GroupInfoParticipantData
import org.linphone.activities.main.viewmodels.ErrorReportingViewModel import org.linphone.activities.main.viewmodels.ErrorReportingViewModel
import org.linphone.core.* import org.linphone.core.*
import org.linphone.core.tools.Log import org.linphone.core.tools.Log
@ -44,7 +45,7 @@ class GroupInfoViewModel(val chatRoom: ChatRoom?) : ErrorReportingViewModel() {
val subject = MutableLiveData<String>() val subject = MutableLiveData<String>()
val participants = MutableLiveData<ArrayList<GroupChatRoomMember>>() val participants = MutableLiveData<ArrayList<GroupInfoParticipantData>>()
val isEncrypted = MutableLiveData<Boolean>() val isEncrypted = MutableLiveData<Boolean>()
@ -105,6 +106,7 @@ class GroupInfoViewModel(val chatRoom: ChatRoom?) : ErrorReportingViewModel() {
} }
override fun onCleared() { override fun onCleared() {
participants.value.orEmpty().forEach(GroupInfoParticipantData::destroy)
chatRoom?.removeListener(listener) chatRoom?.removeListener(listener)
super.onCleared() super.onCleared()
@ -120,8 +122,8 @@ class GroupInfoViewModel(val chatRoom: ChatRoom?) : ErrorReportingViewModel() {
val addresses = arrayOfNulls<Address>(participants.value.orEmpty().size) val addresses = arrayOfNulls<Address>(participants.value.orEmpty().size)
var index = 0 var index = 0
for (participant in participants.value.orEmpty()) { for (participant in participants.value.orEmpty()) {
addresses[index] = participant.address addresses[index] = participant.participant.address
Log.i("[Chat Room Group Info] Participant ${participant.address.asStringUriOnly()} will be added to group") Log.i("[Chat Room Group Info] Participant ${participant.sipUri} will be added to group")
index += 1 index += 1
} }
@ -147,7 +149,7 @@ class GroupInfoViewModel(val chatRoom: ChatRoom?) : ErrorReportingViewModel() {
val participantsToRemove = arrayListOf<Participant>() val participantsToRemove = arrayListOf<Participant>()
for (participant in chatRoom.participants) { for (participant in chatRoom.participants) {
val member = participants.value.orEmpty().find { member -> val member = participants.value.orEmpty().find { member ->
participant.address.weakEqual(member.address) participant.address.weakEqual(member.participant.address)
} }
if (member == null) { if (member == null) {
Log.w("[Chat Room Group Info] Participant ${participant.address.asStringUriOnly()} will be removed from group") Log.w("[Chat Room Group Info] Participant ${participant.address.asStringUriOnly()} will be removed from group")
@ -162,19 +164,19 @@ class GroupInfoViewModel(val chatRoom: ChatRoom?) : ErrorReportingViewModel() {
val participantsToAdd = arrayListOf<Address>() val participantsToAdd = arrayListOf<Address>()
for (member in participants.value.orEmpty()) { for (member in participants.value.orEmpty()) {
val participant = chatRoom.participants.find { participant -> val participant = chatRoom.participants.find { participant ->
participant.address.weakEqual(member.address) participant.address.weakEqual(member.participant.address)
} }
if (participant != null) { if (participant != null) {
// Participant found, check if admin status needs to be updated // Participant found, check if admin status needs to be updated
if (member.isAdmin != participant.isAdmin) { if (member.participant.isAdmin != participant.isAdmin) {
if (chatRoom.me?.isAdmin == true) { if (chatRoom.me?.isAdmin == true) {
Log.i("[Chat Room Group Info] Participant ${member.address.asStringUriOnly()} will be admin? ${member.isAdmin}") Log.i("[Chat Room Group Info] Participant ${member.sipUri} will be admin? ${member.isAdmin}")
chatRoom.setParticipantAdminStatus(participant, member.isAdmin) chatRoom.setParticipantAdminStatus(participant, member.participant.isAdmin)
} }
} }
} else { } else {
Log.i("[Chat Room Group Info] Participant ${member.address.asStringUriOnly()} will be added to group") Log.i("[Chat Room Group Info] Participant ${member.sipUri} will be added to group")
participantsToAdd.add(member.address) participantsToAdd.add(member.participant.address)
} }
} }
val toAdd = arrayOfNulls<Address>(participantsToAdd.size) val toAdd = arrayOfNulls<Address>(participantsToAdd.size)
@ -195,18 +197,23 @@ class GroupInfoViewModel(val chatRoom: ChatRoom?) : ErrorReportingViewModel() {
} }
fun removeParticipant(participant: GroupChatRoomMember) { fun removeParticipant(participant: GroupChatRoomMember) {
val list = arrayListOf<GroupChatRoomMember>() val list = arrayListOf<GroupInfoParticipantData>()
list.addAll(participants.value.orEmpty()) for (data in participants.value.orEmpty()) {
list.remove(participant) if (!data.participant.address.weakEqual(participant.address)) {
list.add(data)
}
}
participants.value = list participants.value = list
} }
private fun updateParticipants() { private fun updateParticipants() {
val list = arrayListOf<GroupChatRoomMember>() val list = arrayListOf<GroupInfoParticipantData>()
if (chatRoom != null) { if (chatRoom != null) {
for (participant in chatRoom.participants) { for (participant in chatRoom.participants) {
list.add(GroupChatRoomMember(participant.address, participant.isAdmin, participant.securityLevel, canBeSetAdmin = true)) list.add(GroupInfoParticipantData(
GroupChatRoomMember(participant.address, participant.isAdmin, participant.securityLevel, canBeSetAdmin = true)
))
} }
} }

View file

@ -23,6 +23,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import org.linphone.activities.main.chat.data.ChatMessageData import org.linphone.activities.main.chat.data.ChatMessageData
import org.linphone.activities.main.chat.data.ImdnParticipantData
import org.linphone.core.ChatMessage import org.linphone.core.ChatMessage
import org.linphone.core.ChatMessageListenerStub import org.linphone.core.ChatMessageListenerStub
import org.linphone.core.ParticipantImdnState import org.linphone.core.ParticipantImdnState
@ -37,7 +38,7 @@ class ImdnViewModelFactory(private val chatMessage: ChatMessage) :
} }
class ImdnViewModel(private val chatMessage: ChatMessage) : ViewModel() { class ImdnViewModel(private val chatMessage: ChatMessage) : ViewModel() {
val participants = MutableLiveData<ArrayList<ParticipantImdnState>>() val participants = MutableLiveData<ArrayList<ImdnParticipantData>>()
val chatMessageViewModel = ChatMessageData(chatMessage) val chatMessageViewModel = ChatMessageData(chatMessage)
@ -56,16 +57,27 @@ class ImdnViewModel(private val chatMessage: ChatMessage) : ViewModel() {
} }
override fun onCleared() { override fun onCleared() {
participants.value.orEmpty().forEach(ImdnParticipantData::destroy)
chatMessage.removeListener(listener) chatMessage.removeListener(listener)
super.onCleared() super.onCleared()
} }
private fun updateParticipantsLists() { private fun updateParticipantsLists() {
val list = arrayListOf<ParticipantImdnState>() val list = arrayListOf<ImdnParticipantData>()
list.addAll(chatMessage.getParticipantsByImdnState(ChatMessage.State.Displayed))
list.addAll(chatMessage.getParticipantsByImdnState(ChatMessage.State.DeliveredToUser)) for (participant in chatMessage.getParticipantsByImdnState(ChatMessage.State.Displayed)) {
list.addAll(chatMessage.getParticipantsByImdnState(ChatMessage.State.Delivered)) list.add(ImdnParticipantData(participant))
list.addAll(chatMessage.getParticipantsByImdnState(ChatMessage.State.NotDelivered)) }
for (participant in chatMessage.getParticipantsByImdnState(ChatMessage.State.DeliveredToUser)) {
list.add(ImdnParticipantData(participant))
}
for (participant in chatMessage.getParticipantsByImdnState(ChatMessage.State.Delivered)) {
list.add(ImdnParticipantData(participant))
}
for (participant in chatMessage.getParticipantsByImdnState(ChatMessage.State.NotDelivered)) {
list.add(ImdnParticipantData(participant))
}
participants.value = list participants.value = list
} }
} }

View file

@ -42,7 +42,7 @@ import org.linphone.utils.HeaderAdapter
class ContactsListAdapter( class ContactsListAdapter(
selectionVM: ListTopBarViewModel, selectionVM: ListTopBarViewModel,
private val viewLifecycleOwner: LifecycleOwner private val viewLifecycleOwner: LifecycleOwner
) : SelectionListAdapter<Contact, RecyclerView.ViewHolder>(selectionVM, ContactDiffCallback()), HeaderAdapter { ) : SelectionListAdapter<ContactViewModel, RecyclerView.ViewHolder>(selectionVM, ContactDiffCallback()), HeaderAdapter {
val selectedContactEvent: MutableLiveData<Event<Contact>> by lazy { val selectedContactEvent: MutableLiveData<Event<Contact>> by lazy {
MutableLiveData<Event<Contact>>() MutableLiveData<Event<Contact>>()
} }
@ -59,16 +59,11 @@ class ContactsListAdapter(
(holder as ViewHolder).bind(getItem(position)) (holder as ViewHolder).bind(getItem(position))
} }
override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
(holder as ViewHolder).binding.viewModel?.destroy()
}
inner class ViewHolder( inner class ViewHolder(
val binding: ContactListCellBinding val binding: ContactListCellBinding
) : RecyclerView.ViewHolder(binding.root) { ) : RecyclerView.ViewHolder(binding.root) {
fun bind(contact: Contact) { fun bind(contactViewModel: ContactViewModel) {
with(binding) { with(binding) {
val contactViewModel = ContactViewModel(contact)
viewModel = contactViewModel viewModel = contactViewModel
lifecycleOwner = viewLifecycleOwner lifecycleOwner = viewLifecycleOwner
@ -83,7 +78,7 @@ class ContactsListAdapter(
if (selectionViewModel.isEditionEnabled.value == true) { if (selectionViewModel.isEditionEnabled.value == true) {
selectionViewModel.onToggleSelect(adapterPosition) selectionViewModel.onToggleSelect(adapterPosition)
} else { } else {
selectedContactEvent.value = Event(contact) selectedContactEvent.value = Event(contactViewModel.contactInternal)
} }
} }
@ -104,17 +99,17 @@ class ContactsListAdapter(
override fun displayHeaderForPosition(position: Int): Boolean { override fun displayHeaderForPosition(position: Int): Boolean {
if (position >= itemCount) return false if (position >= itemCount) return false
val contact = getItem(position) val contact = getItem(position)
val firstLetter = contact.fullName?.first().toString() val firstLetter = contact.displayName.first().toString()
val previousPosition = position - 1 val previousPosition = position - 1
return if (previousPosition >= 0) { return if (previousPosition >= 0) {
val previousItemFirstLetter = getItem(previousPosition).fullName?.first().toString() val previousItemFirstLetter = getItem(previousPosition).displayName.first().toString()
previousItemFirstLetter != firstLetter previousItemFirstLetter != firstLetter
} else true } else true
} }
override fun getHeaderViewForPosition(context: Context, position: Int): View { override fun getHeaderViewForPosition(context: Context, position: Int): View {
val contact = getItem(position) val contact = getItem(position)
val firstLetter = AppUtils.getInitials(contact.fullName ?: "", 1) val firstLetter = AppUtils.getInitials(contact.displayName, 1)
val binding: GenericListHeaderBinding = DataBindingUtil.inflate( val binding: GenericListHeaderBinding = DataBindingUtil.inflate(
LayoutInflater.from(context), LayoutInflater.from(context),
R.layout.generic_list_header, null, false R.layout.generic_list_header, null, false
@ -125,17 +120,17 @@ class ContactsListAdapter(
} }
} }
private class ContactDiffCallback : DiffUtil.ItemCallback<Contact>() { private class ContactDiffCallback : DiffUtil.ItemCallback<ContactViewModel>() {
override fun areItemsTheSame( override fun areItemsTheSame(
oldItem: Contact, oldItem: ContactViewModel,
newItem: Contact newItem: ContactViewModel
): Boolean { ): Boolean {
return oldItem.compareTo(newItem) == 0 return oldItem.contactInternal.compareTo(newItem.contactInternal) == 0
} }
override fun areContentsTheSame( override fun areContentsTheSame(
oldItem: Contact, oldItem: ContactViewModel,
newItem: Contact newItem: ContactViewModel
): Boolean { ): Boolean {
return false // For headers return false // For headers
} }

View file

@ -110,7 +110,7 @@ class MasterContactsFragment : MasterFragment<ContactMasterFragmentBinding, Cont
} }
viewModel.showDeleteButton({ viewModel.showDeleteButton({
listViewModel.deleteContact(adapter.currentList[viewHolder.adapterPosition]) listViewModel.deleteContact(adapter.currentList[viewHolder.adapterPosition].contactInternal)
dialog.dismiss() dialog.dismiss()
}, getString(R.string.dialog_delete)) }, getString(R.string.dialog_delete))
@ -219,7 +219,7 @@ class MasterContactsFragment : MasterFragment<ContactMasterFragmentBinding, Cont
override fun deleteItems(indexesOfItemToDelete: ArrayList<Int>) { override fun deleteItems(indexesOfItemToDelete: ArrayList<Int>) {
val list = ArrayList<Contact>() val list = ArrayList<Contact>()
for (index in indexesOfItemToDelete) { for (index in indexesOfItemToDelete) {
val contact = adapter.currentList[index] val contact = adapter.currentList[index].contactInternal
list.add(contact) list.add(contact)
} }
listViewModel.deleteContacts(list) listViewModel.deleteContacts(list)

View file

@ -48,11 +48,11 @@ class ContactViewModelFactory(private val contact: Contact) :
} }
} }
class ContactViewModel(private val c: Contact) : ErrorReportingViewModel(), ContactDataInterface { class ContactViewModel(val contactInternal: Contact) : ErrorReportingViewModel(), ContactDataInterface {
override val contact = MutableLiveData<Contact>() override val contact = MutableLiveData<Contact>()
override val displayName: String by lazy { override val displayName: String by lazy {
c.fullName ?: c.firstName + " " + c.lastName contactInternal.fullName ?: contactInternal.firstName + " " + contactInternal.lastName
} }
val displayOrganization = corePreferences.displayOrganization val displayOrganization = corePreferences.displayOrganization
@ -75,7 +75,7 @@ class ContactViewModel(private val c: Contact) : ErrorReportingViewModel(), Cont
private val contactsUpdatedListener = object : ContactsUpdatedListenerStub() { private val contactsUpdatedListener = object : ContactsUpdatedListenerStub() {
override fun onContactUpdated(contact: Contact) { override fun onContactUpdated(contact: Contact) {
if (c is NativeContact && contact is NativeContact && c.nativeId == contact.nativeId) { if (contact is NativeContact && contactInternal is NativeContact && contact.nativeId == contactInternal.nativeId) {
Log.d("[Contact] $contact has changed") Log.d("[Contact] $contact has changed")
updateNumbersAndAddresses(contact) updateNumbersAndAddresses(contact)
} }
@ -124,8 +124,8 @@ class ContactViewModel(private val c: Contact) : ErrorReportingViewModel(), Cont
} }
init { init {
contact.value = c contact.value = contactInternal
updateNumbersAndAddresses(c) updateNumbersAndAddresses(contactInternal)
coreContext.contactsManager.addListener(contactsUpdatedListener) coreContext.contactsManager.addListener(contactsUpdatedListener)
waitForChatRoomCreation.value = false waitForChatRoomCreation.value = false
} }
@ -143,8 +143,8 @@ class ContactViewModel(private val c: Contact) : ErrorReportingViewModel(), Cont
val select = ContactsContract.Data.CONTACT_ID + " = ?" val select = ContactsContract.Data.CONTACT_ID + " = ?"
val ops = java.util.ArrayList<ContentProviderOperation>() val ops = java.util.ArrayList<ContentProviderOperation>()
if (c is NativeContact) { if (contactInternal is NativeContact) {
val nativeContact: NativeContact = c val nativeContact: NativeContact = contactInternal
Log.i("[Contact] Setting Android contact id ${nativeContact.nativeId} to batch removal") Log.i("[Contact] Setting Android contact id ${nativeContact.nativeId} to batch removal")
val args = arrayOf(nativeContact.nativeId) val args = arrayOf(nativeContact.nativeId)
ops.add( ops.add(
@ -154,9 +154,9 @@ class ContactViewModel(private val c: Contact) : ErrorReportingViewModel(), Cont
) )
} }
if (c.friend != null) { if (contactInternal.friend != null) {
Log.i("[Contact] Removing friend") Log.i("[Contact] Removing friend")
c.friend?.remove() contactInternal.friend?.remove()
} }
if (ops.isNotEmpty()) { if (ops.isNotEmpty()) {

View file

@ -33,7 +33,7 @@ import org.linphone.core.tools.Log
class ContactsListViewModel : ViewModel() { class ContactsListViewModel : ViewModel() {
val sipContactsSelected = MutableLiveData<Boolean>() val sipContactsSelected = MutableLiveData<Boolean>()
val contactsList = MutableLiveData<ArrayList<Contact>>() val contactsList = MutableLiveData<ArrayList<ContactViewModel>>()
val filter = MutableLiveData<String>() val filter = MutableLiveData<String>()
@ -51,23 +51,31 @@ class ContactsListViewModel : ViewModel() {
} }
override fun onCleared() { override fun onCleared() {
contactsList.value.orEmpty().forEach(ContactViewModel::destroy)
coreContext.contactsManager.removeListener(contactsUpdatedListener) coreContext.contactsManager.removeListener(contactsUpdatedListener)
super.onCleared() super.onCleared()
} }
private fun getSelectedContactsList(): ArrayList<Contact> { private fun getSelectedContactsList(): ArrayList<ContactViewModel> {
return if (sipContactsSelected.value == true) coreContext.contactsManager.sipContacts else coreContext.contactsManager.contacts val list = arrayListOf<ContactViewModel>()
val source =
if (sipContactsSelected.value == true) coreContext.contactsManager.sipContacts
else coreContext.contactsManager.contacts
for (contact in source) {
list.add(ContactViewModel(contact))
}
return list
} }
fun updateContactsList() { fun updateContactsList() {
val list: ArrayList<Contact> val list: ArrayList<ContactViewModel>
val filterValue = filter.value.orEmpty() val filterValue = filter.value.orEmpty()
list = if (filterValue.isNotEmpty()) { list = if (filterValue.isNotEmpty()) {
getSelectedContactsList().filter { contact -> getSelectedContactsList().filter { contact ->
contact.fullName?.contains(filterValue, true) ?: false contact.displayName.contains(filterValue, true) ?: false
} as ArrayList<Contact> } as ArrayList<ContactViewModel>
} else { } else {
getSelectedContactsList() getSelectedContactsList()
} }

View file

@ -31,7 +31,6 @@ 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.history.data.GroupedCallLogData import org.linphone.activities.main.history.data.GroupedCallLogData
import org.linphone.activities.main.history.viewmodels.CallLogViewModel
import org.linphone.activities.main.viewmodels.ListTopBarViewModel import org.linphone.activities.main.viewmodels.ListTopBarViewModel
import org.linphone.databinding.GenericListHeaderBinding import org.linphone.databinding.GenericListHeaderBinding
import org.linphone.databinding.HistoryListCellBinding import org.linphone.databinding.HistoryListCellBinding
@ -61,16 +60,12 @@ class CallLogsListAdapter(
(holder as ViewHolder).bind(getItem(position)) (holder as ViewHolder).bind(getItem(position))
} }
override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
(holder as ViewHolder).binding.viewModel?.destroy()
}
inner class ViewHolder( inner class ViewHolder(
val binding: HistoryListCellBinding val binding: HistoryListCellBinding
) : RecyclerView.ViewHolder(binding.root) { ) : RecyclerView.ViewHolder(binding.root) {
fun bind(callLogGroup: GroupedCallLogData) { fun bind(callLogGroup: GroupedCallLogData) {
with(binding) { with(binding) {
val callLogViewModel = CallLogViewModel(callLogGroup.lastCallLog) val callLogViewModel = callLogGroup.lastCallLogViewModel
viewModel = callLogViewModel viewModel = callLogViewModel
lifecycleOwner = viewLifecycleOwner lifecycleOwner = viewLifecycleOwner

View file

@ -19,9 +19,15 @@
*/ */
package org.linphone.activities.main.history.data package org.linphone.activities.main.history.data
import org.linphone.activities.main.history.viewmodels.CallLogViewModel
import org.linphone.core.CallLog import org.linphone.core.CallLog
class GroupedCallLogData(callLog: CallLog) { class GroupedCallLogData(callLog: CallLog) {
var lastCallLog: CallLog = callLog var lastCallLog: CallLog = callLog
val callLogs = arrayListOf(callLog) val callLogs = arrayListOf(callLog)
val lastCallLogViewModel = CallLogViewModel(lastCallLog)
fun destroy() {
lastCallLogViewModel.destroy()
}
} }

View file

@ -67,6 +67,9 @@ class CallLogsListViewModel : ViewModel() {
} }
override fun onCleared() { override fun onCleared() {
callLogs.value.orEmpty().forEach(GroupedCallLogData::destroy)
missedCallLogs.value.orEmpty().forEach(GroupedCallLogData::destroy)
coreContext.contactsManager.removeListener(contactsUpdatedListener) coreContext.contactsManager.removeListener(contactsUpdatedListener)
coreContext.core.removeListener(listener) coreContext.core.removeListener(listener)

View file

@ -63,10 +63,6 @@ class RecordingsListAdapter(
(holder as ViewHolder).bind(getItem(position)) (holder as ViewHolder).bind(getItem(position))
} }
override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
(holder as ViewHolder).binding.data?.destroy()
}
inner class ViewHolder( inner class ViewHolder(
val binding: RecordingListCellBinding val binding: RecordingListCellBinding
) : RecyclerView.ViewHolder(binding.root) { ) : RecyclerView.ViewHolder(binding.root) {

View file

@ -36,6 +36,11 @@ class RecordingsViewModel : ViewModel() {
isVideoVisible.value = false isVideoVisible.value = false
} }
override fun onCleared() {
recordingsList.value.orEmpty().forEach(RecordingData::destroy)
super.onCleared()
}
fun deleteRecordings(list: ArrayList<RecordingData>) { fun deleteRecordings(list: ArrayList<RecordingData>) {
for (recording in list) { for (recording in list) {
Log.i("[Recordings] Deleting recording ${recording.path}") Log.i("[Recordings] Deleting recording ${recording.path}")