mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-26 16:28:08 +00:00
Fixed duplicated phone numbers in loaded contacts from native addressbook + improved code by factorizing some parts of it
This commit is contained in:
parent
2821d4b72f
commit
ab1ea3392d
11 changed files with 133 additions and 143 deletions
|
|
@ -215,9 +215,14 @@ class ContactLoader : LoaderManager.LoaderCallbacks<Cursor> {
|
|||
}
|
||||
|
||||
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 -> {
|
||||
|
|
|
|||
|
|
@ -730,22 +730,33 @@ fun Friend.getPerson(): Person {
|
|||
return personBuilder.build()
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun Friend.getListOfSipAddresses(): ArrayList<Address> {
|
||||
val addressesList = arrayListOf<Address>()
|
||||
|
||||
for (address in addresses) {
|
||||
if (addressesList.find { it.weakEqual(address) } == null) {
|
||||
addressesList.add(address)
|
||||
}
|
||||
}
|
||||
|
||||
return addressesList
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun Friend.getListOfSipAddressesAndPhoneNumbers(listener: ContactNumberOrAddressClickListener): ArrayList<ContactNumberOrAddressModel> {
|
||||
val addressesAndNumbers = arrayListOf<ContactNumberOrAddressModel>()
|
||||
|
||||
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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -211,6 +211,27 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/sdk_version_subtitle"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/settings_title_style"
|
||||
android:id="@+id/change_ringtone"
|
||||
android:onClick="@{() -> viewModel.clearNativeFriendsDatabase()}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="5dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="@dimen/screen_bottom_margin"
|
||||
android:text="@string/help_troubleshooting_clear_native_friends_in_database"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
android:drawableEnd="@drawable/trash_simple"
|
||||
android:drawableTint="?attr/color_main2_600"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/show_config_file"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<include
|
||||
|
|
|
|||
|
|
@ -169,6 +169,7 @@
|
|||
<string name="help_troubleshooting_debug_logs_cleaned_toast_message">Les journaux ont été nettoyés</string>
|
||||
<string name="help_troubleshooting_debug_logs_upload_error_toast_message">Echec à l\'envoi des journaux</string>
|
||||
<string name="help_troubleshooting_show_config_file">Afficher la configuration</string>
|
||||
<string name="help_troubleshooting_clear_native_friends_in_database">Supprimer les contacts natifs importés</string>
|
||||
|
||||
<!-- App & SDK settings -->
|
||||
<string name="settings_title">Paramètres</string>
|
||||
|
|
|
|||
|
|
@ -206,6 +206,7 @@
|
|||
<string name="help_troubleshooting_debug_logs_cleaned_toast_message">Debug logs have been cleaned</string>
|
||||
<string name="help_troubleshooting_debug_logs_upload_error_toast_message">Failed to upload debug logs</string>
|
||||
<string name="help_troubleshooting_show_config_file">Show configuration</string>
|
||||
<string name="help_troubleshooting_clear_native_friends_in_database">Clear imported contacts from native address book</string>
|
||||
|
||||
<!-- App & SDK settings -->
|
||||
<string name="settings_title">Settings</string>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue