From bf24a6b2a9230d644884cd0fe95dd56542525571 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Wed, 17 Jun 2020 15:52:01 +0200 Subject: [PATCH] Added swipe to remove on call logs & contacts + improved confirmation dialog messages --- .../chat/fragments/MasterChatRoomsFragment.kt | 4 +- .../fragments/DetailContactFragment.kt | 2 +- .../fragments/MasterContactsFragment.kt | 38 +++++++++++++++++-- .../viewmodels/ContactsListViewModel.kt | 32 ++++++++++++++++ .../main/fragments/MasterFragment.kt | 5 ++- .../fragments/MasterCallLogsFragment.kt | 35 ++++++++++++++++- .../viewmodels/CallLogsListViewModel.kt | 17 +++++++++ app/src/main/res/values/strings.xml | 26 ++++++++++++- 8 files changed, 150 insertions(+), 9 deletions(-) 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 c4d20c300..4bd46c369 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 @@ -49,6 +49,7 @@ import org.linphone.databinding.ChatRoomMasterFragmentBinding import org.linphone.utils.* class MasterChatRoomsFragment : MasterFragment() { + override val dialogConfirmationMessageBeforeRemoval = R.plurals.chat_room_delete_dialog private lateinit var binding: ChatRoomMasterFragmentBinding private lateinit var listViewModel: ChatRoomsListViewModel private lateinit var adapter: ChatRoomsListAdapter @@ -94,6 +95,7 @@ class MasterChatRoomsFragment : MasterFragment() { val layoutManager = LinearLayoutManager(activity) binding.chatList.layoutManager = layoutManager + // Swipe action val swipeConfiguration = RecyclerViewSwipeConfiguration() val white = ContextCompat.getColor(requireContext(), R.color.white_color) @@ -102,7 +104,7 @@ class MasterChatRoomsFragment : MasterFragment() { override fun onLeftToRightSwipe(viewHolder: RecyclerView.ViewHolder) {} override fun onRightToLeftSwipe(viewHolder: RecyclerView.ViewHolder) { - val viewModel = DialogViewModel(getString(R.string.dialog_default_delete_message)) + val viewModel = DialogViewModel(getString(R.string.chat_room_delete_one_dialog)) val dialog: Dialog = DialogUtils.getDialog(requireContext(), viewModel) viewModel.showCancelButton { diff --git a/app/src/main/java/org/linphone/activities/main/contact/fragments/DetailContactFragment.kt b/app/src/main/java/org/linphone/activities/main/contact/fragments/DetailContactFragment.kt index 862277832..78a8cc0cb 100644 --- a/app/src/main/java/org/linphone/activities/main/contact/fragments/DetailContactFragment.kt +++ b/app/src/main/java/org/linphone/activities/main/contact/fragments/DetailContactFragment.kt @@ -131,7 +131,7 @@ class DetailContactFragment : Fragment() { } private fun confirmContactRemoval() { - val dialogViewModel = DialogViewModel(getString(R.string.contact_confirm_removal_dialog)) + val dialogViewModel = DialogViewModel(getString(R.string.contact_delete_one_dialog)) val dialog: Dialog = DialogUtils.getDialog(requireContext(), dialogViewModel) dialogViewModel.showCancelButton { 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 56a9937ba..e1779fd04 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 @@ -19,33 +19,37 @@ */ package org.linphone.activities.main.contact.fragments +import android.app.Dialog import android.content.pm.PackageManager import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.content.ContextCompat import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.R import org.linphone.activities.main.MainActivity import org.linphone.activities.main.contact.adapters.ContactsListAdapter import org.linphone.activities.main.contact.viewmodels.ContactsListViewModel import org.linphone.activities.main.fragments.MasterFragment +import org.linphone.activities.main.viewmodels.DialogViewModel import org.linphone.activities.main.viewmodels.SharedMainViewModel import org.linphone.contact.Contact import org.linphone.core.Factory import org.linphone.core.tools.Log import org.linphone.databinding.ContactMasterFragmentBinding -import org.linphone.utils.Event -import org.linphone.utils.PermissionHelper -import org.linphone.utils.RecyclerViewHeaderDecoration +import org.linphone.utils.* class MasterContactsFragment : MasterFragment() { + override val dialogConfirmationMessageBeforeRemoval = R.plurals.contact_delete_dialog private lateinit var binding: ContactMasterFragmentBinding private lateinit var listViewModel: ContactsListViewModel private lateinit var adapter: ContactsListAdapter @@ -90,6 +94,34 @@ class MasterContactsFragment : MasterFragment() { val layoutManager = LinearLayoutManager(activity) binding.contactsList.layoutManager = layoutManager + // Swipe action + val swipeConfiguration = RecyclerViewSwipeConfiguration() + val white = ContextCompat.getColor(requireContext(), R.color.white_color) + + swipeConfiguration.rightToLeftAction = RecyclerViewSwipeConfiguration.Action("Delete", white, ContextCompat.getColor(requireContext(), R.color.red_color)) + val swipeListener = object : RecyclerViewSwipeListener { + override fun onLeftToRightSwipe(viewHolder: RecyclerView.ViewHolder) {} + + override fun onRightToLeftSwipe(viewHolder: RecyclerView.ViewHolder) { + val viewModel = DialogViewModel(getString(R.string.contact_delete_one_dialog)) + val dialog: Dialog = DialogUtils.getDialog(requireContext(), viewModel) + + viewModel.showCancelButton { + adapter.notifyItemChanged(viewHolder.adapterPosition) + dialog.dismiss() + } + + viewModel.showDeleteButton({ + listViewModel.deleteContact(listViewModel.contactsList.value?.get(viewHolder.adapterPosition)) + dialog.dismiss() + }, getString(R.string.dialog_delete)) + + dialog.show() + } + } + RecyclerViewSwipeUtils(ItemTouchHelper.LEFT, swipeConfiguration, swipeListener) + .attachToRecyclerView(binding.contactsList) + // Divider between items val dividerItemDecoration = DividerItemDecoration(context, layoutManager.orientation) dividerItemDecoration.setDrawable(resources.getDrawable(R.drawable.divider, null)) diff --git a/app/src/main/java/org/linphone/activities/main/contact/viewmodels/ContactsListViewModel.kt b/app/src/main/java/org/linphone/activities/main/contact/viewmodels/ContactsListViewModel.kt index 4f6c86821..9cea16233 100644 --- a/app/src/main/java/org/linphone/activities/main/contact/viewmodels/ContactsListViewModel.kt +++ b/app/src/main/java/org/linphone/activities/main/contact/viewmodels/ContactsListViewModel.kt @@ -73,6 +73,38 @@ class ContactsListViewModel : ViewModel() { } } + fun deleteContact(contact: Contact?) { + contact ?: return + + val select = ContactsContract.Data.CONTACT_ID + " = ?" + val ops = ArrayList() + + if (contact is NativeContact) { + val nativeContact: NativeContact = contact + Log.i("[Contacts] Adding Android contact id ${nativeContact.nativeId} to batch removal") + val args = arrayOf(nativeContact.nativeId) + ops.add( + ContentProviderOperation.newDelete(ContactsContract.RawContacts.CONTENT_URI) + .withSelection(select, args) + .build() + ) + } + + if (contact.friend != null) { + Log.i("[Contacts] Removing friend") + contact.friend?.remove() + } + + if (ops.isNotEmpty()) { + try { + Log.i("[Contacts] Removing ${ops.size} contacts") + coreContext.context.contentResolver.applyBatch(ContactsContract.AUTHORITY, ops) + } catch (e: Exception) { + Log.e("[Contacts] $e") + } + } + } + fun deleteContacts(list: ArrayList) { val select = ContactsContract.Data.CONTACT_ID + " = ?" val ops = ArrayList() 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 f5853c06b..0b912516f 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 @@ -27,6 +27,7 @@ import androidx.lifecycle.ViewModelProvider import org.linphone.R import org.linphone.activities.main.viewmodels.DialogViewModel import org.linphone.activities.main.viewmodels.ListTopBarViewModel +import org.linphone.utils.AppUtils import org.linphone.utils.DialogUtils /** @@ -35,6 +36,7 @@ import org.linphone.utils.DialogUtils */ abstract class MasterFragment : Fragment() { protected lateinit var listSelectionViewModel: ListTopBarViewModel + protected open val dialogConfirmationMessageBeforeRemoval: Int = R.plurals.dialog_default_delete override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) @@ -60,7 +62,8 @@ abstract class MasterFragment : Fragment() { listSelectionViewModel.deleteSelectionEvent.observe(viewLifecycleOwner, Observer { it.consume { - val viewModel = DialogViewModel(getString(R.string.dialog_default_delete_message)) + val confirmationDialog = AppUtils.getStringWithPlural(dialogConfirmationMessageBeforeRemoval, listSelectionViewModel.selectedItems.value.orEmpty().size) + val viewModel = DialogViewModel(confirmationDialog) val dialog: Dialog = DialogUtils.getDialog(requireContext(), viewModel) viewModel.showCancelButton { 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 f05389ce1..2a7b26d89 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 @@ -19,15 +19,18 @@ */ package org.linphone.activities.main.history.fragments +import android.app.Dialog import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.content.ContextCompat import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import org.linphone.LinphoneApplication.Companion.coreContext @@ -35,14 +38,16 @@ 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.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.RecyclerViewHeaderDecoration +import org.linphone.utils.* class MasterCallLogsFragment : MasterFragment() { + override val dialogConfirmationMessageBeforeRemoval = R.plurals.history_delete_dialog private lateinit var binding: HistoryMasterFragmentBinding private lateinit var listViewModel: CallLogsListViewModel private lateinit var adapter: CallLogsListAdapter @@ -86,6 +91,34 @@ class MasterCallLogsFragment : MasterFragment() { val layoutManager = LinearLayoutManager(activity) binding.callLogsList.layoutManager = layoutManager + // Swipe action + val swipeConfiguration = RecyclerViewSwipeConfiguration() + val white = ContextCompat.getColor(requireContext(), R.color.white_color) + + swipeConfiguration.rightToLeftAction = RecyclerViewSwipeConfiguration.Action("Delete", white, ContextCompat.getColor(requireContext(), R.color.red_color)) + val swipeListener = object : RecyclerViewSwipeListener { + override fun onLeftToRightSwipe(viewHolder: RecyclerView.ViewHolder) {} + + override fun onRightToLeftSwipe(viewHolder: RecyclerView.ViewHolder) { + val viewModel = DialogViewModel(getString(R.string.history_delete_one_dialog)) + val dialog: Dialog = DialogUtils.getDialog(requireContext(), viewModel) + + viewModel.showCancelButton { + adapter.notifyItemChanged(viewHolder.adapterPosition) + dialog.dismiss() + } + + viewModel.showDeleteButton({ + listViewModel.deleteCallLog(listViewModel.callLogs.value?.get(viewHolder.adapterPosition)) + dialog.dismiss() + }, getString(R.string.dialog_delete)) + + dialog.show() + } + } + RecyclerViewSwipeUtils(ItemTouchHelper.LEFT, swipeConfiguration, swipeListener) + .attachToRecyclerView(binding.callLogsList) + // Divider between items val dividerItemDecoration = DividerItemDecoration(context, layoutManager.orientation) dividerItemDecoration.setDrawable(resources.getDrawable(R.drawable.divider, null)) 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 3f7ecb097..3544c642a 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 @@ -70,6 +70,23 @@ 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()) + + if (callLog != null) { + coreContext.core.removeCallLog(callLog) + list.remove(callLog) + missedList.remove(callLog) + } + + callLogs.value = list + missedCallLogs.value = missedList + } + fun deleteCallLogs(listToDelete: ArrayList) { val list = arrayListOf() list.addAll(callLogs.value.orEmpty()) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e93ec66c8..294c00b02 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -81,6 +81,12 @@ Calls No call in your history No missed call in your history + Do you want to delete this record? + Do you want to delete these records? + + @string/history_delete_one_dialog + @string/history_delete_many_dialog + Invite @@ -94,7 +100,12 @@ Organization Select a contact or create a new one Write Contacts permission denied, can\'t edit contact - Do you want to delete this contact?\nIt will also be removed from your device addressbook + Do you want to delete this contact?\nIt will also be removed from your device addressbook + Are you sure you want to delete these contacts?\nThey will also be removed from your device addressbook + + @string/contact_delete_one_dialog + @string/contact_delete_many_dialog + Enter a number or an address @@ -178,6 +189,12 @@ Chat room creation failed Chat room removal failed + Are you sure you want to delete this conversation? + Are you sure you want to delete these conversations? + + @string/chat_room_delete_one_dialog + @string/chat_room_delete_many_dialog + No recordings @@ -531,9 +548,14 @@ Delete OK Call - Are you sure you want to delete your selection? Instant messages are end-to-end encrypted in secured conversations. It is possible to upgrade the security level of a conversation by authenticating participants. To do so, call the contact and follow the authentication process. Operation in progress, please wait + Are you sure you want to delete this item? + Are you sure you want to delete your selection? + + @string/dialog_default_delete_one + @string/dialog_default_delete_many + Add a SIP address field