From cadd5a84b2ab4a671ba83e4765a67c4521d51eb7 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Wed, 24 Jun 2020 15:27:53 +0200 Subject: [PATCH] Grouped call logs in history for a shorter list --- .../chat/fragments/MasterChatRoomsFragment.kt | 2 +- .../fragments/MasterContactsFragment.kt | 2 +- .../history/adapters/CallLogsListAdapter.kt | 38 +++++---- .../fragments/MasterCallLogsFragment.kt | 14 +-- .../viewmodels/CallLogsListViewModel.kt | 85 ++++++++++++------- .../viewmodels/GroupedCallLogViewModel.kt | 27 ++++++ app/src/main/res/layout/history_list_cell.xml | 7 +- 7 files changed, 116 insertions(+), 59 deletions(-) create mode 100644 app/src/main/java/org/linphone/activities/main/history/viewmodels/GroupedCallLogViewModel.kt 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 4bd46c369..057144ac2 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 @@ -113,7 +113,7 @@ class MasterChatRoomsFragment : MasterFragment() { } viewModel.showDeleteButton({ - listViewModel.deleteChatRoom(listViewModel.chatRooms.value?.get(viewHolder.adapterPosition)) + listViewModel.deleteChatRoom(adapter.getItemAt(viewHolder.adapterPosition)) dialog.dismiss() }, getString(R.string.dialog_delete)) 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 e1779fd04..713388706 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 @@ -112,7 +112,7 @@ class MasterContactsFragment : MasterFragment() { } viewModel.showDeleteButton({ - listViewModel.deleteContact(listViewModel.contactsList.value?.get(viewHolder.adapterPosition)) + listViewModel.deleteContact(adapter.getItemAt(viewHolder.adapterPosition)) dialog.dismiss() }, getString(R.string.dialog_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 ddc20438d..7e6e471e5 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 @@ -29,16 +29,16 @@ import androidx.lifecycle.Observer import androidx.recyclerview.widget.DiffUtil import org.linphone.R import org.linphone.activities.main.history.viewmodels.CallLogViewModel +import org.linphone.activities.main.history.viewmodels.GroupedCallLogViewModel import org.linphone.activities.main.viewmodels.ListTopBarViewModel import org.linphone.core.Address -import org.linphone.core.CallLog import org.linphone.databinding.GenericListHeaderBinding import org.linphone.databinding.HistoryListCellBinding import org.linphone.utils.* -class CallLogsListAdapter(val selectionViewModel: ListTopBarViewModel) : LifecycleListAdapter(CallLogDiffCallback()), HeaderAdapter { - val selectedCallLogEvent: MutableLiveData> by lazy { - MutableLiveData>() +class CallLogsListAdapter(val selectionViewModel: ListTopBarViewModel) : LifecycleListAdapter(CallLogDiffCallback()), HeaderAdapter { + val selectedCallLogEvent: MutableLiveData> by lazy { + MutableLiveData>() } val startCallToEvent: MutableLiveData> by lazy { @@ -62,9 +62,9 @@ class CallLogsListAdapter(val selectionViewModel: ListTopBarViewModel) : Lifecyc inner class ViewHolder( private val binding: HistoryListCellBinding ) : LifecycleViewHolder(binding) { - fun bind(callLog: CallLog) { + fun bind(callLogGroup: GroupedCallLogViewModel) { with(binding) { - val callLogViewModel = CallLogViewModel(callLog) + val callLogViewModel = CallLogViewModel(callLogGroup.lastCallLog) viewModel = callLogViewModel // This is for item selection through ListTopBarFragment @@ -77,15 +77,17 @@ class CallLogsListAdapter(val selectionViewModel: ListTopBarViewModel) : Lifecyc if (selectionViewModel.isEditionEnabled.value == true) { selectionViewModel.onToggleSelect(adapterPosition) } else { - startCallToEvent.value = Event(callLog.remoteAddress) + startCallToEvent.value = Event(callLogGroup.lastCallLog.remoteAddress) } } // This listener is disabled when in edition mode setDetailsClickListener { - selectedCallLogEvent.value = Event(callLog) + selectedCallLogEvent.value = Event(callLogGroup) } + groupCount = callLogGroup.callLogs.size + executePendingBindings() } } @@ -93,18 +95,18 @@ class CallLogsListAdapter(val selectionViewModel: ListTopBarViewModel) : Lifecyc override fun displayHeaderForPosition(position: Int): Boolean { if (position >= itemCount) return false - val callLog = getItem(position) - val date = callLog.startDate + val callLogGroup = getItem(position) + val date = callLogGroup.lastCallLog.startDate val previousPosition = position - 1 return if (previousPosition >= 0) { - val previousItemDate = getItem(previousPosition).startDate + val previousItemDate = getItem(previousPosition).lastCallLog.startDate !TimestampUtils.isSameDay(date, previousItemDate) } else true } override fun getHeaderViewForPosition(context: Context, position: Int): View { val callLog = getItem(position) - val date = formatDate(context, callLog.startDate) + val date = formatDate(context, callLog.lastCallLog.startDate) val binding: GenericListHeaderBinding = DataBindingUtil.inflate( LayoutInflater.from(context), R.layout.generic_list_header, null, false @@ -124,17 +126,17 @@ class CallLogsListAdapter(val selectionViewModel: ListTopBarViewModel) : Lifecyc } } -private class CallLogDiffCallback : DiffUtil.ItemCallback() { +private class CallLogDiffCallback : DiffUtil.ItemCallback() { override fun areItemsTheSame( - oldItem: CallLog, - newItem: CallLog + oldItem: GroupedCallLogViewModel, + newItem: GroupedCallLogViewModel ): Boolean { - return oldItem.callId == newItem.callId + return oldItem.lastCallLog.callId == newItem.lastCallLog.callId } override fun areContentsTheSame( - oldItem: CallLog, - newItem: CallLog + oldItem: GroupedCallLogViewModel, + newItem: GroupedCallLogViewModel ): Boolean { return false // For headers } 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 2a7b26d89..f4da3db1d 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 @@ -38,10 +38,10 @@ import org.linphone.R import org.linphone.activities.main.fragments.MasterFragment import org.linphone.activities.main.history.adapters.CallLogsListAdapter import org.linphone.activities.main.history.viewmodels.CallLogsListViewModel +import org.linphone.activities.main.history.viewmodels.GroupedCallLogViewModel import org.linphone.activities.main.viewmodels.DialogViewModel import org.linphone.activities.main.viewmodels.SharedMainViewModel import org.linphone.activities.main.viewmodels.TabsViewModel -import org.linphone.core.CallLog import org.linphone.core.tools.Log import org.linphone.databinding.HistoryMasterFragmentBinding import org.linphone.utils.* @@ -109,7 +109,7 @@ class MasterCallLogsFragment : MasterFragment() { } viewModel.showDeleteButton({ - listViewModel.deleteCallLog(listViewModel.callLogs.value?.get(viewHolder.adapterPosition)) + listViewModel.deleteCallLogGroup(adapter.getItemAt(viewHolder.adapterPosition)) dialog.dismiss() }, getString(R.string.dialog_delete)) @@ -156,7 +156,7 @@ class MasterCallLogsFragment : MasterFragment() { adapter.selectedCallLogEvent.observe(viewLifecycleOwner, Observer { it.consume { callLog -> - sharedViewModel.selectedCallLog.value = callLog + sharedViewModel.selectedCallLog.value = callLog.lastCallLog if (!resources.getBoolean(R.bool.isTablet)) { if (findNavController().currentDestination?.id == R.id.masterCallLogsFragment) { findNavController().navigate(R.id.action_masterCallLogsFragment_to_detailCallLogFragment) @@ -212,12 +212,12 @@ class MasterCallLogsFragment : MasterFragment() { } override fun deleteItems(indexesOfItemToDelete: ArrayList) { - val list = ArrayList() + val list = ArrayList() for (index in indexesOfItemToDelete) { - val callLog = adapter.getItemAt(index) - list.add(callLog) + val callLogGroup = adapter.getItemAt(index) + list.add(callLogGroup) } - listViewModel.deleteCallLogs(list) + listViewModel.deleteCallLogGroups(list) } private fun scrollToTop() { diff --git a/app/src/main/java/org/linphone/activities/main/history/viewmodels/CallLogsListViewModel.kt b/app/src/main/java/org/linphone/activities/main/history/viewmodels/CallLogsListViewModel.kt index 3544c642a..a9641a5fd 100644 --- a/app/src/main/java/org/linphone/activities/main/history/viewmodels/CallLogsListViewModel.kt +++ b/app/src/main/java/org/linphone/activities/main/history/viewmodels/CallLogsListViewModel.kt @@ -26,10 +26,11 @@ import org.linphone.contact.ContactsUpdatedListenerStub import org.linphone.core.* import org.linphone.core.tools.Log import org.linphone.utils.Event +import org.linphone.utils.TimestampUtils class CallLogsListViewModel : ViewModel() { - val callLogs = MutableLiveData>() - val missedCallLogs = MutableLiveData>() + val callLogs = MutableLiveData>() + val missedCallLogs = MutableLiveData>() val missedCallLogsSelected = MutableLiveData() @@ -70,47 +71,71 @@ class CallLogsListViewModel : ViewModel() { super.onCleared() } - fun deleteCallLog(callLog: CallLog?) { - val list = arrayListOf() - list.addAll(callLogs.value.orEmpty()) - - val missedList = arrayListOf() - missedList.addAll(missedCallLogs.value.orEmpty()) - + fun deleteCallLogGroup(callLog: GroupedCallLogViewModel?) { if (callLog != null) { - coreContext.core.removeCallLog(callLog) - list.remove(callLog) - missedList.remove(callLog) + for (log in callLog.callLogs) { + coreContext.core.removeCallLog(log) + } } - callLogs.value = list - missedCallLogs.value = missedList + updateCallLogs() } - fun deleteCallLogs(listToDelete: ArrayList) { - val list = arrayListOf() - list.addAll(callLogs.value.orEmpty()) - - val missedList = arrayListOf() - missedList.addAll(missedCallLogs.value.orEmpty()) - + fun deleteCallLogGroups(listToDelete: ArrayList) { for (callLog in listToDelete) { - coreContext.core.removeCallLog(callLog) - list.remove(callLog) - missedList.remove(callLog) + for (log in callLog.callLogs) { + coreContext.core.removeCallLog(log) + } } - callLogs.value = list - missedCallLogs.value = missedList + updateCallLogs() } private fun updateCallLogs() { - val list = arrayListOf() - val missedList = arrayListOf() + val list = arrayListOf() + val missedList = arrayListOf() + var previousCallLogGroup: GroupedCallLogViewModel? = null + var previousMissedCallLogGroup: GroupedCallLogViewModel? = null for (callLog in coreContext.core.callLogs) { - list.add(callLog) - if (callLog.status == Call.Status.Missed) missedList.add(callLog) + if (previousCallLogGroup == null) { + previousCallLogGroup = GroupedCallLogViewModel(callLog) + } else if (previousCallLogGroup.lastCallLog.localAddress.weakEqual(callLog.localAddress) && previousCallLogGroup.lastCallLog.remoteAddress.weakEqual(callLog.remoteAddress)) { + if (TimestampUtils.isSameDay(previousCallLogGroup.lastCallLog.startDate, callLog.startDate)) { + previousCallLogGroup.callLogs.add(callLog) + previousCallLogGroup.lastCallLog = callLog + } else { + list.add(previousCallLogGroup) + previousCallLogGroup = GroupedCallLogViewModel(callLog) + } + } else { + list.add(previousCallLogGroup) + previousCallLogGroup = GroupedCallLogViewModel(callLog) + } + + if (callLog.status == Call.Status.Missed) { + if (previousMissedCallLogGroup == null) { + previousMissedCallLogGroup = GroupedCallLogViewModel(callLog) + } else if (previousMissedCallLogGroup.lastCallLog.localAddress.weakEqual(callLog.localAddress) && previousMissedCallLogGroup.lastCallLog.remoteAddress.weakEqual(callLog.remoteAddress)) { + if (TimestampUtils.isSameDay(previousMissedCallLogGroup.lastCallLog.startDate, callLog.startDate)) { + previousMissedCallLogGroup.callLogs.add(callLog) + previousMissedCallLogGroup.lastCallLog = callLog + } else { + missedList.add(previousMissedCallLogGroup) + previousMissedCallLogGroup = GroupedCallLogViewModel(callLog) + } + } else { + missedList.add(previousMissedCallLogGroup) + previousMissedCallLogGroup = GroupedCallLogViewModel(callLog) + } + } + } + + if (previousCallLogGroup != null && !list.contains(previousCallLogGroup)) { + list.add(previousCallLogGroup) + } + if (previousMissedCallLogGroup != null && !missedList.contains(previousMissedCallLogGroup)) { + missedList.add(previousMissedCallLogGroup) } callLogs.value = list diff --git a/app/src/main/java/org/linphone/activities/main/history/viewmodels/GroupedCallLogViewModel.kt b/app/src/main/java/org/linphone/activities/main/history/viewmodels/GroupedCallLogViewModel.kt new file mode 100644 index 000000000..12272f88d --- /dev/null +++ b/app/src/main/java/org/linphone/activities/main/history/viewmodels/GroupedCallLogViewModel.kt @@ -0,0 +1,27 @@ +/* + * 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 . + */ +package org.linphone.activities.main.history.viewmodels + +import org.linphone.core.CallLog + +class GroupedCallLogViewModel(callLog: CallLog) { + var lastCallLog: CallLog = callLog + val callLogs = arrayListOf(callLog) +} diff --git a/app/src/main/res/layout/history_list_cell.xml b/app/src/main/res/layout/history_list_cell.xml index 0c176b95a..aa74c302a 100644 --- a/app/src/main/res/layout/history_list_cell.xml +++ b/app/src/main/res/layout/history_list_cell.xml @@ -20,6 +20,9 @@ +