diff --git a/app/src/main/java/org/linphone/activities/main/chat/adapters/ChatMessagesListAdapter.kt b/app/src/main/java/org/linphone/activities/main/chat/adapters/ChatMessagesListAdapter.kt index e92fd0966..4f07a53ca 100644 --- a/app/src/main/java/org/linphone/activities/main/chat/adapters/ChatMessagesListAdapter.kt +++ b/app/src/main/java/org/linphone/activities/main/chat/adapters/ChatMessagesListAdapter.kt @@ -27,8 +27,10 @@ import android.view.MenuItem import android.view.ViewGroup import androidx.appcompat.widget.PopupMenu import androidx.databinding.DataBindingUtil +import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.MutableLiveData import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.RecyclerView import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.corePreferences import org.linphone.R @@ -42,10 +44,12 @@ import org.linphone.core.EventLog import org.linphone.databinding.ChatEventListCellBinding import org.linphone.databinding.ChatMessageListCellBinding import org.linphone.utils.Event -import org.linphone.utils.LifecycleListAdapter -import org.linphone.utils.LifecycleViewHolder +import org.linphone.utils.SelectionListAdapter -class ChatMessagesListAdapter(val selectionViewModel: ListTopBarViewModel) : LifecycleListAdapter(ChatMessageDiffCallback()) { +class ChatMessagesListAdapter( + selectionVM: ListTopBarViewModel, + private val viewLifecycleOwner: LifecycleOwner +) : SelectionListAdapter(selectionVM, ChatMessageDiffCallback()) { companion object { const val MAX_TIME_TO_GROUP_MESSAGES = 60 // 1 minute } @@ -80,7 +84,7 @@ class ChatMessagesListAdapter(val selectionViewModel: ListTopBarViewModel) : Lif } } - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LifecycleViewHolder { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return when (viewType) { EventLog.Type.ConferenceChatMessage.toInt() -> createChatMessageViewHolder(parent) else -> createEventViewHolder(parent) @@ -92,9 +96,7 @@ class ChatMessagesListAdapter(val selectionViewModel: ListTopBarViewModel) : Lif LayoutInflater.from(parent.context), R.layout.chat_message_list_cell, parent, false ) - val viewHolder = ChatMessageViewHolder(binding) - binding.lifecycleOwner = viewHolder - return viewHolder + return ChatMessageViewHolder(binding) } private fun createEventViewHolder(parent: ViewGroup): EventViewHolder { @@ -102,12 +104,10 @@ class ChatMessagesListAdapter(val selectionViewModel: ListTopBarViewModel) : Lif LayoutInflater.from(parent.context), R.layout.chat_event_list_cell, parent, false ) - val viewHolder = EventViewHolder(binding) - binding.lifecycleOwner = viewHolder - return viewHolder + return EventViewHolder(binding) } - override fun onBindViewHolder(holder: LifecycleViewHolder, position: Int) { + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { val eventLog = getItem(position) when (holder) { is ChatMessageViewHolder -> holder.bind(eventLog) @@ -122,7 +122,7 @@ class ChatMessagesListAdapter(val selectionViewModel: ListTopBarViewModel) : Lif inner class ChatMessageViewHolder( private val binding: ChatMessageListCellBinding - ) : LifecycleViewHolder(binding), PopupMenu.OnMenuItemClickListener { + ) : RecyclerView.ViewHolder(binding.root), PopupMenu.OnMenuItemClickListener { fun bind(eventLog: EventLog) { with(binding) { if (eventLog.type == EventLog.Type.ConferenceChatMessage) { @@ -131,9 +131,11 @@ class ChatMessagesListAdapter(val selectionViewModel: ListTopBarViewModel) : Lif val chatMessageViewModel = ChatMessageViewModel(chatMessage, contentClickedListener) viewModel = chatMessageViewModel + binding.lifecycleOwner = viewLifecycleOwner + // This is for item selection through ListTopBarFragment selectionListViewModel = selectionViewModel - selectionViewModel.isEditionEnabled.observe(this@ChatMessageViewHolder, { + selectionViewModel.isEditionEnabled.observe(viewLifecycleOwner, { position = adapterPosition }) @@ -284,15 +286,17 @@ class ChatMessagesListAdapter(val selectionViewModel: ListTopBarViewModel) : Lif inner class EventViewHolder( private val binding: ChatEventListCellBinding - ) : LifecycleViewHolder(binding) { + ) : RecyclerView.ViewHolder(binding.root) { fun bind(eventLog: EventLog) { with(binding) { val eventViewModel = EventViewModel(eventLog) viewModel = eventViewModel + binding.lifecycleOwner = viewLifecycleOwner + // This is for item selection through ListTopBarFragment selectionListViewModel = selectionViewModel - selectionViewModel.isEditionEnabled.observe(this@EventViewHolder, { + selectionViewModel.isEditionEnabled.observe(viewLifecycleOwner, { position = adapterPosition }) diff --git a/app/src/main/java/org/linphone/activities/main/chat/adapters/ChatRoomCreationContactsAdapter.kt b/app/src/main/java/org/linphone/activities/main/chat/adapters/ChatRoomCreationContactsAdapter.kt index 2f1172589..cb227f36c 100644 --- a/app/src/main/java/org/linphone/activities/main/chat/adapters/ChatRoomCreationContactsAdapter.kt +++ b/app/src/main/java/org/linphone/activities/main/chat/adapters/ChatRoomCreationContactsAdapter.kt @@ -22,8 +22,11 @@ package org.linphone.activities.main.chat.adapters import android.view.LayoutInflater import android.view.ViewGroup import androidx.databinding.DataBindingUtil +import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.MutableLiveData import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.R import org.linphone.activities.main.chat.viewmodels.ChatRoomCreationContactViewModel @@ -32,51 +35,57 @@ import org.linphone.core.FriendCapability import org.linphone.core.SearchResult import org.linphone.databinding.ChatRoomCreationContactCellBinding import org.linphone.utils.Event -import org.linphone.utils.LifecycleListAdapter -import org.linphone.utils.LifecycleViewHolder -class ChatRoomCreationContactsAdapter : LifecycleListAdapter(SearchResultDiffCallback()) { +class ChatRoomCreationContactsAdapter( + private val viewLifecycleOwner: LifecycleOwner +) : ListAdapter(SearchResultDiffCallback()) { val selectedContact = MutableLiveData>() - val selectedAddresses = MutableLiveData>() - var groupChatEnabled: Boolean = false - val securityEnabled = MutableLiveData() + private var selectedAddresses = ArrayList
() - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + private var securityEnabled: Boolean = false + + fun updateSelectedAddresses(selection: ArrayList
) { + selectedAddresses = selection + notifyDataSetChanged() + } + + fun updateSecurity(enabled: Boolean) { + securityEnabled = enabled + notifyDataSetChanged() + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val binding: ChatRoomCreationContactCellBinding = DataBindingUtil.inflate( LayoutInflater.from(parent.context), R.layout.chat_room_creation_contact_cell, parent, false ) - val viewHolder = ViewHolder(binding) - binding.lifecycleOwner = viewHolder - return viewHolder + return ViewHolder(binding) } - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - holder.bind(getItem(position)) + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + (holder as ViewHolder).bind(getItem(position)) } inner class ViewHolder( private val binding: ChatRoomCreationContactCellBinding - ) : LifecycleViewHolder(binding) { + ) : RecyclerView.ViewHolder(binding.root) { fun bind(searchResult: SearchResult) { with(binding) { val searchResultViewModel = ChatRoomCreationContactViewModel(searchResult) viewModel = searchResultViewModel - securityEnabled.observe(this@ViewHolder, { - updateSecurity(searchResult, searchResultViewModel, it) - }) + binding.lifecycleOwner = viewLifecycleOwner - selectedAddresses.observe(this@ViewHolder, { - val selected = it.find { address -> - val searchAddress = searchResult.address - if (searchAddress != null) address.weakEqual(searchAddress) else false - } - searchResultViewModel.isSelected.value = selected != null - }) + updateSecurity(searchResult, searchResultViewModel, securityEnabled) + + val selected = selectedAddresses.find { address -> + val searchAddress = searchResult.address + if (searchAddress != null) address.weakEqual(searchAddress) else false + } + searchResultViewModel.isSelected.value = selected != null setClickListener { selectedContact.value = Event(searchResult) diff --git a/app/src/main/java/org/linphone/activities/main/chat/adapters/ChatRoomsListAdapter.kt b/app/src/main/java/org/linphone/activities/main/chat/adapters/ChatRoomsListAdapter.kt index 3bcf1e97a..f6885c1f3 100644 --- a/app/src/main/java/org/linphone/activities/main/chat/adapters/ChatRoomsListAdapter.kt +++ b/app/src/main/java/org/linphone/activities/main/chat/adapters/ChatRoomsListAdapter.kt @@ -22,47 +22,55 @@ package org.linphone.activities.main.chat.adapters import android.view.LayoutInflater import android.view.ViewGroup import androidx.databinding.DataBindingUtil +import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.MutableLiveData import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.RecyclerView import org.linphone.R import org.linphone.activities.main.chat.viewmodels.ChatRoomViewModel import org.linphone.activities.main.viewmodels.ListTopBarViewModel import org.linphone.core.ChatRoom import org.linphone.databinding.ChatRoomListCellBinding import org.linphone.utils.Event -import org.linphone.utils.LifecycleListAdapter -import org.linphone.utils.LifecycleViewHolder +import org.linphone.utils.SelectionListAdapter -class ChatRoomsListAdapter(val selectionViewModel: ListTopBarViewModel) : LifecycleListAdapter(ChatRoomDiffCallback()) { +class ChatRoomsListAdapter( + selectionVM: ListTopBarViewModel, + private val viewLifecycleOwner: LifecycleOwner +) : SelectionListAdapter(selectionVM, ChatRoomDiffCallback()) { val selectedChatRoomEvent: MutableLiveData> by lazy { MutableLiveData>() } + val toggledPositionForSelectionEvent: MutableLiveData> by lazy { + MutableLiveData>() + } + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val binding: ChatRoomListCellBinding = DataBindingUtil.inflate( LayoutInflater.from(parent.context), R.layout.chat_room_list_cell, parent, false ) - val viewHolder = ViewHolder(binding) - binding.lifecycleOwner = viewHolder - return viewHolder + return ViewHolder(binding) } - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - holder.bind(getItem(position)) + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + (holder as ViewHolder).bind(getItem(position)) } inner class ViewHolder( private val binding: ChatRoomListCellBinding - ) : LifecycleViewHolder(binding) { + ) : RecyclerView.ViewHolder(binding.root) { fun bind(chatRoom: ChatRoom) { with(binding) { val chatRoomViewModel = ChatRoomViewModel(chatRoom) viewModel = chatRoomViewModel + binding.lifecycleOwner = viewLifecycleOwner + // This is for item selection through ListTopBarFragment selectionListViewModel = selectionViewModel - selectionViewModel.isEditionEnabled.observe(this@ViewHolder, { + selectionViewModel.isEditionEnabled.observe(viewLifecycleOwner, { position = adapterPosition }) diff --git a/app/src/main/java/org/linphone/activities/main/chat/adapters/GroupInfoParticipantsAdapter.kt b/app/src/main/java/org/linphone/activities/main/chat/adapters/GroupInfoParticipantsAdapter.kt index b5af4b208..ebad1ef2f 100644 --- a/app/src/main/java/org/linphone/activities/main/chat/adapters/GroupInfoParticipantsAdapter.kt +++ b/app/src/main/java/org/linphone/activities/main/chat/adapters/GroupInfoParticipantsAdapter.kt @@ -22,18 +22,21 @@ package org.linphone.activities.main.chat.adapters import android.view.LayoutInflater import android.view.ViewGroup import androidx.databinding.DataBindingUtil +import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.MutableLiveData import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView import org.linphone.R import org.linphone.activities.main.chat.GroupChatRoomMember import org.linphone.activities.main.chat.viewmodels.GroupInfoParticipantViewModel import org.linphone.databinding.ChatRoomGroupInfoParticipantCellBinding import org.linphone.utils.Event -import org.linphone.utils.LifecycleListAdapter -import org.linphone.utils.LifecycleViewHolder -class GroupInfoParticipantsAdapter(private val isEncryptionEnabled: Boolean) : LifecycleListAdapter(ParticipantDiffCallback()) { +class GroupInfoParticipantsAdapter( + private val viewLifecycleOwner: LifecycleOwner, + private val isEncryptionEnabled: Boolean +) : ListAdapter(ParticipantDiffCallback()) { private var showAdmin: Boolean = false val participantRemovedEvent: MutableLiveData> by lazy { @@ -45,13 +48,11 @@ class GroupInfoParticipantsAdapter(private val isEncryptionEnabled: Boolean) : L LayoutInflater.from(parent.context), R.layout.chat_room_group_info_participant_cell, parent, false ) - val viewHolder = ViewHolder(binding) - binding.lifecycleOwner = viewHolder - return viewHolder + return ViewHolder(binding) } - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - holder.bind(getItem(position)) + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + (holder as ViewHolder).bind(getItem(position)) } fun showAdminControls(show: Boolean) { @@ -61,13 +62,15 @@ class GroupInfoParticipantsAdapter(private val isEncryptionEnabled: Boolean) : L inner class ViewHolder( private val binding: ChatRoomGroupInfoParticipantCellBinding - ) : LifecycleViewHolder(binding) { + ) : RecyclerView.ViewHolder(binding.root) { fun bind(participant: GroupChatRoomMember) { with(binding) { val participantViewModel = GroupInfoParticipantViewModel(participant) participantViewModel.showAdminControls.value = showAdmin viewModel = participantViewModel + binding.lifecycleOwner = viewLifecycleOwner + setRemoveClickListener { participantRemovedEvent.value = Event(participant) } diff --git a/app/src/main/java/org/linphone/activities/main/chat/adapters/ImdnAdapter.kt b/app/src/main/java/org/linphone/activities/main/chat/adapters/ImdnAdapter.kt index 146cea8d3..b07dc9c87 100644 --- a/app/src/main/java/org/linphone/activities/main/chat/adapters/ImdnAdapter.kt +++ b/app/src/main/java/org/linphone/activities/main/chat/adapters/ImdnAdapter.kt @@ -24,8 +24,10 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.databinding.DataBindingUtil +import androidx.lifecycle.LifecycleOwner import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView import org.linphone.R import org.linphone.activities.main.chat.viewmodels.ImdnParticipantViewModel import org.linphone.core.ChatMessage @@ -33,31 +35,30 @@ import org.linphone.core.ParticipantImdnState import org.linphone.databinding.ChatRoomImdnParticipantCellBinding import org.linphone.databinding.ImdnListHeaderBinding import org.linphone.utils.HeaderAdapter -import org.linphone.utils.LifecycleViewHolder -class ImdnAdapter : ListAdapter(ParticipantImdnStateDiffCallback()), HeaderAdapter { +class ImdnAdapter( + private val viewLifecycleOwner: LifecycleOwner +) : ListAdapter(ParticipantImdnStateDiffCallback()), HeaderAdapter { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val binding: ChatRoomImdnParticipantCellBinding = DataBindingUtil.inflate( LayoutInflater.from(parent.context), R.layout.chat_room_imdn_participant_cell, parent, false ) - val viewHolder = ViewHolder(binding) - binding.lifecycleOwner = viewHolder - return viewHolder + return ViewHolder(binding) } - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - holder.bind(getItem(position)) + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + (holder as ViewHolder).bind(getItem(position)) } - class ViewHolder( + inner class ViewHolder( private val binding: ChatRoomImdnParticipantCellBinding - ) : LifecycleViewHolder(binding) { + ) : RecyclerView.ViewHolder(binding.root) { fun bind(participantImdnState: ParticipantImdnState) { with(binding) { - val imdnViewModel = ImdnParticipantViewModel(participantImdnState) - viewModel = imdnViewModel + viewModel = ImdnParticipantViewModel(participantImdnState) + + binding.lifecycleOwner = viewLifecycleOwner executePendingBindings() } diff --git a/app/src/main/java/org/linphone/activities/main/chat/fragments/ChatRoomCreationFragment.kt b/app/src/main/java/org/linphone/activities/main/chat/fragments/ChatRoomCreationFragment.kt index e8bccdef1..bb1748469 100644 --- a/app/src/main/java/org/linphone/activities/main/chat/fragments/ChatRoomCreationFragment.kt +++ b/app/src/main/java/org/linphone/activities/main/chat/fragments/ChatRoomCreationFragment.kt @@ -59,9 +59,9 @@ class ChatRoomCreationFragment : GenericFragment() { +class DetailChatRoomFragment : MasterFragment() { private lateinit var viewModel: ChatRoomViewModel private lateinit var chatSendingViewModel: ChatMessageSendingViewModel private lateinit var listViewModel: ChatMessagesListViewModel - private lateinit var adapter: ChatMessagesListAdapter private lateinit var sharedViewModel: SharedMainViewModel + + private val observer = object : RecyclerView.AdapterDataObserver() { + override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { + if (positionStart == adapter.itemCount - 1) { + adapter.notifyItemChanged(positionStart - 1) // For grouping purposes + scrollToBottom() + } + } + } + private var chatRoomAddress: String? = null override fun getLayoutId(): Int { return R.layout.chat_room_detail_fragment } + override fun onDestroyView() { + binding.chatMessagesList.adapter = null + adapter.unregisterAdapterDataObserver(observer) + super.onDestroyView() + } + override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) @@ -98,18 +113,11 @@ class DetailChatRoomFragment : MasterFragment() { ChatMessagesListViewModelFactory(chatRoom) )[ChatMessagesListViewModel::class.java] - adapter = ChatMessagesListAdapter(listSelectionViewModel) + _adapter = ChatMessagesListAdapter(listSelectionViewModel, viewLifecycleOwner) // SubmitList is done on a background thread // We need this adapter data observer to know when to scroll - adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { - override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { - if (positionStart == adapter.itemCount - 1) { - adapter.notifyItemChanged(positionStart - 1) // For grouping purposes - scrollToBottom() - } - } - }) binding.chatMessagesList.adapter = adapter + adapter.registerAdapterDataObserver(observer) val layoutManager = LinearLayoutManager(activity) layoutManager.stackFromEnd = true @@ -265,7 +273,7 @@ class DetailChatRoomFragment : MasterFragment() { override fun deleteItems(indexesOfItemToDelete: ArrayList) { val list = ArrayList() for (index in indexesOfItemToDelete) { - val eventLog = adapter.getItemAt(index) + val eventLog = adapter.currentList[index] list.add(eventLog) } listViewModel.deleteEventLogs(list) diff --git a/app/src/main/java/org/linphone/activities/main/chat/fragments/GroupInfoFragment.kt b/app/src/main/java/org/linphone/activities/main/chat/fragments/GroupInfoFragment.kt index be6c0f209..f394a853f 100644 --- a/app/src/main/java/org/linphone/activities/main/chat/fragments/GroupInfoFragment.kt +++ b/app/src/main/java/org/linphone/activities/main/chat/fragments/GroupInfoFragment.kt @@ -67,7 +67,10 @@ class GroupInfoFragment : GenericFragment() { viewModel.isEncrypted.value = sharedViewModel.createEncryptedChatRoom - adapter = GroupInfoParticipantsAdapter(chatRoom?.hasCapability(ChatRoomCapabilities.Encrypted.toInt()) ?: viewModel.isEncrypted.value == true) + adapter = GroupInfoParticipantsAdapter( + viewLifecycleOwner, + chatRoom?.hasCapability(ChatRoomCapabilities.Encrypted.toInt()) ?: viewModel.isEncrypted.value == true + ) binding.participants.adapter = adapter val layoutManager = LinearLayoutManager(activity) diff --git a/app/src/main/java/org/linphone/activities/main/chat/fragments/ImdnFragment.kt b/app/src/main/java/org/linphone/activities/main/chat/fragments/ImdnFragment.kt index 69f7f9d9d..763382148 100644 --- a/app/src/main/java/org/linphone/activities/main/chat/fragments/ImdnFragment.kt +++ b/app/src/main/java/org/linphone/activities/main/chat/fragments/ImdnFragment.kt @@ -76,7 +76,7 @@ class ImdnFragment : GenericFragment() { return } - adapter = ImdnAdapter() + adapter = ImdnAdapter(viewLifecycleOwner) binding.participantsList.adapter = adapter val layoutManager = LinearLayoutManager(activity) diff --git a/app/src/main/java/org/linphone/activities/main/chat/fragments/MasterChatRoomsFragment.kt b/app/src/main/java/org/linphone/activities/main/chat/fragments/MasterChatRoomsFragment.kt index 11a7be924..5cec07e2f 100644 --- a/app/src/main/java/org/linphone/activities/main/chat/fragments/MasterChatRoomsFragment.kt +++ b/app/src/main/java/org/linphone/activities/main/chat/fragments/MasterChatRoomsFragment.kt @@ -43,14 +43,31 @@ import org.linphone.core.tools.Log import org.linphone.databinding.ChatRoomMasterFragmentBinding import org.linphone.utils.* -class MasterChatRoomsFragment : MasterFragment() { +class MasterChatRoomsFragment : MasterFragment() { override val dialogConfirmationMessageBeforeRemoval = R.plurals.chat_room_delete_dialog private lateinit var listViewModel: ChatRoomsListViewModel - private lateinit var adapter: ChatRoomsListAdapter private lateinit var sharedViewModel: SharedMainViewModel + private val observer = object : RecyclerView.AdapterDataObserver() { + override fun onChanged() { + scrollToTop() + } + override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { + scrollToTop() + } + override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) { + scrollToTop() + } + } + override fun getLayoutId(): Int = R.layout.chat_room_master_fragment + override fun onDestroyView() { + binding.chatList.adapter = null + adapter.unregisterAdapterDataObserver(observer) + super.onDestroyView() + } + override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) @@ -63,20 +80,10 @@ class MasterChatRoomsFragment : MasterFragment() ViewModelProvider(this).get(SharedMainViewModel::class.java) } ?: throw Exception("Invalid Activity") - adapter = ChatRoomsListAdapter(listSelectionViewModel) + _adapter = ChatRoomsListAdapter(listSelectionViewModel, viewLifecycleOwner) // SubmitList is done on a background thread // We need this adapter data observer to know when to scroll - adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { - override fun onChanged() { - scrollToTop() - } - override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { - scrollToTop() - } - override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) { - scrollToTop() - } - }) + adapter.registerAdapterDataObserver(observer) binding.chatList.adapter = adapter val layoutManager = LinearLayoutManager(activity) @@ -100,7 +107,7 @@ class MasterChatRoomsFragment : MasterFragment() } viewModel.showDeleteButton({ - listViewModel.deleteChatRoom(adapter.getItemAt(viewHolder.adapterPosition)) + listViewModel.deleteChatRoom(adapter.currentList[viewHolder.adapterPosition]) dialog.dismiss() }, getString(R.string.dialog_delete)) @@ -136,6 +143,12 @@ class MasterChatRoomsFragment : MasterFragment() } }) + adapter.toggledPositionForSelectionEvent.observe(viewLifecycleOwner, { + it.consume { position -> + listSelectionViewModel.onToggleSelect(position) + } + }) + binding.setEditClickListener { listSelectionViewModel.isEditionEnabled.value = true } @@ -201,7 +214,7 @@ class MasterChatRoomsFragment : MasterFragment() override fun deleteItems(indexesOfItemToDelete: ArrayList) { val list = ArrayList() for (index in indexesOfItemToDelete) { - val chatRoom = adapter.getItemAt(index) + val chatRoom = adapter.currentList[index] list.add(chatRoom) } listViewModel.deleteChatRooms(list) diff --git a/app/src/main/java/org/linphone/activities/main/chat/viewmodels/ChatMessageViewModel.kt b/app/src/main/java/org/linphone/activities/main/chat/viewmodels/ChatMessageViewModel.kt index 8b89b32d0..92958055f 100644 --- a/app/src/main/java/org/linphone/activities/main/chat/viewmodels/ChatMessageViewModel.kt +++ b/app/src/main/java/org/linphone/activities/main/chat/viewmodels/ChatMessageViewModel.kt @@ -40,7 +40,7 @@ import org.linphone.utils.TimestampUtils class ChatMessageViewModel( val chatMessage: ChatMessage, - private val contentListener: OnContentClickedListener? = null + private var contentListener: OnContentClickedListener? = null ) : GenericContactViewModel(chatMessage.fromAddress) { val sendInProgress = MutableLiveData() @@ -114,6 +114,7 @@ class ChatMessageViewModel( override fun onCleared() { chatMessage.removeListener(listener) + contentListener = null super.onCleared() } diff --git a/app/src/main/java/org/linphone/activities/main/contact/adapters/ContactsListAdapter.kt b/app/src/main/java/org/linphone/activities/main/contact/adapters/ContactsListAdapter.kt index e7556ad33..f0e59bd33 100644 --- a/app/src/main/java/org/linphone/activities/main/contact/adapters/ContactsListAdapter.kt +++ b/app/src/main/java/org/linphone/activities/main/contact/adapters/ContactsListAdapter.kt @@ -24,8 +24,10 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.databinding.DataBindingUtil +import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.MutableLiveData import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.RecyclerView import org.linphone.R import org.linphone.activities.main.contact.viewmodels.ContactViewModel import org.linphone.activities.main.viewmodels.ListTopBarViewModel @@ -34,10 +36,12 @@ import org.linphone.databinding.ContactListCellBinding import org.linphone.databinding.GenericListHeaderBinding import org.linphone.utils.Event import org.linphone.utils.HeaderAdapter -import org.linphone.utils.LifecycleListAdapter -import org.linphone.utils.LifecycleViewHolder +import org.linphone.utils.SelectionListAdapter -class ContactsListAdapter(val selectionViewModel: ListTopBarViewModel) : LifecycleListAdapter(ContactDiffCallback()), HeaderAdapter { +class ContactsListAdapter( + selectionVM: ListTopBarViewModel, + private val viewLifecycleOwner: LifecycleOwner +) : SelectionListAdapter(selectionVM, ContactDiffCallback()), HeaderAdapter { val selectedContactEvent: MutableLiveData> by lazy { MutableLiveData>() } @@ -47,26 +51,26 @@ class ContactsListAdapter(val selectionViewModel: ListTopBarViewModel) : Lifecyc LayoutInflater.from(parent.context), R.layout.contact_list_cell, parent, false ) - val viewHolder = ViewHolder(binding) - binding.lifecycleOwner = viewHolder - return viewHolder + return ViewHolder(binding) } - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - holder.bind(getItem(position)) + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + (holder as ViewHolder).bind(getItem(position)) } inner class ViewHolder( private val binding: ContactListCellBinding - ) : LifecycleViewHolder(binding) { + ) : RecyclerView.ViewHolder(binding.root) { fun bind(contact: Contact) { with(binding) { val contactViewModel = ContactViewModel(contact) viewModel = contactViewModel + binding.lifecycleOwner = viewLifecycleOwner + // This is for item selection through ListTopBarFragment selectionListViewModel = selectionViewModel - selectionViewModel.isEditionEnabled.observe(this@ViewHolder, { + selectionViewModel.isEditionEnabled.observe(viewLifecycleOwner, { position = adapterPosition }) diff --git a/app/src/main/java/org/linphone/activities/main/contact/fragments/MasterContactsFragment.kt b/app/src/main/java/org/linphone/activities/main/contact/fragments/MasterContactsFragment.kt index af80f7f1e..fdb00856b 100644 --- a/app/src/main/java/org/linphone/activities/main/contact/fragments/MasterContactsFragment.kt +++ b/app/src/main/java/org/linphone/activities/main/contact/fragments/MasterContactsFragment.kt @@ -44,10 +44,9 @@ import org.linphone.core.tools.Log import org.linphone.databinding.ContactMasterFragmentBinding import org.linphone.utils.* -class MasterContactsFragment : MasterFragment() { +class MasterContactsFragment : MasterFragment() { override val dialogConfirmationMessageBeforeRemoval = R.plurals.contact_delete_dialog private lateinit var listViewModel: ContactsListViewModel - private lateinit var adapter: ContactsListAdapter private lateinit var sharedViewModel: SharedMainViewModel private var sipUriToAdd: String? = null @@ -55,6 +54,11 @@ class MasterContactsFragment : MasterFragment() { override fun getLayoutId(): Int = R.layout.contact_master_fragment + override fun onDestroyView() { + binding.contactsList.adapter = null + super.onDestroyView() + } + override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) @@ -67,7 +71,7 @@ class MasterContactsFragment : MasterFragment() { ViewModelProvider(this).get(SharedMainViewModel::class.java) } ?: throw Exception("Invalid Activity") - adapter = ContactsListAdapter(listSelectionViewModel) + _adapter = ContactsListAdapter(listSelectionViewModel, viewLifecycleOwner) binding.contactsList.adapter = adapter binding.setEditClickListener { @@ -100,7 +104,7 @@ class MasterContactsFragment : MasterFragment() { } viewModel.showDeleteButton({ - listViewModel.deleteContact(adapter.getItemAt(viewHolder.adapterPosition)) + listViewModel.deleteContact(adapter.currentList[viewHolder.adapterPosition]) dialog.dismiss() }, getString(R.string.dialog_delete)) @@ -211,7 +215,7 @@ class MasterContactsFragment : MasterFragment() { override fun deleteItems(indexesOfItemToDelete: ArrayList) { val list = ArrayList() for (index in indexesOfItemToDelete) { - val contact = adapter.getItemAt(index) + val contact = adapter.currentList[index] list.add(contact) } listViewModel.deleteContacts(list) diff --git a/app/src/main/java/org/linphone/activities/main/fragments/MasterFragment.kt b/app/src/main/java/org/linphone/activities/main/fragments/MasterFragment.kt index 48ffb0cfb..891236e02 100644 --- a/app/src/main/java/org/linphone/activities/main/fragments/MasterFragment.kt +++ b/app/src/main/java/org/linphone/activities/main/fragments/MasterFragment.kt @@ -29,12 +29,16 @@ import org.linphone.activities.main.viewmodels.DialogViewModel import org.linphone.activities.main.viewmodels.ListTopBarViewModel import org.linphone.utils.AppUtils import org.linphone.utils.DialogUtils +import org.linphone.utils.SelectionListAdapter /** * This fragment can be inherited by all fragments that will display a list * where items can be selected for removal through the ListTopBarFragment */ -abstract class MasterFragment : GenericFragment() { +abstract class MasterFragment> : GenericFragment() { + protected var _adapter: U? = null + protected val adapter get() = _adapter!! + protected lateinit var listSelectionViewModel: ListTopBarViewModel protected open val dialogConfirmationMessageBeforeRemoval: Int = R.plurals.dialog_default_delete diff --git a/app/src/main/java/org/linphone/activities/main/history/adapters/CallLogsListAdapter.kt b/app/src/main/java/org/linphone/activities/main/history/adapters/CallLogsListAdapter.kt index bb383f7fb..17b1daeb3 100644 --- a/app/src/main/java/org/linphone/activities/main/history/adapters/CallLogsListAdapter.kt +++ b/app/src/main/java/org/linphone/activities/main/history/adapters/CallLogsListAdapter.kt @@ -24,8 +24,10 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.databinding.DataBindingUtil +import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.MutableLiveData import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.RecyclerView import org.linphone.R import org.linphone.activities.main.history.viewmodels.CallLogViewModel import org.linphone.activities.main.history.viewmodels.GroupedCallLogViewModel @@ -35,7 +37,10 @@ import org.linphone.databinding.GenericListHeaderBinding import org.linphone.databinding.HistoryListCellBinding import org.linphone.utils.* -class CallLogsListAdapter(val selectionViewModel: ListTopBarViewModel) : LifecycleListAdapter(CallLogDiffCallback()), HeaderAdapter { +class CallLogsListAdapter( + selectionVM: ListTopBarViewModel, + private val viewLifecycleOwner: LifecycleOwner +) : SelectionListAdapter(selectionVM, CallLogDiffCallback()), HeaderAdapter { val selectedCallLogEvent: MutableLiveData> by lazy { MutableLiveData>() } @@ -44,31 +49,31 @@ class CallLogsListAdapter(val selectionViewModel: ListTopBarViewModel) : Lifecyc MutableLiveData>() } - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val binding: HistoryListCellBinding = DataBindingUtil.inflate( LayoutInflater.from(parent.context), R.layout.history_list_cell, parent, false ) - val viewHolder = ViewHolder(binding) - binding.lifecycleOwner = viewHolder - return viewHolder + return ViewHolder(binding) } - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - holder.bind(getItem(position)) + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + (holder as ViewHolder).bind(getItem(position)) } inner class ViewHolder( private val binding: HistoryListCellBinding - ) : LifecycleViewHolder(binding) { + ) : RecyclerView.ViewHolder(binding.root) { fun bind(callLogGroup: GroupedCallLogViewModel) { with(binding) { val callLogViewModel = CallLogViewModel(callLogGroup.lastCallLog) viewModel = callLogViewModel + binding.lifecycleOwner = viewLifecycleOwner + // This is for item selection through ListTopBarFragment selectionListViewModel = selectionViewModel - selectionViewModel.isEditionEnabled.observe(this@ViewHolder, { + selectionViewModel.isEditionEnabled.observe(viewLifecycleOwner, { position = adapterPosition }) diff --git a/app/src/main/java/org/linphone/activities/main/history/fragments/MasterCallLogsFragment.kt b/app/src/main/java/org/linphone/activities/main/history/fragments/MasterCallLogsFragment.kt index 7403d357f..5b8f804d1 100644 --- a/app/src/main/java/org/linphone/activities/main/history/fragments/MasterCallLogsFragment.kt +++ b/app/src/main/java/org/linphone/activities/main/history/fragments/MasterCallLogsFragment.kt @@ -42,14 +42,25 @@ import org.linphone.core.tools.Log import org.linphone.databinding.HistoryMasterFragmentBinding import org.linphone.utils.* -class MasterCallLogsFragment : MasterFragment() { +class MasterCallLogsFragment : MasterFragment() { override val dialogConfirmationMessageBeforeRemoval = R.plurals.history_delete_dialog private lateinit var listViewModel: CallLogsListViewModel - private lateinit var adapter: CallLogsListAdapter private lateinit var sharedViewModel: SharedMainViewModel + private val observer = object : RecyclerView.AdapterDataObserver() { + override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { + scrollToTop() + } + } + override fun getLayoutId(): Int = R.layout.history_master_fragment + override fun onDestroyView() { + binding.callLogsList.adapter = null + adapter.unregisterAdapterDataObserver(observer) + super.onDestroyView() + } + override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) @@ -62,14 +73,10 @@ class MasterCallLogsFragment : MasterFragment() { ViewModelProvider(this).get(SharedMainViewModel::class.java) } ?: throw Exception("Invalid Activity") - adapter = CallLogsListAdapter(listSelectionViewModel) + _adapter = CallLogsListAdapter(listSelectionViewModel, viewLifecycleOwner) // SubmitList is done on a background thread // We need this adapter data observer to know when to scroll - adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { - override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { - scrollToTop() - } - }) + adapter.registerAdapterDataObserver(observer) binding.callLogsList.adapter = adapter binding.setEditClickListener { @@ -97,7 +104,7 @@ class MasterCallLogsFragment : MasterFragment() { } viewModel.showDeleteButton({ - listViewModel.deleteCallLogGroup(adapter.getItemAt(viewHolder.adapterPosition)) + listViewModel.deleteCallLogGroup(adapter.currentList[viewHolder.adapterPosition]) dialog.dismiss() }, getString(R.string.dialog_delete)) @@ -194,7 +201,7 @@ class MasterCallLogsFragment : MasterFragment() { override fun deleteItems(indexesOfItemToDelete: ArrayList) { val list = ArrayList() for (index in indexesOfItemToDelete) { - val callLogGroup = adapter.getItemAt(index) + val callLogGroup = adapter.currentList[index] list.add(callLogGroup) } listViewModel.deleteCallLogGroups(list) diff --git a/app/src/main/java/org/linphone/activities/main/recordings/adapters/RecordingsListAdapter.kt b/app/src/main/java/org/linphone/activities/main/recordings/adapters/RecordingsListAdapter.kt index 8b6e88f7f..8583da526 100644 --- a/app/src/main/java/org/linphone/activities/main/recordings/adapters/RecordingsListAdapter.kt +++ b/app/src/main/java/org/linphone/activities/main/recordings/adapters/RecordingsListAdapter.kt @@ -25,8 +25,10 @@ import android.view.TextureView import android.view.View import android.view.ViewGroup import androidx.databinding.DataBindingUtil +import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.MutableLiveData import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.RecyclerView import org.linphone.R import org.linphone.activities.main.recordings.viewmodels.RecordingViewModel import org.linphone.activities.main.viewmodels.ListTopBarViewModel @@ -34,10 +36,10 @@ import org.linphone.databinding.GenericListHeaderBinding import org.linphone.databinding.RecordingListCellBinding import org.linphone.utils.* -class RecordingsListAdapter(val selectionViewModel: ListTopBarViewModel) : LifecycleListAdapter( - RecordingDiffCallback() -), HeaderAdapter { - +class RecordingsListAdapter( + selectionVM: ListTopBarViewModel, + private val viewLifecycleOwner: LifecycleOwner +) : SelectionListAdapter(selectionVM, RecordingDiffCallback()), HeaderAdapter { val isVideoRecordingPlayingEvent: MutableLiveData> by lazy { MutableLiveData>() } @@ -48,32 +50,30 @@ class RecordingsListAdapter(val selectionViewModel: ListTopBarViewModel) : Lifec videoSurface = textureView } - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val binding: RecordingListCellBinding = DataBindingUtil.inflate( LayoutInflater.from(parent.context), R.layout.recording_list_cell, parent, false ) - val viewHolder = ViewHolder(binding) - binding.lifecycleOwner = viewHolder - return viewHolder + return ViewHolder(binding) } - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - holder.bind(getItem(position)) + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + (holder as ViewHolder).bind(getItem(position)) } inner class ViewHolder( private val binding: RecordingListCellBinding - ) : LifecycleViewHolder(binding) { + ) : RecyclerView.ViewHolder(binding.root) { fun bind(recording: RecordingViewModel) { with(binding) { viewModel = recording + binding.lifecycleOwner = viewLifecycleOwner + // This is for item selection through ListTopBarFragment + position = adapterPosition selectionListViewModel = selectionViewModel - selectionViewModel.isEditionEnabled.observe(this@ViewHolder, { - position = adapterPosition - }) setClickListener { if (selectionViewModel.isEditionEnabled.value == true) { @@ -81,14 +81,18 @@ class RecordingsListAdapter(val selectionViewModel: ListTopBarViewModel) : Lifec } } - recording.isVideoRecordingPlayingEvent.observe(this@ViewHolder, { - it.consume { value -> - if (value) { + setPlayListener { + if (recording.isPlaying.value == true) { + recording.pause() + isVideoRecordingPlayingEvent.value = Event(false) + } else { + recording.play() + if (recording.isVideoAvailable()) { recording.setTextureView(videoSurface) + isVideoRecordingPlayingEvent.value = Event(true) } - isVideoRecordingPlayingEvent.value = Event(value) } - }) + } executePendingBindings() } diff --git a/app/src/main/java/org/linphone/activities/main/recordings/fragments/RecordingsFragment.kt b/app/src/main/java/org/linphone/activities/main/recordings/fragments/RecordingsFragment.kt index e95c51fcf..be5bed0c6 100644 --- a/app/src/main/java/org/linphone/activities/main/recordings/fragments/RecordingsFragment.kt +++ b/app/src/main/java/org/linphone/activities/main/recordings/fragments/RecordingsFragment.kt @@ -33,15 +33,19 @@ import org.linphone.activities.main.recordings.viewmodels.RecordingsViewModel import org.linphone.databinding.RecordingsFragmentBinding import org.linphone.utils.RecyclerViewHeaderDecoration -class RecordingsFragment : MasterFragment() { +class RecordingsFragment : MasterFragment() { private lateinit var viewModel: RecordingsViewModel - private lateinit var adapter: RecordingsListAdapter private var videoX: Float = 0f private var videoY: Float = 0f override fun getLayoutId(): Int = R.layout.recordings_fragment + override fun onDestroyView() { + binding.recordingsList.adapter = null + super.onDestroyView() + } + override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) @@ -50,10 +54,7 @@ class RecordingsFragment : MasterFragment() { viewModel = ViewModelProvider(this).get(RecordingsViewModel::class.java) binding.viewModel = viewModel - adapter = - RecordingsListAdapter( - listSelectionViewModel - ) + _adapter = RecordingsListAdapter(listSelectionViewModel, viewLifecycleOwner) binding.recordingsList.adapter = adapter val layoutManager = LinearLayoutManager(activity) @@ -109,7 +110,7 @@ class RecordingsFragment : MasterFragment() { override fun deleteItems(indexesOfItemToDelete: ArrayList) { val list = ArrayList() for (index in indexesOfItemToDelete) { - val recording = adapter.getItemAt(index) + val recording = adapter.currentList[index] list.add(recording) } viewModel.deleteRecordings(list) diff --git a/app/src/main/java/org/linphone/activities/main/recordings/viewmodels/RecordingViewModel.kt b/app/src/main/java/org/linphone/activities/main/recordings/viewmodels/RecordingViewModel.kt index afc944062..2f97a8030 100644 --- a/app/src/main/java/org/linphone/activities/main/recordings/viewmodels/RecordingViewModel.kt +++ b/app/src/main/java/org/linphone/activities/main/recordings/viewmodels/RecordingViewModel.kt @@ -37,7 +37,6 @@ import org.linphone.core.AudioDevice import org.linphone.core.Player import org.linphone.core.PlayerListener import org.linphone.core.tools.Log -import org.linphone.utils.Event import org.linphone.utils.LinphoneUtils class RecordingViewModel(val path: String) : ViewModel(), Comparable { @@ -56,10 +55,6 @@ class RecordingViewModel(val path: String) : ViewModel(), Comparable() val isPlaying = MutableLiveData() - val isVideoRecordingPlayingEvent: MutableLiveData> by lazy { - MutableLiveData>() - } - private val tickerChannel = ticker(1000, 1000) private lateinit var player: Player @@ -68,6 +63,30 @@ class RecordingViewModel(val path: String) : ViewModel(), Comparable= 2) { @@ -84,6 +103,7 @@ class RecordingViewModel(val path: String) : ViewModel(), Comparable. - */ -package org.linphone.utils - -import androidx.databinding.ViewDataBinding -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.LifecycleRegistry -import androidx.recyclerview.widget.RecyclerView - -/** - * This class allows us to use ViewHolder as lifecycle owner so items in lists can refresh when - * a live data value changes without having to notify the adapter that the item as changed. - */ -abstract class LifecycleViewHolder(viewBinding: ViewDataBinding) : RecyclerView.ViewHolder(viewBinding.root), LifecycleOwner { - private val lifecycleRegistry = LifecycleRegistry(this) // TODO FIXME leak ? - - init { - lifecycleRegistry.currentState = Lifecycle.State.INITIALIZED - } - - override fun getLifecycle(): Lifecycle { - return lifecycleRegistry - } - - fun attach() { - lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START) - } - - fun detach() { - lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP) - } -} diff --git a/app/src/main/java/org/linphone/utils/LifecycleListAdapter.kt b/app/src/main/java/org/linphone/utils/SelectionListAdapter.kt similarity index 59% rename from app/src/main/java/org/linphone/utils/LifecycleListAdapter.kt rename to app/src/main/java/org/linphone/utils/SelectionListAdapter.kt index bbb9a8d26..b7a460532 100644 --- a/app/src/main/java/org/linphone/utils/LifecycleListAdapter.kt +++ b/app/src/main/java/org/linphone/utils/SelectionListAdapter.kt @@ -21,23 +21,20 @@ package org.linphone.utils import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import org.linphone.activities.main.viewmodels.ListTopBarViewModel -/** - * This class prevents having to do in each adapter the viewHolder.attach/detach calls - * to create lifecycle events that are required for the data binding to work correctly - */ -abstract class LifecycleListAdapter(diff: DiffUtil.ItemCallback) : ListAdapter(diff) { - override fun onViewAttachedToWindow(holder: VH) { - super.onViewAttachedToWindow(holder) - holder.attach() - } +abstract class SelectionListAdapter( + selectionVM: ListTopBarViewModel, + diff: DiffUtil.ItemCallback +) : + ListAdapter(diff) { - override fun onViewDetachedFromWindow(holder: VH) { - super.onViewDetachedFromWindow(holder) - holder.detach() - } + private var _selectionViewModel: ListTopBarViewModel? = selectionVM + protected val selectionViewModel get() = _selectionViewModel!! - fun getItemAt(position: Int): T { - return getItem(position) + override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) { + super.onDetachedFromRecyclerView(recyclerView) + _selectionViewModel = null } } diff --git a/app/src/main/res/layout/recording_list_cell.xml b/app/src/main/res/layout/recording_list_cell.xml index 41de1c6c0..e3d7099fb 100644 --- a/app/src/main/res/layout/recording_list_cell.xml +++ b/app/src/main/res/layout/recording_list_cell.xml @@ -6,6 +6,9 @@ + @@ -28,7 +31,7 @@