From ab1ea3392d5920a7311306e51b0f3f621a45fa4a Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Wed, 21 Aug 2024 10:12:24 +0200 Subject: [PATCH] Fixed duplicated phone numbers in loaded contacts from native addressbook + improved code by factorizing some parts of it --- .../org/linphone/contacts/ContactLoader.kt | 11 ++- .../org/linphone/contacts/ContactsManager.kt | 35 +++++--- .../AbstractNewTransferCallFragment.kt | 28 ++----- .../ConversationForwardMessageViewModel.kt | 26 ++---- .../contacts/viewmodel/ContactViewModel.kt | 80 ++++--------------- .../fragment/GenericAddressPickerFragment.kt | 26 ++---- .../ui/main/help/viewmodel/HelpViewModel.kt | 17 ++++ .../java/org/linphone/utils/LinphoneUtils.kt | 30 +++++++ .../main/res/layout/help_debug_fragment.xml | 21 +++++ app/src/main/res/values-fr/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 11 files changed, 133 insertions(+), 143 deletions(-) diff --git a/app/src/main/java/org/linphone/contacts/ContactLoader.kt b/app/src/main/java/org/linphone/contacts/ContactLoader.kt index ddfcc1a6b..6e8843b3d 100644 --- a/app/src/main/java/org/linphone/contacts/ContactLoader.kt +++ b/app/src/main/java/org/linphone/contacts/ContactLoader.kt @@ -215,9 +215,14 @@ class ContactLoader : LoaderManager.LoaderCallbacks { } if (number != null) { - val phoneNumber = Factory.instance() - .createFriendPhoneNumber(number, label) - friend.addPhoneNumberWithLabel(phoneNumber) + if (friend.phoneNumbersWithLabel.find { + PhoneNumberUtils.arePhoneNumberWeakEqual(it.phoneNumber, number) + } == null + ) { + val phoneNumber = Factory.instance() + .createFriendPhoneNumber(number, label) + friend.addPhoneNumberWithLabel(phoneNumber) + } } } ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE -> { diff --git a/app/src/main/java/org/linphone/contacts/ContactsManager.kt b/app/src/main/java/org/linphone/contacts/ContactsManager.kt index d3286d9ce..aee09a67a 100644 --- a/app/src/main/java/org/linphone/contacts/ContactsManager.kt +++ b/app/src/main/java/org/linphone/contacts/ContactsManager.kt @@ -730,22 +730,33 @@ fun Friend.getPerson(): Person { return personBuilder.build() } +@WorkerThread +fun Friend.getListOfSipAddresses(): ArrayList
{ + val addressesList = arrayListOf
() + + for (address in addresses) { + if (addressesList.find { it.weakEqual(address) } == null) { + addressesList.add(address) + } + } + + return addressesList +} + @WorkerThread fun Friend.getListOfSipAddressesAndPhoneNumbers(listener: ContactNumberOrAddressClickListener): ArrayList { val addressesAndNumbers = arrayListOf() - for (address in addresses) { - if (addressesAndNumbers.find { it.address?.weakEqual(address) == true } == null) { - val data = ContactNumberOrAddressModel( - this, - address, - address.asStringUriOnly(), - true, // SIP addresses are always enabled - listener, - true - ) - addressesAndNumbers.add(data) - } + for (address in getListOfSipAddresses()) { + val data = ContactNumberOrAddressModel( + this, + address, + address.asStringUriOnly(), + true, // SIP addresses are always enabled + listener, + true + ) + addressesAndNumbers.add(data) } val indexOfLastSipAddress = addressesAndNumbers.count() diff --git a/app/src/main/java/org/linphone/ui/call/fragment/AbstractNewTransferCallFragment.kt b/app/src/main/java/org/linphone/ui/call/fragment/AbstractNewTransferCallFragment.kt index 14d67cfa2..7b90c50b7 100644 --- a/app/src/main/java/org/linphone/ui/call/fragment/AbstractNewTransferCallFragment.kt +++ b/app/src/main/java/org/linphone/ui/call/fragment/AbstractNewTransferCallFragment.kt @@ -43,7 +43,6 @@ import org.linphone.ui.main.contacts.model.ContactNumberOrAddressModel import org.linphone.ui.main.contacts.model.NumberOrAddressPickerDialogModel import org.linphone.ui.main.history.viewmodel.StartCallViewModel import org.linphone.ui.main.model.ConversationContactOrSuggestionModel -import org.linphone.ui.main.model.isEndToEndEncryptionMandatory import org.linphone.utils.DialogUtils import org.linphone.utils.LinphoneUtils import org.linphone.utils.RecyclerViewHeaderDecoration @@ -214,36 +213,19 @@ abstract class AbstractNewTransferCallFragment : GenericCallFragment() { abstract fun action(address: Address) private fun startCall(model: ConversationContactOrSuggestionModel) { - coreContext.postOnCoreThread { core -> + coreContext.postOnCoreThread { val friend = model.friend if (friend == null) { action(model.address) return@postOnCoreThread } - val addressesCount = friend.addresses.size - val numbersCount = friend.phoneNumbers.size - - // Do not consider phone numbers if default account is in secure mode - val enablePhoneNumbers = !isEndToEndEncryptionMandatory() - - if (addressesCount == 1 && (numbersCount == 0 || !enablePhoneNumbers)) { + val singleAvailableAddress = LinphoneUtils.getSingleAvailableAddressForFriend(friend) + if (singleAvailableAddress != null) { Log.i( - "$TAG Only 1 SIP address found for contact [${friend.name}], starting call directly" + "$TAG Only 1 SIP address or phone number found for contact [${friend.name}], starting call directly" ) - val address = friend.addresses.first() - action(address) - } else if (addressesCount == 0 && numbersCount == 1 && enablePhoneNumbers) { - val number = friend.phoneNumbers.first() - val address = core.interpretUrl(number, LinphoneUtils.applyInternationalPrefix()) - if (address != null) { - Log.i( - "$TAG Only 1 phone number found for contact [${friend.name}], starting call directly" - ) - action(address) - } else { - Log.e("$TAG Failed to interpret phone number [$number] as SIP address") - } + action(singleAvailableAddress) } else { val list = friend.getListOfSipAddressesAndPhoneNumbers(listener) Log.i( diff --git a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationForwardMessageViewModel.kt b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationForwardMessageViewModel.kt index 7e10c4a72..7a6198ecc 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationForwardMessageViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationForwardMessageViewModel.kt @@ -34,7 +34,6 @@ import org.linphone.core.tools.Log import org.linphone.ui.main.contacts.model.ContactNumberOrAddressClickListener import org.linphone.ui.main.contacts.model.ContactNumberOrAddressModel import org.linphone.ui.main.model.ConversationContactOrSuggestionModel -import org.linphone.ui.main.model.isEndToEndEncryptionMandatory import org.linphone.ui.main.viewmodel.AddressSelectionViewModel import org.linphone.utils.AppUtils import org.linphone.utils.Event @@ -158,25 +157,12 @@ class ConversationForwardMessageViewModel @UiThread constructor() : AddressSelec return@postOnCoreThread } - val addressesCount = friend.addresses.size - val numbersCount = friend.phoneNumbers.size - - // Do not consider phone numbers if default account is in secure mode - val enablePhoneNumbers = !isEndToEndEncryptionMandatory() - - if (addressesCount == 1 && (numbersCount == 0 || !enablePhoneNumbers)) { - val address = friend.addresses.first() - Log.i("$TAG Only 1 SIP address found for contact [${friend.name}], using it") - onAddressSelected(address) - } else if (addressesCount == 0 && numbersCount == 1 && enablePhoneNumbers) { - val number = friend.phoneNumbers.first() - val address = core.interpretUrl(number, LinphoneUtils.applyInternationalPrefix()) - if (address != null) { - Log.i("$TAG Only 1 phone number found for contact [${friend.name}], using it") - onAddressSelected(address) - } else { - Log.e("$TAG Failed to interpret phone number [$number] as SIP address") - } + val singleAvailableAddress = LinphoneUtils.getSingleAvailableAddressForFriend(friend) + if (singleAvailableAddress != null) { + Log.i( + "$TAG Only 1 SIP address or phone number found for contact [${friend.name}], using it" + ) + onAddressSelected(singleAvailableAddress) } else { val list = friend.getListOfSipAddressesAndPhoneNumbers(listener) Log.i( diff --git a/app/src/main/java/org/linphone/ui/main/contacts/viewmodel/ContactViewModel.kt b/app/src/main/java/org/linphone/ui/main/contacts/viewmodel/ContactViewModel.kt index 8693ef4ad..09f14f48e 100644 --- a/app/src/main/java/org/linphone/ui/main/contacts/viewmodel/ContactViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/contacts/viewmodel/ContactViewModel.kt @@ -430,30 +430,13 @@ class ContactViewModel @UiThread constructor() : GenericViewModel() { @UiThread fun startAudioCall() { - coreContext.postOnCoreThread { core -> - val addressesCount = friend.addresses.size - val numbersCount = friend.phoneNumbers.size - - // Do not consider phone numbers if default account is in secure mode - val enablePhoneNumbers = !isEndToEndEncryptionMandatory() - - if (addressesCount == 1 && (numbersCount == 0 || !enablePhoneNumbers)) { + coreContext.postOnCoreThread { + val singleAvailableAddress = LinphoneUtils.getSingleAvailableAddressForFriend(friend) + if (singleAvailableAddress != null) { Log.i( - "$TAG Only 1 SIP address found for contact [${friend.name}], starting audio call directly" + "$TAG Only 1 SIP address or phone number found for contact [${friend.name}], starting audio call directly" ) - val address = friend.addresses.first() - coreContext.startAudioCall(address) - } else if (addressesCount == 0 && numbersCount == 1 && enablePhoneNumbers) { - val number = friend.phoneNumbers.first() - val address = core.interpretUrl(number, LinphoneUtils.applyInternationalPrefix()) - if (address != null) { - Log.i( - "$TAG Only 1 phone number found for contact [${friend.name}], starting audio call directly" - ) - coreContext.startAudioCall(address) - } else { - Log.e("$TAG Failed to interpret phone number [$number] as SIP address") - } + coreContext.startAudioCall(singleAvailableAddress) } else { expectedAction = START_AUDIO_CALL val list = sipAddressesAndPhoneNumbers.value.orEmpty() @@ -467,30 +450,13 @@ class ContactViewModel @UiThread constructor() : GenericViewModel() { @UiThread fun startVideoCall() { - coreContext.postOnCoreThread { core -> - val addressesCount = friend.addresses.size - val numbersCount = friend.phoneNumbers.size - - // Do not consider phone numbers if default account is in secure mode - val enablePhoneNumbers = !isEndToEndEncryptionMandatory() - - if (addressesCount == 1 && (numbersCount == 0 || !enablePhoneNumbers)) { + coreContext.postOnCoreThread { + val singleAvailableAddress = LinphoneUtils.getSingleAvailableAddressForFriend(friend) + if (singleAvailableAddress != null) { Log.i( - "$TAG Only 1 SIP address found for contact [${friend.name}], starting video call directly" + "$TAG Only 1 SIP address or phone number found for contact [${friend.name}], starting video call directly" ) - val address = friend.addresses.first() - coreContext.startVideoCall(address) - } else if (addressesCount == 0 && numbersCount == 1 && enablePhoneNumbers) { - val number = friend.phoneNumbers.first() - val address = core.interpretUrl(number, LinphoneUtils.applyInternationalPrefix()) - if (address != null) { - Log.i( - "$TAG Only 1 phone number found for contact [${friend.name}], starting video call directly" - ) - coreContext.startVideoCall(address) - } else { - Log.e("$TAG Failed to interpret phone number [$number] as SIP address") - } + coreContext.startVideoCall(singleAvailableAddress) } else { expectedAction = START_VIDEO_CALL val list = sipAddressesAndPhoneNumbers.value.orEmpty() @@ -504,29 +470,13 @@ class ContactViewModel @UiThread constructor() : GenericViewModel() { @UiThread fun goToConversation() { - coreContext.postOnCoreThread { core -> - val addressesCount = friend.addresses.size - val numbersCount = friend.phoneNumbers.size - - // Do not consider phone numbers if default account is in secure mode - val enablePhoneNumbers = !isEndToEndEncryptionMandatory() - - if (addressesCount == 1 && (numbersCount == 0 || !enablePhoneNumbers)) { + coreContext.postOnCoreThread { + val singleAvailableAddress = LinphoneUtils.getSingleAvailableAddressForFriend(friend) + if (singleAvailableAddress != null) { Log.i( - "$TAG Only 1 SIP address found for contact [${friend.name}], sending message directly" + "$TAG Only 1 SIP address or phone number found for contact [${friend.name}], sending message directly" ) - goToConversation(friend.addresses.first()) - } else if (addressesCount == 0 && numbersCount == 1 && enablePhoneNumbers) { - val number = friend.phoneNumbers.first() - val address = core.interpretUrl(number, LinphoneUtils.applyInternationalPrefix()) - if (address != null) { - Log.i( - "$TAG Only 1 phone number found for contact [${friend.name}], sending message directly" - ) - goToConversation(address) - } else { - Log.e("$TAG Failed to interpret phone number [$number] as SIP address") - } + goToConversation(singleAvailableAddress) } else { expectedAction = START_CONVERSATION val list = sipAddressesAndPhoneNumbers.value.orEmpty() diff --git a/app/src/main/java/org/linphone/ui/main/fragment/GenericAddressPickerFragment.kt b/app/src/main/java/org/linphone/ui/main/fragment/GenericAddressPickerFragment.kt index 36ba4859f..1ba8edf89 100644 --- a/app/src/main/java/org/linphone/ui/main/fragment/GenericAddressPickerFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/fragment/GenericAddressPickerFragment.kt @@ -37,7 +37,6 @@ import org.linphone.ui.main.contacts.model.ContactNumberOrAddressModel import org.linphone.ui.main.contacts.model.NumberOrAddressPickerDialogModel import org.linphone.ui.main.model.ConversationContactOrSuggestionModel import org.linphone.ui.main.model.SelectedAddressModel -import org.linphone.ui.main.model.isEndToEndEncryptionMandatory import org.linphone.ui.main.viewmodel.AddressSelectionViewModel import org.linphone.utils.DialogUtils import org.linphone.utils.LinphoneUtils @@ -158,25 +157,12 @@ abstract class GenericAddressPickerFragment : GenericMainFragment() { return@postOnCoreThread } - val addressesCount = friend.addresses.size - val numbersCount = friend.phoneNumbers.size - - // Do not consider phone numbers if default account is in secure mode - val enablePhoneNumbers = !isEndToEndEncryptionMandatory() - - if (addressesCount == 1 && (numbersCount == 0 || !enablePhoneNumbers)) { - val address = friend.addresses.first() - Log.i("$TAG Only 1 SIP address found for contact [${friend.name}], using it") - onAddressSelected(address, friend) - } else if (addressesCount == 0 && numbersCount == 1 && enablePhoneNumbers) { - val number = friend.phoneNumbers.first() - val address = core.interpretUrl(number, LinphoneUtils.applyInternationalPrefix()) - if (address != null) { - Log.i("$TAG Only 1 phone number found for contact [${friend.name}], using it") - onAddressSelected(address, friend) - } else { - Log.e("$TAG Failed to interpret phone number [$number] as SIP address") - } + val singleAvailableAddress = LinphoneUtils.getSingleAvailableAddressForFriend(friend) + if (singleAvailableAddress != null) { + Log.i( + "$TAG Only 1 SIP address or phone number found for contact [${friend.name}], using it" + ) + onAddressSelected(singleAvailableAddress, friend) } else { val list = friend.getListOfSipAddressesAndPhoneNumbers(listener) Log.i( diff --git a/app/src/main/java/org/linphone/ui/main/help/viewmodel/HelpViewModel.kt b/app/src/main/java/org/linphone/ui/main/help/viewmodel/HelpViewModel.kt index dd484db7e..2e80f291b 100644 --- a/app/src/main/java/org/linphone/ui/main/help/viewmodel/HelpViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/help/viewmodel/HelpViewModel.kt @@ -28,6 +28,7 @@ import org.linphone.BuildConfig import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.corePreferences import org.linphone.R +import org.linphone.contacts.ContactLoader.Companion.NATIVE_ADDRESS_BOOK_FRIEND_LIST import org.linphone.core.Core import org.linphone.core.CoreListenerStub import org.linphone.core.VersionUpdateCheckResult @@ -197,4 +198,20 @@ class HelpViewModel @UiThread constructor() : GenericViewModel() { } } } + + @UiThread + fun clearNativeFriendsDatabase() { + coreContext.postOnCoreThread { core -> + val list = core.getFriendListByName(NATIVE_ADDRESS_BOOK_FRIEND_LIST) + if (list != null) { + val friends = list.friends + Log.i("$TAG Friend list to remove found with [${friends.size}] friends") + for (friend in friends) { + list.removeFriend(friend) + } + core.removeFriendList(list) + Log.i("$TAG Friend list [$NATIVE_ADDRESS_BOOK_FRIEND_LIST] removed") + } + } + } } diff --git a/app/src/main/java/org/linphone/utils/LinphoneUtils.kt b/app/src/main/java/org/linphone/utils/LinphoneUtils.kt index d066f03fb..25faea4fb 100644 --- a/app/src/main/java/org/linphone/utils/LinphoneUtils.kt +++ b/app/src/main/java/org/linphone/utils/LinphoneUtils.kt @@ -25,6 +25,7 @@ import androidx.annotation.IntegerRes import androidx.annotation.WorkerThread import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.R +import org.linphone.contacts.getListOfSipAddresses import org.linphone.core.Account import org.linphone.core.Address import org.linphone.core.Call @@ -36,9 +37,11 @@ import org.linphone.core.ChatRoom import org.linphone.core.ConferenceInfo import org.linphone.core.Core import org.linphone.core.Factory +import org.linphone.core.Friend import org.linphone.core.Reason import org.linphone.core.tools.Log import org.linphone.ui.main.contacts.model.ContactAvatarModel +import org.linphone.ui.main.model.isEndToEndEncryptionMandatory class LinphoneUtils { companion object { @@ -95,6 +98,33 @@ class LinphoneUtils { return address.displayName ?: address.username ?: address.asString() } + @WorkerThread + fun getSingleAvailableAddressForFriend(friend: Friend): Address? { + val addresses = friend.getListOfSipAddresses() + val addressesCount = addresses.size + val numbersCount = friend.phoneNumbers.size + + // Do not consider phone numbers if default account is in secure mode + val enablePhoneNumbers = !isEndToEndEncryptionMandatory() + + if (addressesCount == 1 && (numbersCount == 0 || !enablePhoneNumbers)) { + val address = addresses.first() + Log.i("$TAG Only 1 SIP address found for contact [${friend.name}], using it") + return address + } else if (addressesCount == 0 && numbersCount == 1 && enablePhoneNumbers) { + val number = friend.phoneNumbers.first() + val address = friend.core.interpretUrl(number, applyInternationalPrefix()) + if (address != null) { + Log.i("$TAG Only 1 phone number found for contact [${friend.name}], using it") + return address + } else { + Log.e("$TAG Failed to interpret phone number [$number] as SIP address") + } + } + + return null + } + @AnyThread fun isCallIncoming(callState: Call.State): Boolean { return when (callState) { diff --git a/app/src/main/res/layout/help_debug_fragment.xml b/app/src/main/res/layout/help_debug_fragment.xml index ccc02a636..f557d4c44 100644 --- a/app/src/main/res/layout/help_debug_fragment.xml +++ b/app/src/main/res/layout/help_debug_fragment.xml @@ -211,6 +211,27 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/sdk_version_subtitle"/> + + Les journaux ont été nettoyés Echec à l\'envoi des journaux Afficher la configuration + Supprimer les contacts natifs importés Paramètres diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 73a4a342f..462c439bd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -206,6 +206,7 @@ Debug logs have been cleaned Failed to upload debug logs Show configuration + Clear imported contacts from native address book Settings