From c8ff7262d440b9bec0c1e71d864c1054b5ddf161 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Thu, 3 Apr 2025 10:05:43 +0200 Subject: [PATCH] Follow contacts list filter for every contact/address picker --- .../java/org/linphone/core/CoreContext.kt | 15 +++++ .../viewmodel/ContactsListViewModel.kt | 36 ++++-------- .../viewmodel/AddressSelectionViewModel.kt | 56 +++++++++---------- .../java/org/linphone/utils/LinphoneUtils.kt | 34 +++++++++++ 4 files changed, 86 insertions(+), 55 deletions(-) diff --git a/app/src/main/java/org/linphone/core/CoreContext.kt b/app/src/main/java/org/linphone/core/CoreContext.kt index f57969305..dcfaf6b77 100644 --- a/app/src/main/java/org/linphone/core/CoreContext.kt +++ b/app/src/main/java/org/linphone/core/CoreContext.kt @@ -180,6 +180,21 @@ class CoreContext @WorkerThread override fun onDefaultAccountChanged(core: Core, account: Account?) { defaultAccountHasVideoConferenceFactoryUri = account?.params?.audioVideoConferenceFactoryAddress != null + + val defaultDomain = corePreferences.defaultDomain + val isAccountOnDefaultDomain = account?.params?.domain == defaultDomain + val domainFilter = corePreferences.contactsFilter + Log.i("$TAG Currently selected filter is [$domainFilter]") + + if (!isAccountOnDefaultDomain && domainFilter == defaultDomain) { + corePreferences.contactsFilter = "*" + Log.i( + "$TAG New default account isn't on default domain, changing filter to any SIP contacts instead" + ) + } else if (isAccountOnDefaultDomain && domainFilter != "") { + corePreferences.contactsFilter = defaultDomain + Log.i("$TAG New default account is on default domain, using that domain as filter instead of wildcard") + } } @WorkerThread diff --git a/app/src/main/java/org/linphone/ui/main/contacts/viewmodel/ContactsListViewModel.kt b/app/src/main/java/org/linphone/ui/main/contacts/viewmodel/ContactsListViewModel.kt index e4064861b..e5293ea00 100644 --- a/app/src/main/java/org/linphone/ui/main/contacts/viewmodel/ContactsListViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/contacts/viewmodel/ContactsListViewModel.kt @@ -104,7 +104,8 @@ class ContactsListViewModel showFilter.value = !corePreferences.hidePhoneNumbers && !corePreferences.hideSipAddresses coreContext.postOnCoreThread { core -> - updateDomainFilter() + domainFilter = corePreferences.contactsFilter + checkIfDefaultAccountOnDefaultDomain() coreContext.contactsManager.addListener(contactsListener) magicSearch = core.createMagicSearch() @@ -141,7 +142,8 @@ class ContactsListViewModel @UiThread fun applyCurrentDefaultAccountFilter() { coreContext.postOnCoreThread { - updateDomainFilter() + domainFilter = corePreferences.contactsFilter + checkIfDefaultAccountOnDefaultDomain() coreContext.postOnMainThread { applyFilter(currentFilter) @@ -179,28 +181,6 @@ class ContactsListViewModel corePreferences.showFavoriteContacts = show } - @WorkerThread - private fun updateDomainFilter() { - val defaultAccount = coreContext.core.defaultAccount - val defaultDomain = corePreferences.defaultDomain - val isAccountOnDefaultDomain = defaultAccount?.params?.domain == defaultDomain - isDefaultAccountLinphone.postValue(isAccountOnDefaultDomain) - - domainFilter = corePreferences.contactsFilter - Log.i("$TAG Currently selected filter is [$domainFilter]") - if (!isAccountOnDefaultDomain && domainFilter == defaultDomain) { - domainFilter = "*" - corePreferences.contactsFilter = domainFilter - Log.i( - "$TAG New default account isn't on default domain, changing filter to all SIP contacts instead" - ) - } else if (isAccountOnDefaultDomain && domainFilter != "") { - domainFilter = defaultDomain - corePreferences.contactsFilter = domainFilter - Log.i("$TAG New default account is on default domain, using that as filter instead") - } - } - @UiThread fun exportContactAsVCard(friend: Friend) { coreContext.postOnCoreThread { @@ -354,4 +334,12 @@ class ContactsListViewModel Log.i("$TAG Processed [${results.size}] results into [${list.size} contacts]") firstLoad = false } + + @WorkerThread + private fun checkIfDefaultAccountOnDefaultDomain() { + val defaultAccount = coreContext.core.defaultAccount + val defaultDomain = corePreferences.defaultDomain + val isAccountOnDefaultDomain = defaultAccount?.params?.domain == defaultDomain + isDefaultAccountLinphone.postValue(isAccountOnDefaultDomain) + } } diff --git a/app/src/main/java/org/linphone/ui/main/viewmodel/AddressSelectionViewModel.kt b/app/src/main/java/org/linphone/ui/main/viewmodel/AddressSelectionViewModel.kt index c095029ba..b4898578f 100644 --- a/app/src/main/java/org/linphone/ui/main/viewmodel/AddressSelectionViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/viewmodel/AddressSelectionViewModel.kt @@ -38,7 +38,6 @@ import org.linphone.mediastream.Log import org.linphone.ui.main.contacts.model.ContactAvatarModel import org.linphone.ui.main.model.ConversationContactOrSuggestionModel import org.linphone.ui.main.model.SelectedAddressModel -import org.linphone.ui.main.model.isEndToEndEncryptionMandatory import org.linphone.utils.AppUtils import org.linphone.utils.LinphoneUtils @@ -68,8 +67,6 @@ abstract class AddressSelectionViewModel private var currentFilter = "" private var previousFilter = "NotSet" - private var limitSearchToLinphoneAccounts = true - private lateinit var magicSearch: MagicSearch private val magicSearchListener = object : MagicSearchListenerStub() { @@ -86,7 +83,6 @@ abstract class AddressSelectionViewModel Log.i("$TAG Contacts have been (re)loaded, updating list") applyFilter( currentFilter, - if (limitSearchToLinphoneAccounts) corePreferences.defaultDomain else "", magicSearchSourceFlags ) } @@ -100,8 +96,6 @@ abstract class AddressSelectionViewModel isEmpty.value = true coreContext.postOnCoreThread { core -> - limitSearchToLinphoneAccounts = isEndToEndEncryptionMandatory() - coreContext.contactsManager.addListener(contactsListener) magicSearch = core.createMagicSearch() magicSearch.limitedSearch = true @@ -220,7 +214,6 @@ abstract class AddressSelectionViewModel coreContext.postOnCoreThread { applyFilter( filter, - if (limitSearchToLinphoneAccounts) corePreferences.defaultDomain else "", magicSearchSourceFlags ) } @@ -229,7 +222,6 @@ abstract class AddressSelectionViewModel @WorkerThread private fun applyFilter( filter: String, - domain: String, sources: Int ) { if (previousFilter.isNotEmpty() && ( @@ -242,6 +234,7 @@ abstract class AddressSelectionViewModel currentFilter = filter previousFilter = filter + val domain = corePreferences.contactsFilter Log.i( "$TAG Asking Magic search for contacts matching filter [$filter], domain [$domain] and in sources [$sources]" ) @@ -269,23 +262,14 @@ abstract class AddressSelectionViewModel for (result in results) { val address = result.address - if (address != null) { - if (result.sourceFlags == MagicSearch.Source.Request.toInt()) { - val model = ConversationContactOrSuggestionModel(address) { - coreContext.startAudioCall(address) - } - suggestionsList.add(model) - continue - } + val friend = result.friend + if (friend != null) { + val found = contactsList.find { it.friend == friend } + if (found != null) continue - val friend = result.friend ?: coreContext.contactsManager.findContactByAddress( - address - ) - if (friend != null) { - val found = contactsList.find { it.friend == friend } - if (found != null) continue - - val model = ConversationContactOrSuggestionModel(address, friend = friend) + val mainAddress = address ?: LinphoneUtils.getFirstAvailableAddressForFriend(friend) + if (mainAddress != null) { + val model = ConversationContactOrSuggestionModel(mainAddress, friend = friend) val avatarModel = coreContext.contactsManager.getContactAvatarModelForFriend( friend ) @@ -297,18 +281,28 @@ abstract class AddressSelectionViewModel contactsList.add(model) } } else { - val defaultAccountAddress = coreContext.core.defaultAccount?.params?.identityAddress - if (defaultAccountAddress != null && address.weakEqual(defaultAccountAddress)) { - Log.i("$TAG Removing from suggestions current default account address") - continue - } - + Log.w("$TAG Found friend [${friend.name}] in search results but no Address could be found, skipping it") + } + } else if (address != null) { + if (result.sourceFlags == MagicSearch.Source.Request.toInt()) { val model = ConversationContactOrSuggestionModel(address) { coreContext.startAudioCall(address) } - suggestionsList.add(model) + continue } + + val defaultAccountAddress = coreContext.core.defaultAccount?.params?.identityAddress + if (defaultAccountAddress != null && address.weakEqual(defaultAccountAddress)) { + Log.i("$TAG Removing from suggestions current default account address") + continue + } + + val model = ConversationContactOrSuggestionModel(address) { + coreContext.startAudioCall(address) + } + + suggestionsList.add(model) } } diff --git a/app/src/main/java/org/linphone/utils/LinphoneUtils.kt b/app/src/main/java/org/linphone/utils/LinphoneUtils.kt index 9d5351528..24fb71d6b 100644 --- a/app/src/main/java/org/linphone/utils/LinphoneUtils.kt +++ b/app/src/main/java/org/linphone/utils/LinphoneUtils.kt @@ -143,6 +143,40 @@ class LinphoneUtils { return null } + @WorkerThread + fun getFirstAvailableAddressForFriend(friend: Friend): Address? { + // Return any SIP address first + val address = friend.address ?: friend.addresses.firstOrNull() + if (address != null) return address + + val phoneNumbers = friend.phoneNumbers + // If no SIP address stored in Friend, check for SIP address in phone numbers presence + for (phoneNumber in phoneNumbers) { + val presenceModel = friend.getPresenceModelForUriOrTel(phoneNumber) + val hasPresenceInfo = !presenceModel?.contact.isNullOrEmpty() + if (presenceModel != null && hasPresenceInfo) { + val contact = presenceModel.contact + if (!contact.isNullOrEmpty()) { + val address = coreContext.core.interpretUrl(contact, false) + if (address != null) { + address.clean() // To remove ;user=phone + return address + } + } + } + } + + // Finally format any phone number as SIP address + for (phoneNumber in phoneNumbers) { + val address = coreContext.core.interpretUrl(phoneNumber, false) + if (address != null) { + return address + } + } + + return null + } + @AnyThread fun isCallIncoming(callState: Call.State): Boolean { return when (callState) {