mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-17 19:38:08 +00:00
Reworked contacts list to use search results from Magic Search instead of directly fetched contacts from native addressbook
This commit is contained in:
parent
69fdff6858
commit
7894311cdd
10 changed files with 111 additions and 29 deletions
|
|
@ -10,7 +10,13 @@ Group changes to describe their impact on the project, as follows:
|
|||
Fixed for any bug fixes.
|
||||
Security to invite users to upgrade in case of vulnerabilities.
|
||||
|
||||
## [4.7.0] - Unreleased
|
||||
## [4.6.2] - Unreleased
|
||||
|
||||
### Added
|
||||
- LDAP settings if SDK is built with OpenLDAP (requires 5.1.1 or higher linphone-sdk), will add contacts if any
|
||||
|
||||
### Changed
|
||||
- Contacts lists now show LDAP contacts if any, as well as "generated" contacts from SIP addresses you have interacted with
|
||||
|
||||
## [4.6.1] - 2022-02-14
|
||||
|
||||
|
|
|
|||
|
|
@ -173,6 +173,14 @@ class MasterContactsFragment : MasterFragment<ContactMasterFragmentBinding, Cont
|
|||
val viewModel = DialogViewModel(getString(R.string.contact_delete_one_dialog))
|
||||
val dialog: Dialog = DialogUtils.getDialog(requireContext(), viewModel)
|
||||
|
||||
val contactViewModel = adapter.currentList[viewHolder.bindingAdapterPosition]
|
||||
if (contactViewModel.isNativeContact.value == false) {
|
||||
adapter.notifyItemChanged(viewHolder.bindingAdapterPosition)
|
||||
val activity = requireActivity() as MainActivity
|
||||
activity.showSnackBar(R.string.contact_cant_be_deleted)
|
||||
return
|
||||
}
|
||||
|
||||
viewModel.showCancelButton {
|
||||
adapter.notifyItemChanged(viewHolder.bindingAdapterPosition)
|
||||
dialog.dismiss()
|
||||
|
|
@ -212,6 +220,7 @@ class MasterContactsFragment : MasterFragment<ContactMasterFragmentBinding, Cont
|
|||
it.consume { contact ->
|
||||
Log.i("[Contacts] Selected item in list changed: $contact")
|
||||
sharedViewModel.selectedContact.value = contact
|
||||
(requireActivity() as MainActivity).hideKeyboard()
|
||||
|
||||
if (editOnClick) {
|
||||
navigateToContactEditor(sipUriToAdd, binding.slidingPane)
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@ class ContactViewModel(val contactInternal: Contact) : ErrorReportingViewModel()
|
|||
|
||||
val waitForChatRoomCreation = MutableLiveData<Boolean>()
|
||||
|
||||
val isNativeContact = MutableLiveData<Boolean>()
|
||||
|
||||
private val contactsUpdatedListener = object : ContactsUpdatedListenerStub() {
|
||||
override fun onContactUpdated(contact: Contact) {
|
||||
if (contact is NativeContact && contactInternal is NativeContact && contact.nativeId == contactInternal.nativeId) {
|
||||
|
|
@ -127,6 +129,7 @@ class ContactViewModel(val contactInternal: Contact) : ErrorReportingViewModel()
|
|||
init {
|
||||
contact.value = contactInternal
|
||||
displayName.value = contactInternal.fullName ?: contactInternal.firstName + " " + contactInternal.lastName
|
||||
isNativeContact.value = contactInternal is NativeContact
|
||||
|
||||
updateNumbersAndAddresses(contactInternal)
|
||||
coreContext.contactsManager.addListener(contactsUpdatedListener)
|
||||
|
|
@ -172,7 +175,7 @@ class ContactViewModel(val contactInternal: Contact) : ErrorReportingViewModel()
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateNumbersAndAddresses(contact: Contact) {
|
||||
fun updateNumbersAndAddresses(contact: Contact) {
|
||||
val list = arrayListOf<ContactNumberOrAddressData>()
|
||||
for (address in contact.sipAddresses) {
|
||||
val value = address.asStringUriOnly()
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import org.linphone.LinphoneApplication.Companion.coreContext
|
|||
import org.linphone.contact.Contact
|
||||
import org.linphone.contact.ContactsUpdatedListenerStub
|
||||
import org.linphone.contact.NativeContact
|
||||
import org.linphone.core.SearchResult
|
||||
import org.linphone.core.tools.Log
|
||||
|
||||
class ContactsListViewModel : ViewModel() {
|
||||
|
|
@ -36,6 +37,7 @@ class ContactsListViewModel : ViewModel() {
|
|||
val contactsList = MutableLiveData<ArrayList<ContactViewModel>>()
|
||||
|
||||
val filter = MutableLiveData<String>()
|
||||
private var previousFilter = "NotSet"
|
||||
|
||||
private val contactsUpdatedListener = object : ContactsUpdatedListenerStub() {
|
||||
override fun onContactsUpdated() {
|
||||
|
|
@ -57,27 +59,41 @@ class ContactsListViewModel : ViewModel() {
|
|||
super.onCleared()
|
||||
}
|
||||
|
||||
private fun getSelectedContactsList(): ArrayList<ContactViewModel> {
|
||||
val list = arrayListOf<ContactViewModel>()
|
||||
val source =
|
||||
if (sipContactsSelected.value == true) coreContext.contactsManager.sipContacts
|
||||
else coreContext.contactsManager.contacts
|
||||
for (contact in source) {
|
||||
list.add(ContactViewModel(contact))
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
fun updateContactsList() {
|
||||
val list: ArrayList<ContactViewModel>
|
||||
|
||||
val filterValue = filter.value.orEmpty()
|
||||
list = if (filterValue.isNotEmpty()) {
|
||||
getSelectedContactsList().filter { contact ->
|
||||
contact.name.contains(filterValue, true)
|
||||
} as ArrayList<ContactViewModel>
|
||||
} else {
|
||||
getSelectedContactsList()
|
||||
contactsList.value.orEmpty().forEach(ContactViewModel::destroy)
|
||||
|
||||
if (previousFilter.isNotEmpty() && previousFilter.length > filterValue.length) {
|
||||
coreContext.contactsManager.magicSearch.resetSearchCache()
|
||||
}
|
||||
previousFilter = filterValue
|
||||
|
||||
val domain = if (sipContactsSelected.value == true) coreContext.core.defaultAccount?.params?.domain ?: "" else ""
|
||||
val results = coreContext.contactsManager.magicSearch.getContactListFromFilter(filterValue, domain)
|
||||
|
||||
val list = arrayListOf<ContactViewModel>()
|
||||
for (result in results) {
|
||||
val contact = searchMatchingContact(result) ?: Contact(searchResult = result)
|
||||
if (contact is NativeContact) {
|
||||
val found = list.find { contactViewModel ->
|
||||
contactViewModel.contactInternal is NativeContact && contactViewModel.contactInternal.nativeId == contact.nativeId
|
||||
}
|
||||
if (found != null) {
|
||||
Log.d("[Contacts] Found a search result that matches a native contact [$contact] we already have, skipping")
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
val found = list.find { contactViewModel ->
|
||||
contactViewModel.displayName.value == contact.fullName
|
||||
}
|
||||
if (found != null) {
|
||||
Log.i("[Contacts] Found a search result that matches a contact [$contact] we already have, updating it with the new information")
|
||||
found.contactInternal.addAddressAndPhoneNumberFromSearchResult(result)
|
||||
found.updateNumbersAndAddresses(found.contactInternal)
|
||||
continue
|
||||
}
|
||||
}
|
||||
list.add(ContactViewModel(contact))
|
||||
}
|
||||
|
||||
contactsList.postValue(list)
|
||||
|
|
@ -146,4 +162,19 @@ class ContactsListViewModel : ViewModel() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun searchMatchingContact(searchResult: SearchResult): Contact? {
|
||||
val address = searchResult.address
|
||||
|
||||
if (address != null) {
|
||||
val contact = coreContext.contactsManager.findContactByAddress(address, ignoreLocalContact = true)
|
||||
if (contact != null) return contact
|
||||
}
|
||||
|
||||
if (searchResult.phoneNumber != null) {
|
||||
return coreContext.contactsManager.findContactByPhoneNumber(searchResult.phoneNumber.orEmpty())
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,8 +29,10 @@ import org.linphone.R
|
|||
import org.linphone.core.Address
|
||||
import org.linphone.core.Friend
|
||||
import org.linphone.core.PresenceBasicStatus
|
||||
import org.linphone.core.SearchResult
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.utils.ImageUtils
|
||||
import org.linphone.utils.LinphoneUtils
|
||||
|
||||
data class PhoneNumber(val value: String, val typeLabel: String) : Comparable<PhoneNumber> {
|
||||
override fun compareTo(other: PhoneNumber): Int {
|
||||
|
|
@ -38,7 +40,7 @@ data class PhoneNumber(val value: String, val typeLabel: String) : Comparable<Ph
|
|||
}
|
||||
}
|
||||
|
||||
open class Contact : Comparable<Contact> {
|
||||
open class Contact() : Comparable<Contact> {
|
||||
var fullName: String? = null
|
||||
var firstName: String? = null
|
||||
var lastName: String? = null
|
||||
|
|
@ -55,6 +57,31 @@ open class Contact : Comparable<Contact> {
|
|||
|
||||
private var thumbnailUri: Uri? = null
|
||||
|
||||
constructor(searchResult: SearchResult) : this() {
|
||||
friend = searchResult.friend
|
||||
addAddressAndPhoneNumberFromSearchResult(searchResult)
|
||||
}
|
||||
|
||||
fun addAddressAndPhoneNumberFromSearchResult(searchResult: SearchResult) {
|
||||
val address = searchResult.address
|
||||
if (address != null) {
|
||||
if (fullName == null) {
|
||||
fullName = friend?.name ?: LinphoneUtils.getDisplayName(address)
|
||||
}
|
||||
|
||||
sipAddresses.add(address)
|
||||
}
|
||||
|
||||
val phoneNumber = searchResult.phoneNumber
|
||||
if (phoneNumber != null) {
|
||||
if (address == null && fullName == null) {
|
||||
fullName = friend?.name ?: phoneNumber.orEmpty()
|
||||
}
|
||||
|
||||
phoneNumbers.add(PhoneNumber(phoneNumber, ""))
|
||||
}
|
||||
}
|
||||
|
||||
override fun compareTo(other: Contact): Int {
|
||||
val fn = fullName ?: ""
|
||||
val otherFn = other.fullName ?: ""
|
||||
|
|
|
|||
|
|
@ -236,13 +236,15 @@ class ContactsManager(private val context: Context) {
|
|||
}
|
||||
|
||||
@Synchronized
|
||||
fun findContactByAddress(address: Address): Contact? {
|
||||
val localContact = localAccountsContacts.find { localContact ->
|
||||
localContact.sipAddresses.find { localAddress ->
|
||||
address.weakEqual(localAddress)
|
||||
} != null
|
||||
fun findContactByAddress(address: Address, ignoreLocalContact: Boolean = false): Contact? {
|
||||
if (!ignoreLocalContact) {
|
||||
val localContact = localAccountsContacts.find { localContact ->
|
||||
localContact.sipAddresses.find { localAddress ->
|
||||
address.weakEqual(localAddress)
|
||||
} != null
|
||||
}
|
||||
if (localContact != null) return localContact
|
||||
}
|
||||
if (localContact != null) return localContact
|
||||
|
||||
val cleanAddress = address.clone()
|
||||
cleanAddress.clean() // To remove gruu if any
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@
|
|||
|
||||
<ImageView
|
||||
android:onClick="@{editClickListener}"
|
||||
android:visibility="@{viewModel.isNativeContact ? View.VISIBLE : View.INVISIBLE}"
|
||||
android:contentDescription="@string/content_description_edit_contact"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
|
|
@ -62,6 +63,7 @@
|
|||
|
||||
<ImageView
|
||||
android:onClick="@{deleteClickListener}"
|
||||
android:visibility="@{viewModel.isNativeContact ? View.VISIBLE : View.INVISIBLE}"
|
||||
android:contentDescription="@string/content_description_delete_contact"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@
|
|||
|
||||
<CheckBox
|
||||
android:onClick="@{() -> selectionListViewModel.onToggleSelect(position)}"
|
||||
android:visibility="@{selectionListViewModel.isEditionEnabled ? View.VISIBLE : View.GONE, default=gone}"
|
||||
android:visibility="@{selectionListViewModel.isEditionEnabled && viewModel.isNativeContact ? View.VISIBLE : View.GONE, default=gone}"
|
||||
android:checked="@{selectionListViewModel.selectedItems.contains(position)}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
|
|
|
|||
|
|
@ -652,4 +652,5 @@
|
|||
<string name="contacts_settings_ldap_sip_domain_title">Domaine</string>
|
||||
<string name="contacts_settings_ldap_misc_title">Divers</string>
|
||||
<string name="contacts_settings_ldap_debug_title">Débogage</string>
|
||||
<string name="contact_cant_be_deleted">Ce contact ne peut être supprimé</string>
|
||||
</resources>
|
||||
|
|
@ -114,6 +114,7 @@
|
|||
</plurals>
|
||||
<string name="contact_new_choose_sync_account">Choose where to save the contact</string>
|
||||
<string name="contact_local_sync_account">Store locally</string>
|
||||
<string name="contact_cant_be_deleted">This contact can\'t be deleted</string>
|
||||
|
||||
<!-- Dialer -->
|
||||
<string name="dialer_address_bar_hint">Enter a number or an address</string>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue