Fixed more leaks
This commit is contained in:
parent
ebda69e739
commit
2433921f22
22 changed files with 290 additions and 261 deletions
|
@ -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<EventLog, LifecycleViewHolder>(ChatMessageDiffCallback()) {
|
||||
class ChatMessagesListAdapter(
|
||||
selectionVM: ListTopBarViewModel,
|
||||
private val viewLifecycleOwner: LifecycleOwner
|
||||
) : SelectionListAdapter<EventLog, RecyclerView.ViewHolder>(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
|
||||
})
|
||||
|
||||
|
|
|
@ -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<SearchResult, ChatRoomCreationContactsAdapter.ViewHolder>(SearchResultDiffCallback()) {
|
||||
class ChatRoomCreationContactsAdapter(
|
||||
private val viewLifecycleOwner: LifecycleOwner
|
||||
) : ListAdapter<SearchResult, RecyclerView.ViewHolder>(SearchResultDiffCallback()) {
|
||||
val selectedContact = MutableLiveData<Event<SearchResult>>()
|
||||
|
||||
val selectedAddresses = MutableLiveData<ArrayList<Address>>()
|
||||
|
||||
var groupChatEnabled: Boolean = false
|
||||
|
||||
val securityEnabled = MutableLiveData<Boolean>()
|
||||
private var selectedAddresses = ArrayList<Address>()
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
private var securityEnabled: Boolean = false
|
||||
|
||||
fun updateSelectedAddresses(selection: ArrayList<Address>) {
|
||||
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)
|
||||
|
|
|
@ -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<ChatRoom, ChatRoomsListAdapter.ViewHolder>(ChatRoomDiffCallback()) {
|
||||
class ChatRoomsListAdapter(
|
||||
selectionVM: ListTopBarViewModel,
|
||||
private val viewLifecycleOwner: LifecycleOwner
|
||||
) : SelectionListAdapter<ChatRoom, RecyclerView.ViewHolder>(selectionVM, ChatRoomDiffCallback()) {
|
||||
val selectedChatRoomEvent: MutableLiveData<Event<ChatRoom>> by lazy {
|
||||
MutableLiveData<Event<ChatRoom>>()
|
||||
}
|
||||
|
||||
val toggledPositionForSelectionEvent: MutableLiveData<Event<Int>> by lazy {
|
||||
MutableLiveData<Event<Int>>()
|
||||
}
|
||||
|
||||
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
|
||||
})
|
||||
|
||||
|
|
|
@ -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<GroupChatRoomMember,
|
||||
GroupInfoParticipantsAdapter.ViewHolder>(ParticipantDiffCallback()) {
|
||||
class GroupInfoParticipantsAdapter(
|
||||
private val viewLifecycleOwner: LifecycleOwner,
|
||||
private val isEncryptionEnabled: Boolean
|
||||
) : ListAdapter<GroupChatRoomMember, RecyclerView.ViewHolder>(ParticipantDiffCallback()) {
|
||||
private var showAdmin: Boolean = false
|
||||
|
||||
val participantRemovedEvent: MutableLiveData<Event<GroupChatRoomMember>> 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)
|
||||
}
|
||||
|
|
|
@ -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<ParticipantImdnState,
|
||||
ImdnAdapter.ViewHolder>(ParticipantImdnStateDiffCallback()), HeaderAdapter {
|
||||
class ImdnAdapter(
|
||||
private val viewLifecycleOwner: LifecycleOwner
|
||||
) : ListAdapter<ParticipantImdnState, RecyclerView.ViewHolder>(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()
|
||||
}
|
||||
|
|
|
@ -59,9 +59,9 @@ class ChatRoomCreationFragment : GenericFragment<ChatRoomCreationFragmentBinding
|
|||
|
||||
binding.viewModel = viewModel
|
||||
|
||||
adapter = ChatRoomCreationContactsAdapter()
|
||||
adapter = ChatRoomCreationContactsAdapter(viewLifecycleOwner)
|
||||
adapter.groupChatEnabled = viewModel.createGroupChat.value == true
|
||||
adapter.securityEnabled.value = viewModel.isEncrypted.value == true
|
||||
adapter.updateSecurity(viewModel.isEncrypted.value == true)
|
||||
binding.contactsList.adapter = adapter
|
||||
|
||||
val layoutManager = LinearLayoutManager(activity)
|
||||
|
@ -90,7 +90,7 @@ class ChatRoomCreationFragment : GenericFragment<ChatRoomCreationFragmentBinding
|
|||
})
|
||||
|
||||
viewModel.isEncrypted.observe(viewLifecycleOwner, {
|
||||
adapter.securityEnabled.value = it
|
||||
adapter.updateSecurity(it)
|
||||
})
|
||||
|
||||
viewModel.sipContactsSelected.observe(viewLifecycleOwner, {
|
||||
|
@ -98,7 +98,7 @@ class ChatRoomCreationFragment : GenericFragment<ChatRoomCreationFragmentBinding
|
|||
})
|
||||
|
||||
viewModel.selectedAddresses.observe(viewLifecycleOwner, {
|
||||
adapter.selectedAddresses.value = it
|
||||
adapter.updateSelectedAddresses(it)
|
||||
})
|
||||
|
||||
viewModel.chatRoomCreatedEvent.observe(viewLifecycleOwner, {
|
||||
|
|
|
@ -56,18 +56,33 @@ import org.linphone.databinding.ChatRoomDetailFragmentBinding
|
|||
import org.linphone.utils.*
|
||||
import org.linphone.utils.Event
|
||||
|
||||
class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding>() {
|
||||
class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, ChatMessagesListAdapter>() {
|
||||
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<ChatRoomDetailFragmentBinding>() {
|
|||
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<ChatRoomDetailFragmentBinding>() {
|
|||
override fun deleteItems(indexesOfItemToDelete: ArrayList<Int>) {
|
||||
val list = ArrayList<EventLog>()
|
||||
for (index in indexesOfItemToDelete) {
|
||||
val eventLog = adapter.getItemAt(index)
|
||||
val eventLog = adapter.currentList[index]
|
||||
list.add(eventLog)
|
||||
}
|
||||
listViewModel.deleteEventLogs(list)
|
||||
|
|
|
@ -67,7 +67,10 @@ class GroupInfoFragment : GenericFragment<ChatRoomGroupInfoFragmentBinding>() {
|
|||
|
||||
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)
|
||||
|
|
|
@ -76,7 +76,7 @@ class ImdnFragment : GenericFragment<ChatRoomImdnFragmentBinding>() {
|
|||
return
|
||||
}
|
||||
|
||||
adapter = ImdnAdapter()
|
||||
adapter = ImdnAdapter(viewLifecycleOwner)
|
||||
binding.participantsList.adapter = adapter
|
||||
|
||||
val layoutManager = LinearLayoutManager(activity)
|
||||
|
|
|
@ -43,14 +43,31 @@ import org.linphone.core.tools.Log
|
|||
import org.linphone.databinding.ChatRoomMasterFragmentBinding
|
||||
import org.linphone.utils.*
|
||||
|
||||
class MasterChatRoomsFragment : MasterFragment<ChatRoomMasterFragmentBinding>() {
|
||||
class MasterChatRoomsFragment : MasterFragment<ChatRoomMasterFragmentBinding, ChatRoomsListAdapter>() {
|
||||
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<ChatRoomMasterFragmentBinding>()
|
|||
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<ChatRoomMasterFragmentBinding>()
|
|||
}
|
||||
|
||||
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<ChatRoomMasterFragmentBinding>()
|
|||
}
|
||||
})
|
||||
|
||||
adapter.toggledPositionForSelectionEvent.observe(viewLifecycleOwner, {
|
||||
it.consume { position ->
|
||||
listSelectionViewModel.onToggleSelect(position)
|
||||
}
|
||||
})
|
||||
|
||||
binding.setEditClickListener {
|
||||
listSelectionViewModel.isEditionEnabled.value = true
|
||||
}
|
||||
|
@ -201,7 +214,7 @@ class MasterChatRoomsFragment : MasterFragment<ChatRoomMasterFragmentBinding>()
|
|||
override fun deleteItems(indexesOfItemToDelete: ArrayList<Int>) {
|
||||
val list = ArrayList<ChatRoom>()
|
||||
for (index in indexesOfItemToDelete) {
|
||||
val chatRoom = adapter.getItemAt(index)
|
||||
val chatRoom = adapter.currentList[index]
|
||||
list.add(chatRoom)
|
||||
}
|
||||
listViewModel.deleteChatRooms(list)
|
||||
|
|
|
@ -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<Boolean>()
|
||||
|
||||
|
@ -114,6 +114,7 @@ class ChatMessageViewModel(
|
|||
|
||||
override fun onCleared() {
|
||||
chatMessage.removeListener(listener)
|
||||
contentListener = null
|
||||
|
||||
super.onCleared()
|
||||
}
|
||||
|
|
|
@ -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<Contact, ContactsListAdapter.ViewHolder>(ContactDiffCallback()), HeaderAdapter {
|
||||
class ContactsListAdapter(
|
||||
selectionVM: ListTopBarViewModel,
|
||||
private val viewLifecycleOwner: LifecycleOwner
|
||||
) : SelectionListAdapter<Contact, RecyclerView.ViewHolder>(selectionVM, ContactDiffCallback()), HeaderAdapter {
|
||||
val selectedContactEvent: MutableLiveData<Event<Contact>> by lazy {
|
||||
MutableLiveData<Event<Contact>>()
|
||||
}
|
||||
|
@ -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
|
||||
})
|
||||
|
||||
|
|
|
@ -44,10 +44,9 @@ import org.linphone.core.tools.Log
|
|||
import org.linphone.databinding.ContactMasterFragmentBinding
|
||||
import org.linphone.utils.*
|
||||
|
||||
class MasterContactsFragment : MasterFragment<ContactMasterFragmentBinding>() {
|
||||
class MasterContactsFragment : MasterFragment<ContactMasterFragmentBinding, ContactsListAdapter>() {
|
||||
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<ContactMasterFragmentBinding>() {
|
|||
|
||||
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<ContactMasterFragmentBinding>() {
|
|||
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<ContactMasterFragmentBinding>() {
|
|||
}
|
||||
|
||||
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<ContactMasterFragmentBinding>() {
|
|||
override fun deleteItems(indexesOfItemToDelete: ArrayList<Int>) {
|
||||
val list = ArrayList<Contact>()
|
||||
for (index in indexesOfItemToDelete) {
|
||||
val contact = adapter.getItemAt(index)
|
||||
val contact = adapter.currentList[index]
|
||||
list.add(contact)
|
||||
}
|
||||
listViewModel.deleteContacts(list)
|
||||
|
|
|
@ -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<T : ViewDataBinding> : GenericFragment<T>() {
|
||||
abstract class MasterFragment<T : ViewDataBinding, U : SelectionListAdapter<*, *>> : GenericFragment<T>() {
|
||||
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
|
||||
|
||||
|
|
|
@ -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<GroupedCallLogViewModel, CallLogsListAdapter.ViewHolder>(CallLogDiffCallback()), HeaderAdapter {
|
||||
class CallLogsListAdapter(
|
||||
selectionVM: ListTopBarViewModel,
|
||||
private val viewLifecycleOwner: LifecycleOwner
|
||||
) : SelectionListAdapter<GroupedCallLogViewModel, RecyclerView.ViewHolder>(selectionVM, CallLogDiffCallback()), HeaderAdapter {
|
||||
val selectedCallLogEvent: MutableLiveData<Event<GroupedCallLogViewModel>> by lazy {
|
||||
MutableLiveData<Event<GroupedCallLogViewModel>>()
|
||||
}
|
||||
|
@ -44,31 +49,31 @@ class CallLogsListAdapter(val selectionViewModel: ListTopBarViewModel) : Lifecyc
|
|||
MutableLiveData<Event<Address>>()
|
||||
}
|
||||
|
||||
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
|
||||
})
|
||||
|
||||
|
|
|
@ -42,14 +42,25 @@ import org.linphone.core.tools.Log
|
|||
import org.linphone.databinding.HistoryMasterFragmentBinding
|
||||
import org.linphone.utils.*
|
||||
|
||||
class MasterCallLogsFragment : MasterFragment<HistoryMasterFragmentBinding>() {
|
||||
class MasterCallLogsFragment : MasterFragment<HistoryMasterFragmentBinding, CallLogsListAdapter>() {
|
||||
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<HistoryMasterFragmentBinding>() {
|
|||
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<HistoryMasterFragmentBinding>() {
|
|||
}
|
||||
|
||||
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<HistoryMasterFragmentBinding>() {
|
|||
override fun deleteItems(indexesOfItemToDelete: ArrayList<Int>) {
|
||||
val list = ArrayList<GroupedCallLogViewModel>()
|
||||
for (index in indexesOfItemToDelete) {
|
||||
val callLogGroup = adapter.getItemAt(index)
|
||||
val callLogGroup = adapter.currentList[index]
|
||||
list.add(callLogGroup)
|
||||
}
|
||||
listViewModel.deleteCallLogGroups(list)
|
||||
|
|
|
@ -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<RecordingViewModel, RecordingsListAdapter.ViewHolder>(
|
||||
RecordingDiffCallback()
|
||||
), HeaderAdapter {
|
||||
|
||||
class RecordingsListAdapter(
|
||||
selectionVM: ListTopBarViewModel,
|
||||
private val viewLifecycleOwner: LifecycleOwner
|
||||
) : SelectionListAdapter<RecordingViewModel, RecyclerView.ViewHolder>(selectionVM, RecordingDiffCallback()), HeaderAdapter {
|
||||
val isVideoRecordingPlayingEvent: MutableLiveData<Event<Boolean>> by lazy {
|
||||
MutableLiveData<Event<Boolean>>()
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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<RecordingsFragmentBinding>() {
|
||||
class RecordingsFragment : MasterFragment<RecordingsFragmentBinding, RecordingsListAdapter>() {
|
||||
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<RecordingsFragmentBinding>() {
|
|||
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<RecordingsFragmentBinding>() {
|
|||
override fun deleteItems(indexesOfItemToDelete: ArrayList<Int>) {
|
||||
val list = ArrayList<RecordingViewModel>()
|
||||
for (index in indexesOfItemToDelete) {
|
||||
val recording = adapter.getItemAt(index)
|
||||
val recording = adapter.currentList[index]
|
||||
list.add(recording)
|
||||
}
|
||||
viewModel.deleteRecordings(list)
|
||||
|
|
|
@ -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<RecordingViewModel> {
|
||||
|
@ -56,10 +55,6 @@ class RecordingViewModel(val path: String) : ViewModel(), Comparable<RecordingVi
|
|||
val formattedPosition = MutableLiveData<String>()
|
||||
val isPlaying = MutableLiveData<Boolean>()
|
||||
|
||||
val isVideoRecordingPlayingEvent: MutableLiveData<Event<Boolean>> by lazy {
|
||||
MutableLiveData<Event<Boolean>>()
|
||||
}
|
||||
|
||||
private val tickerChannel = ticker(1000, 1000)
|
||||
|
||||
private lateinit var player: Player
|
||||
|
@ -68,6 +63,30 @@ class RecordingViewModel(val path: String) : ViewModel(), Comparable<RecordingVi
|
|||
stop()
|
||||
}
|
||||
|
||||
private val textureViewListener = object : TextureView.SurfaceTextureListener {
|
||||
override fun onSurfaceTextureSizeChanged(
|
||||
surface: SurfaceTexture,
|
||||
width: Int,
|
||||
height: Int
|
||||
) { }
|
||||
|
||||
override fun onSurfaceTextureUpdated(surface: SurfaceTexture) { }
|
||||
|
||||
override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean {
|
||||
player.setWindowId(null)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onSurfaceTextureAvailable(
|
||||
surface: SurfaceTexture,
|
||||
width: Int,
|
||||
height: Int
|
||||
) {
|
||||
Log.i("[Recording VM] Surface texture should be available now")
|
||||
player.setWindowId(surface)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
val m = RECORD_PATTERN.matcher(path)
|
||||
if (m.matches() && m.groupCount() >= 2) {
|
||||
|
@ -84,6 +103,7 @@ class RecordingViewModel(val path: String) : ViewModel(), Comparable<RecordingVi
|
|||
|
||||
override fun onCleared() {
|
||||
tickerChannel.cancel()
|
||||
player.setWindowId(null)
|
||||
if (!isClosed()) player.close()
|
||||
player.removeListener(listener)
|
||||
|
||||
|
@ -111,8 +131,10 @@ class RecordingViewModel(val path: String) : ViewModel(), Comparable<RecordingVi
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isVideoRecordingPlayingEvent.value = Event(player.isVideoAvailable)
|
||||
fun isVideoAvailable(): Boolean {
|
||||
return player.isVideoAvailable
|
||||
}
|
||||
|
||||
fun pause() {
|
||||
|
@ -135,28 +157,7 @@ class RecordingViewModel(val path: String) : ViewModel(), Comparable<RecordingVi
|
|||
if (textureView.isAvailable) {
|
||||
player.setWindowId(textureView.surfaceTexture)
|
||||
} else {
|
||||
textureView.surfaceTextureListener = object : TextureView.SurfaceTextureListener {
|
||||
override fun onSurfaceTextureSizeChanged(
|
||||
surface: SurfaceTexture,
|
||||
width: Int,
|
||||
height: Int
|
||||
) { }
|
||||
|
||||
override fun onSurfaceTextureUpdated(surface: SurfaceTexture) { }
|
||||
|
||||
override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onSurfaceTextureAvailable(
|
||||
surface: SurfaceTexture,
|
||||
width: Int,
|
||||
height: Int
|
||||
) {
|
||||
Log.i("[Recording VM] Surface texture should be available now")
|
||||
player.setWindowId(textureView.surfaceTexture)
|
||||
}
|
||||
}
|
||||
textureView.surfaceTextureListener = textureViewListener
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,7 +198,6 @@ class RecordingViewModel(val path: String) : ViewModel(), Comparable<RecordingVi
|
|||
player.seek(0)
|
||||
updatePosition()
|
||||
player.close()
|
||||
isVideoRecordingPlayingEvent.value = Event(false)
|
||||
}
|
||||
|
||||
private fun isClosed(): Boolean {
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 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.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)
|
||||
}
|
||||
}
|
|
@ -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<T, VH : LifecycleViewHolder>(diff: DiffUtil.ItemCallback<T>) : ListAdapter<T, VH>(diff) {
|
||||
override fun onViewAttachedToWindow(holder: VH) {
|
||||
super.onViewAttachedToWindow(holder)
|
||||
holder.attach()
|
||||
}
|
||||
abstract class SelectionListAdapter<T, VH : RecyclerView.ViewHolder>(
|
||||
selectionVM: ListTopBarViewModel,
|
||||
diff: DiffUtil.ItemCallback<T>
|
||||
) :
|
||||
ListAdapter<T, VH>(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
|
||||
}
|
||||
}
|
|
@ -6,6 +6,9 @@
|
|||
<variable
|
||||
name="clickListener"
|
||||
type="android.view.View.OnClickListener"/>
|
||||
<variable
|
||||
name="playListener"
|
||||
type="android.view.View.OnClickListener"/>
|
||||
<variable
|
||||
name="position"
|
||||
type="Integer"/>
|
||||
|
@ -28,7 +31,7 @@
|
|||
|
||||
<ImageView
|
||||
android:id="@+id/record_play_pause"
|
||||
android:onClick="@{() -> viewModel.isPlaying ? viewModel.pause() : viewModel.play()}"
|
||||
android:onClick="@{playListener}"
|
||||
android:selected="@{viewModel.isPlaying}"
|
||||
android:contentDescription="@string/content_description_recording_toggle_play"
|
||||
android:layout_width="wrap_content"
|
||||
|
|
Loading…
Reference in a new issue