Added swipe to remove on call logs & contacts + improved confirmation dialog messages

This commit is contained in:
Sylvain Berfini 2020-06-17 15:52:01 +02:00
parent 766891301c
commit bf24a6b2a9
8 changed files with 150 additions and 9 deletions

View file

@ -49,6 +49,7 @@ import org.linphone.databinding.ChatRoomMasterFragmentBinding
import org.linphone.utils.* import org.linphone.utils.*
class MasterChatRoomsFragment : MasterFragment() { class MasterChatRoomsFragment : MasterFragment() {
override val dialogConfirmationMessageBeforeRemoval = R.plurals.chat_room_delete_dialog
private lateinit var binding: ChatRoomMasterFragmentBinding private lateinit var binding: ChatRoomMasterFragmentBinding
private lateinit var listViewModel: ChatRoomsListViewModel private lateinit var listViewModel: ChatRoomsListViewModel
private lateinit var adapter: ChatRoomsListAdapter private lateinit var adapter: ChatRoomsListAdapter
@ -94,6 +95,7 @@ class MasterChatRoomsFragment : MasterFragment() {
val layoutManager = LinearLayoutManager(activity) val layoutManager = LinearLayoutManager(activity)
binding.chatList.layoutManager = layoutManager binding.chatList.layoutManager = layoutManager
// Swipe action
val swipeConfiguration = RecyclerViewSwipeConfiguration() val swipeConfiguration = RecyclerViewSwipeConfiguration()
val white = ContextCompat.getColor(requireContext(), R.color.white_color) val white = ContextCompat.getColor(requireContext(), R.color.white_color)
@ -102,7 +104,7 @@ class MasterChatRoomsFragment : MasterFragment() {
override fun onLeftToRightSwipe(viewHolder: RecyclerView.ViewHolder) {} override fun onLeftToRightSwipe(viewHolder: RecyclerView.ViewHolder) {}
override fun onRightToLeftSwipe(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) val dialog: Dialog = DialogUtils.getDialog(requireContext(), viewModel)
viewModel.showCancelButton { viewModel.showCancelButton {

View file

@ -131,7 +131,7 @@ class DetailContactFragment : Fragment() {
} }
private fun confirmContactRemoval() { 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) val dialog: Dialog = DialogUtils.getDialog(requireContext(), dialogViewModel)
dialogViewModel.showCancelButton { dialogViewModel.showCancelButton {

View file

@ -19,33 +19,37 @@
*/ */
package org.linphone.activities.main.contact.fragments package org.linphone.activities.main.contact.fragments
import android.app.Dialog
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R import org.linphone.R
import org.linphone.activities.main.MainActivity import org.linphone.activities.main.MainActivity
import org.linphone.activities.main.contact.adapters.ContactsListAdapter import org.linphone.activities.main.contact.adapters.ContactsListAdapter
import org.linphone.activities.main.contact.viewmodels.ContactsListViewModel import org.linphone.activities.main.contact.viewmodels.ContactsListViewModel
import org.linphone.activities.main.fragments.MasterFragment import org.linphone.activities.main.fragments.MasterFragment
import org.linphone.activities.main.viewmodels.DialogViewModel
import org.linphone.activities.main.viewmodels.SharedMainViewModel import org.linphone.activities.main.viewmodels.SharedMainViewModel
import org.linphone.contact.Contact import org.linphone.contact.Contact
import org.linphone.core.Factory import org.linphone.core.Factory
import org.linphone.core.tools.Log import org.linphone.core.tools.Log
import org.linphone.databinding.ContactMasterFragmentBinding import org.linphone.databinding.ContactMasterFragmentBinding
import org.linphone.utils.Event import org.linphone.utils.*
import org.linphone.utils.PermissionHelper
import org.linphone.utils.RecyclerViewHeaderDecoration
class MasterContactsFragment : MasterFragment() { class MasterContactsFragment : MasterFragment() {
override val dialogConfirmationMessageBeforeRemoval = R.plurals.contact_delete_dialog
private lateinit var binding: ContactMasterFragmentBinding private lateinit var binding: ContactMasterFragmentBinding
private lateinit var listViewModel: ContactsListViewModel private lateinit var listViewModel: ContactsListViewModel
private lateinit var adapter: ContactsListAdapter private lateinit var adapter: ContactsListAdapter
@ -90,6 +94,34 @@ class MasterContactsFragment : MasterFragment() {
val layoutManager = LinearLayoutManager(activity) val layoutManager = LinearLayoutManager(activity)
binding.contactsList.layoutManager = layoutManager 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 // Divider between items
val dividerItemDecoration = DividerItemDecoration(context, layoutManager.orientation) val dividerItemDecoration = DividerItemDecoration(context, layoutManager.orientation)
dividerItemDecoration.setDrawable(resources.getDrawable(R.drawable.divider, null)) dividerItemDecoration.setDrawable(resources.getDrawable(R.drawable.divider, null))

View file

@ -73,6 +73,38 @@ class ContactsListViewModel : ViewModel() {
} }
} }
fun deleteContact(contact: Contact?) {
contact ?: return
val select = ContactsContract.Data.CONTACT_ID + " = ?"
val ops = ArrayList<ContentProviderOperation>()
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<Contact>) { fun deleteContacts(list: ArrayList<Contact>) {
val select = ContactsContract.Data.CONTACT_ID + " = ?" val select = ContactsContract.Data.CONTACT_ID + " = ?"
val ops = ArrayList<ContentProviderOperation>() val ops = ArrayList<ContentProviderOperation>()

View file

@ -27,6 +27,7 @@ import androidx.lifecycle.ViewModelProvider
import org.linphone.R import org.linphone.R
import org.linphone.activities.main.viewmodels.DialogViewModel import org.linphone.activities.main.viewmodels.DialogViewModel
import org.linphone.activities.main.viewmodels.ListTopBarViewModel import org.linphone.activities.main.viewmodels.ListTopBarViewModel
import org.linphone.utils.AppUtils
import org.linphone.utils.DialogUtils import org.linphone.utils.DialogUtils
/** /**
@ -35,6 +36,7 @@ import org.linphone.utils.DialogUtils
*/ */
abstract class MasterFragment : Fragment() { abstract class MasterFragment : Fragment() {
protected lateinit var listSelectionViewModel: ListTopBarViewModel protected lateinit var listSelectionViewModel: ListTopBarViewModel
protected open val dialogConfirmationMessageBeforeRemoval: Int = R.plurals.dialog_default_delete
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
@ -60,7 +62,8 @@ abstract class MasterFragment : Fragment() {
listSelectionViewModel.deleteSelectionEvent.observe(viewLifecycleOwner, Observer { listSelectionViewModel.deleteSelectionEvent.observe(viewLifecycleOwner, Observer {
it.consume { 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) val dialog: Dialog = DialogUtils.getDialog(requireContext(), viewModel)
viewModel.showCancelButton { viewModel.showCancelButton {

View file

@ -19,15 +19,18 @@
*/ */
package org.linphone.activities.main.history.fragments package org.linphone.activities.main.history.fragments
import android.app.Dialog
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import org.linphone.LinphoneApplication.Companion.coreContext 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.fragments.MasterFragment
import org.linphone.activities.main.history.adapters.CallLogsListAdapter import org.linphone.activities.main.history.adapters.CallLogsListAdapter
import org.linphone.activities.main.history.viewmodels.CallLogsListViewModel 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.SharedMainViewModel
import org.linphone.activities.main.viewmodels.TabsViewModel import org.linphone.activities.main.viewmodels.TabsViewModel
import org.linphone.core.CallLog import org.linphone.core.CallLog
import org.linphone.core.tools.Log import org.linphone.core.tools.Log
import org.linphone.databinding.HistoryMasterFragmentBinding import org.linphone.databinding.HistoryMasterFragmentBinding
import org.linphone.utils.RecyclerViewHeaderDecoration import org.linphone.utils.*
class MasterCallLogsFragment : MasterFragment() { class MasterCallLogsFragment : MasterFragment() {
override val dialogConfirmationMessageBeforeRemoval = R.plurals.history_delete_dialog
private lateinit var binding: HistoryMasterFragmentBinding private lateinit var binding: HistoryMasterFragmentBinding
private lateinit var listViewModel: CallLogsListViewModel private lateinit var listViewModel: CallLogsListViewModel
private lateinit var adapter: CallLogsListAdapter private lateinit var adapter: CallLogsListAdapter
@ -86,6 +91,34 @@ class MasterCallLogsFragment : MasterFragment() {
val layoutManager = LinearLayoutManager(activity) val layoutManager = LinearLayoutManager(activity)
binding.callLogsList.layoutManager = layoutManager 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 // Divider between items
val dividerItemDecoration = DividerItemDecoration(context, layoutManager.orientation) val dividerItemDecoration = DividerItemDecoration(context, layoutManager.orientation)
dividerItemDecoration.setDrawable(resources.getDrawable(R.drawable.divider, null)) dividerItemDecoration.setDrawable(resources.getDrawable(R.drawable.divider, null))

View file

@ -70,6 +70,23 @@ class CallLogsListViewModel : ViewModel() {
super.onCleared() super.onCleared()
} }
fun deleteCallLog(callLog: CallLog?) {
val list = arrayListOf<CallLog>()
list.addAll(callLogs.value.orEmpty())
val missedList = arrayListOf<CallLog>()
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<CallLog>) { fun deleteCallLogs(listToDelete: ArrayList<CallLog>) {
val list = arrayListOf<CallLog>() val list = arrayListOf<CallLog>()
list.addAll(callLogs.value.orEmpty()) list.addAll(callLogs.value.orEmpty())

View file

@ -81,6 +81,12 @@
<string name="history_calls_list">Calls</string> <string name="history_calls_list">Calls</string>
<string name="no_call_history">No call in your history</string> <string name="no_call_history">No call in your history</string>
<string name="no_missed_call_history">No missed call in your history</string> <string name="no_missed_call_history">No missed call in your history</string>
<string name="history_delete_one_dialog">Do you want to delete this record?</string>
<string name="history_delete_many_dialog">Do you want to delete these records?</string>
<plurals name="history_delete_dialog">
<item quantity="one">@string/history_delete_one_dialog</item>
<item quantity="other">@string/history_delete_many_dialog</item>
</plurals>
<!-- Contacts --> <!-- Contacts -->
<string name="contact_invite_friend">Invite</string> <string name="contact_invite_friend">Invite</string>
@ -94,7 +100,12 @@
<string name="contact_organization">Organization</string> <string name="contact_organization">Organization</string>
<string name="contact_choose_existing_or_new_to_add_number">Select a contact or create a new one</string> <string name="contact_choose_existing_or_new_to_add_number">Select a contact or create a new one</string>
<string name="contact_editor_write_permission_denied">Write Contacts permission denied, can\'t edit contact</string> <string name="contact_editor_write_permission_denied">Write Contacts permission denied, can\'t edit contact</string>
<string name="contact_confirm_removal_dialog">Do you want to delete this contact?\nIt will also be removed from your device addressbook</string> <string name="contact_delete_one_dialog">Do you want to delete this contact?\nIt will also be removed from your device addressbook</string>
<string name="contact_delete_many_dialog">Are you sure you want to delete these contacts?\nThey will also be removed from your device addressbook</string>
<plurals name="contact_delete_dialog">
<item quantity="one">@string/contact_delete_one_dialog</item>
<item quantity="other">@string/contact_delete_many_dialog</item>
</plurals>
<!-- Dialer --> <!-- Dialer -->
<string name="dialer_address_bar_hint">Enter a number or an address</string> <string name="dialer_address_bar_hint">Enter a number or an address</string>
@ -178,6 +189,12 @@
</plurals> </plurals>
<string name="chat_room_creation_failed_snack">Chat room creation failed</string> <string name="chat_room_creation_failed_snack">Chat room creation failed</string>
<string name="chat_room_removal_failed_snack">Chat room removal failed</string> <string name="chat_room_removal_failed_snack">Chat room removal failed</string>
<string name="chat_room_delete_one_dialog">Are you sure you want to delete this conversation?</string>
<string name="chat_room_delete_many_dialog">Are you sure you want to delete these conversations?</string>
<plurals name="chat_room_delete_dialog">
<item quantity="one">@string/chat_room_delete_one_dialog</item>
<item quantity="other">@string/chat_room_delete_many_dialog</item>
</plurals>
<!-- Recordings --> <!-- Recordings -->
<string name="recordings_empty_list">No recordings</string> <string name="recordings_empty_list">No recordings</string>
@ -531,9 +548,14 @@
<string name="dialog_delete">Delete</string> <string name="dialog_delete">Delete</string>
<string name="dialog_ok">OK</string> <string name="dialog_ok">OK</string>
<string name="dialog_call">Call</string> <string name="dialog_call">Call</string>
<string name="dialog_default_delete_message">Are you sure you want to delete your selection?</string>
<string name="dialog_lime_security_message">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.</string> <string name="dialog_lime_security_message">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.</string>
<string name="operation_in_progress_wait">Operation in progress, please wait</string> <string name="operation_in_progress_wait">Operation in progress, please wait</string>
<string name="dialog_default_delete_one">Are you sure you want to delete this item?</string>
<string name="dialog_default_delete_many">Are you sure you want to delete your selection?</string>
<plurals name="dialog_default_delete">
<item quantity="one">@string/dialog_default_delete_one</item>
<item quantity="other">@string/dialog_default_delete_many</item>
</plurals>
<!-- Content description --> <!-- Content description -->
<string name="content_description_add_sip_address_field">Add a SIP address field</string> <string name="content_description_add_sip_address_field">Add a SIP address field</string>