From e147efd358bf1167e208a2101501be5528d4feff Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Tue, 8 Aug 2023 16:09:25 +0200 Subject: [PATCH] Added long press context menu to phone number & sip address in contact details --- ...Data.kt => ContactNumberOrAddressModel.kt} | 13 ++++- .../ui/contacts/fragment/ContactFragment.kt | 16 +++++- ...ontactNumberOrAddressMenuDialogFragment.kt | 56 +++++++++++++++++++ .../ui/contacts/viewmodel/ContactViewModel.kt | 23 +++++--- .../linphone/ui/fragment/GenericFragment.kt | 18 +++--- .../res/drawable/shape_white_background.xml | 1 - .../main/res/layout-land/bottom_nav_bar.xml | 1 + app/src/main/res/layout/bottom_nav_bar.xml | 1 + app/src/main/res/layout/contact_fragment.xml | 14 ----- .../contact_number_address_list_cell.xml | 23 +++----- ...tact_number_or_address_long_press_menu.xml | 33 +++++++++++ app/src/main/res/values/styles.xml | 4 +- 12 files changed, 151 insertions(+), 52 deletions(-) rename app/src/main/java/org/linphone/ui/contacts/{ContactNumberOrAddressData.kt => ContactNumberOrAddressModel.kt} (82%) create mode 100644 app/src/main/java/org/linphone/ui/contacts/fragment/ContactNumberOrAddressMenuDialogFragment.kt create mode 100644 app/src/main/res/layout/contact_number_or_address_long_press_menu.xml diff --git a/app/src/main/java/org/linphone/ui/contacts/ContactNumberOrAddressData.kt b/app/src/main/java/org/linphone/ui/contacts/ContactNumberOrAddressModel.kt similarity index 82% rename from app/src/main/java/org/linphone/ui/contacts/ContactNumberOrAddressData.kt rename to app/src/main/java/org/linphone/ui/contacts/ContactNumberOrAddressModel.kt index 0c35ae701..d8693b99f 100644 --- a/app/src/main/java/org/linphone/ui/contacts/ContactNumberOrAddressData.kt +++ b/app/src/main/java/org/linphone/ui/contacts/ContactNumberOrAddressModel.kt @@ -19,15 +19,18 @@ */ package org.linphone.ui.contacts +import androidx.lifecycle.MutableLiveData import org.linphone.core.Address -class ContactNumberOrAddressData( +class ContactNumberOrAddressModel( val address: Address?, val displayedValue: String, private val listener: ContactNumberOrAddressClickListener, val isSip: Boolean = true, val label: String = "" ) { + val selected = MutableLiveData() + fun startCall() { address ?: return listener.onCall(address) @@ -42,6 +45,12 @@ class ContactNumberOrAddressData( address ?: return listener.onChat(address) } + + fun onLongPress(): Boolean { + selected.value = true + listener.onLongPress(this) + return true + } } interface ContactNumberOrAddressClickListener { @@ -50,4 +59,6 @@ interface ContactNumberOrAddressClickListener { fun onVideoCall(address: Address) fun onChat(address: Address) + + fun onLongPress(model: ContactNumberOrAddressModel) } diff --git a/app/src/main/java/org/linphone/ui/contacts/fragment/ContactFragment.kt b/app/src/main/java/org/linphone/ui/contacts/fragment/ContactFragment.kt index f64f883c9..0c4f27df0 100644 --- a/app/src/main/java/org/linphone/ui/contacts/fragment/ContactFragment.kt +++ b/app/src/main/java/org/linphone/ui/contacts/fragment/ContactFragment.kt @@ -74,10 +74,22 @@ class ContactFragment : GenericFragment() { viewModel.showBackButton.value = slideable } - viewModel.contact.observe(viewLifecycleOwner) { + viewModel.contactFoundEvent.observe(viewLifecycleOwner) { (view.parent as? ViewGroup)?.doOnPreDraw { startPostponedEnterTransition() - sharedViewModel.openSlidingPaneEvent.value = Event(true) + } + sharedViewModel.openSlidingPaneEvent.value = Event(true) + } + + viewModel.showLongPressMenuForNumberOrAddress.observe(viewLifecycleOwner) { + it.consume { model -> + val modalBottomSheet = ContactNumberOrAddressMenuDialogFragment() { + model.selected.value = false + } + modalBottomSheet.show( + parentFragmentManager, + ContactNumberOrAddressMenuDialogFragment.TAG + ) } } } diff --git a/app/src/main/java/org/linphone/ui/contacts/fragment/ContactNumberOrAddressMenuDialogFragment.kt b/app/src/main/java/org/linphone/ui/contacts/fragment/ContactNumberOrAddressMenuDialogFragment.kt new file mode 100644 index 000000000..b9209ce48 --- /dev/null +++ b/app/src/main/java/org/linphone/ui/contacts/fragment/ContactNumberOrAddressMenuDialogFragment.kt @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2010-2023 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.ui.contacts.fragment + +import android.content.DialogInterface +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import org.linphone.databinding.ContactNumberOrAddressLongPressMenuBinding + +class ContactNumberOrAddressMenuDialogFragment( + private val onDismiss: (() -> Unit)? = null +) : BottomSheetDialogFragment() { + companion object { + const val TAG = "ContactNumberOrAddressMenuDialogFragment" + } + + override fun onCancel(dialog: DialogInterface) { + onDismiss?.invoke() + super.onCancel(dialog) + } + + override fun onDismiss(dialog: DialogInterface) { + onDismiss?.invoke() + super.onDismiss(dialog) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + val view = ContactNumberOrAddressLongPressMenuBinding.inflate(layoutInflater) + + return view.root + } +} diff --git a/app/src/main/java/org/linphone/ui/contacts/viewmodel/ContactViewModel.kt b/app/src/main/java/org/linphone/ui/contacts/viewmodel/ContactViewModel.kt index 6cf40f4b0..0b0f2c7e5 100644 --- a/app/src/main/java/org/linphone/ui/contacts/viewmodel/ContactViewModel.kt +++ b/app/src/main/java/org/linphone/ui/contacts/viewmodel/ContactViewModel.kt @@ -24,14 +24,14 @@ import androidx.lifecycle.ViewModel import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.core.Address import org.linphone.ui.contacts.ContactNumberOrAddressClickListener -import org.linphone.ui.contacts.ContactNumberOrAddressData +import org.linphone.ui.contacts.ContactNumberOrAddressModel import org.linphone.ui.contacts.model.ContactModel import org.linphone.utils.Event class ContactViewModel : ViewModel() { val contact = MutableLiveData() - val sipAddressesAndPhoneNumbers = MutableLiveData>() + val sipAddressesAndPhoneNumbers = MutableLiveData>() val company = MutableLiveData() @@ -43,7 +43,11 @@ class ContactViewModel : ViewModel() { val showDevicesTrust = MutableLiveData() - val friendFoundEvent = MutableLiveData>() + val contactFoundEvent = MutableLiveData>() + + val showLongPressMenuForNumberOrAddress: MutableLiveData> by lazy { + MutableLiveData>() + } val listener = object : ContactNumberOrAddressClickListener { override fun onCall(address: Address) { @@ -57,6 +61,10 @@ class ContactViewModel : ViewModel() { override fun onChat(address: Address) { // UI thread } + + override fun onLongPress(model: ContactNumberOrAddressModel) { + showLongPressMenuForNumberOrAddress.value = Event(model) + } } init { @@ -69,15 +77,16 @@ class ContactViewModel : ViewModel() { coreContext.postOnCoreThread { core -> val friend = coreContext.contactsManager.findContactById(refKey) if (friend != null) { + contact.postValue(ContactModel(friend)) val organization = friend.organization if (!organization.isNullOrEmpty()) { company.postValue(organization) showCompany.postValue(true) } - val addressesAndNumbers = arrayListOf() + val addressesAndNumbers = arrayListOf() for (address in friend.addresses) { - val data = ContactNumberOrAddressData( + val data = ContactNumberOrAddressModel( address, address.asStringUriOnly(), listener, @@ -87,7 +96,7 @@ class ContactViewModel : ViewModel() { } for (number in friend.phoneNumbersWithLabel) { val address = core.interpretUrl(number.phoneNumber, true) - val data = ContactNumberOrAddressData( + val data = ContactNumberOrAddressModel( address, number.phoneNumber, listener, @@ -97,7 +106,7 @@ class ContactViewModel : ViewModel() { addressesAndNumbers.add(data) } sipAddressesAndPhoneNumbers.postValue(addressesAndNumbers) - contact.postValue(ContactModel(friend)) + contactFoundEvent.postValue(Event(true)) } } } diff --git a/app/src/main/java/org/linphone/ui/fragment/GenericFragment.kt b/app/src/main/java/org/linphone/ui/fragment/GenericFragment.kt index fadf9ccf9..9e7634e4d 100644 --- a/app/src/main/java/org/linphone/ui/fragment/GenericFragment.kt +++ b/app/src/main/java/org/linphone/ui/fragment/GenericFragment.kt @@ -37,11 +37,11 @@ abstract class GenericFragment : Fragment() { override fun handleOnBackPressed() { try { val navController = findNavController() - Log.i("[Generic Fragment] ${getFragmentRealClassName()} handleOnBackPressed") + Log.d("[Generic Fragment] ${getFragmentRealClassName()} handleOnBackPressed") if (!navController.popBackStack()) { - Log.i("[Generic Fragment] ${getFragmentRealClassName()} couldn't pop") + Log.d("[Generic Fragment] ${getFragmentRealClassName()} couldn't pop") if (!navController.navigateUp()) { - Log.i( + Log.d( "[Generic Fragment] ${getFragmentRealClassName()} couldn't navigate up" ) // Disable this callback & start a new back press event @@ -65,7 +65,7 @@ abstract class GenericFragment : Fragment() { } sharedViewModel.isSlidingPaneSlideable.observe(viewLifecycleOwner) { - Log.i( + Log.d( "[Generic Fragment] ${getFragmentRealClassName()} shared main VM sliding pane has changed" ) onBackPressedCallback.isEnabled = backPressedCallBackEnabled() @@ -94,11 +94,11 @@ abstract class GenericFragment : Fragment() { } private fun setupBackPressCallback() { - Log.i("[Generic Fragment] ${getFragmentRealClassName()} setupBackPressCallback") + Log.d("[Generic Fragment] ${getFragmentRealClassName()} setupBackPressCallback") val backButton = view?.findViewById(R.id.back) if (backButton != null && backButton.visibility == View.VISIBLE) { - Log.i("[Generic Fragment] ${getFragmentRealClassName()} found back button") + Log.d("[Generic Fragment] ${getFragmentRealClassName()} found back button") // If popping navigation back stack entry would bring us to an "empty" fragment // then don't do it if sliding pane layout isn't "flat" onBackPressedCallback.isEnabled = backPressedCallBackEnabled() @@ -120,15 +120,15 @@ abstract class GenericFragment : Fragment() { if (findNavController().graph.id == R.id.main_nav_graph) return false val isSlidingPaneFlat = sharedViewModel.isSlidingPaneSlideable.value == false - Log.i( + Log.d( "[Generic Fragment] ${getFragmentRealClassName()} isSlidingPaneFlat ? $isSlidingPaneFlat" ) val isPreviousFragmentEmpty = findNavController().previousBackStackEntry?.destination?.id == R.id.emptyFragment - Log.i( + Log.d( "[Generic Fragment] ${getFragmentRealClassName()} isPreviousFragmentEmpty ? $isPreviousFragmentEmpty" ) val popBackStack = isSlidingPaneFlat || !isPreviousFragmentEmpty - Log.i("[Generic Fragment] ${getFragmentRealClassName()} popBackStack ? $popBackStack") + Log.d("[Generic Fragment] ${getFragmentRealClassName()} popBackStack ? $popBackStack") return popBackStack } } diff --git a/app/src/main/res/drawable/shape_white_background.xml b/app/src/main/res/drawable/shape_white_background.xml index 500dd8cd8..95a658bb5 100644 --- a/app/src/main/res/drawable/shape_white_background.xml +++ b/app/src/main/res/drawable/shape_white_background.xml @@ -1,5 +1,4 @@ - \ No newline at end of file diff --git a/app/src/main/res/layout-land/bottom_nav_bar.xml b/app/src/main/res/layout-land/bottom_nav_bar.xml index 9d6ce4a40..69ae0f009 100644 --- a/app/src/main/res/layout-land/bottom_nav_bar.xml +++ b/app/src/main/res/layout-land/bottom_nav_bar.xml @@ -80,6 +80,7 @@ - - + type="org.linphone.ui.contacts.ContactNumberOrAddressModel" /> - - + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index b931bf6c5..d7997d667 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -3,7 +3,7 @@