diff --git a/app/src/main/java/org/linphone/contacts/ContactsManager.kt b/app/src/main/java/org/linphone/contacts/ContactsManager.kt index c7d429e9a..c23c66d9a 100644 --- a/app/src/main/java/org/linphone/contacts/ContactsManager.kt +++ b/app/src/main/java/org/linphone/contacts/ContactsManager.kt @@ -90,7 +90,7 @@ class ContactsManager @UiThread constructor() { @WorkerThread override fun onFriendListRemoved(core: Core, friendList: FriendList) { - Log.i("$TAG Friend list [${friendList.displayName}] remoed") + Log.i("$TAG Friend list [${friendList.displayName}] removed") friendList.removeListener(friendListListener) } } diff --git a/app/src/main/java/org/linphone/ui/main/contacts/adapter/ContactsListAdapter.kt b/app/src/main/java/org/linphone/ui/main/contacts/adapter/ContactsListAdapter.kt index 06d3ada00..3399ff6c2 100644 --- a/app/src/main/java/org/linphone/ui/main/contacts/adapter/ContactsListAdapter.kt +++ b/app/src/main/java/org/linphone/ui/main/contacts/adapter/ContactsListAdapter.kt @@ -105,6 +105,17 @@ class ContactsListAdapter( binding.root.isSelected = bindingAdapterPosition == selectedAdapterPosition + val previousItem = bindingAdapterPosition - 1 + val previousLetter = if (previousItem >= 0) { + getItem(previousItem).contactName?.get(0).toString() + } else { + "" + } + + val currentLetter = contactModel.contactName?.get(0).toString() + val displayLetter = previousLetter.isEmpty() || currentLetter != previousLetter + firstContactStartingByThatLetter = displayLetter + executePendingBindings() } } @@ -127,12 +138,11 @@ class ContactsListAdapter( private class ContactDiffCallback : DiffUtil.ItemCallback() { override fun areItemsTheSame(oldItem: ContactAvatarModel, newItem: ContactAvatarModel): Boolean { - return oldItem.id == newItem.id + return oldItem.id == newItem.id && oldItem.contactName == newItem.contactName } override fun areContentsTheSame(oldItem: ContactAvatarModel, newItem: ContactAvatarModel): Boolean { - return oldItem.firstContactStartingByThatLetter.value == newItem.firstContactStartingByThatLetter.value && - oldItem.presenceStatus.value == newItem.presenceStatus.value && + return oldItem.presenceStatus.value == newItem.presenceStatus.value && (newItem.presenceStatus.value == ConsolidatedPresence.Busy || newItem.presenceStatus.value == ConsolidatedPresence.Online) } } diff --git a/app/src/main/java/org/linphone/ui/main/contacts/model/ContactAvatarModel.kt b/app/src/main/java/org/linphone/ui/main/contacts/model/ContactAvatarModel.kt index 382ede692..f43846ec7 100644 --- a/app/src/main/java/org/linphone/ui/main/contacts/model/ContactAvatarModel.kt +++ b/app/src/main/java/org/linphone/ui/main/contacts/model/ContactAvatarModel.kt @@ -42,6 +42,8 @@ class ContactAvatarModel @WorkerThread constructor(val friend: Friend) : Abstrac val id = friend.refKey ?: friend.name + val contactName = friend.name + val starred = friend.starred val lastPresenceInfo = MutableLiveData() @@ -52,8 +54,6 @@ class ContactAvatarModel @WorkerThread constructor(val friend: Friend) : Abstrac val firstLetter: String = AppUtils.getFirstLetter(friend.name.orEmpty()) - val firstContactStartingByThatLetter = MutableLiveData() - private val friendListener = object : FriendListenerStub() { @WorkerThread override fun onPresenceReceived(fr: Friend) { 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 4e9c87874..e76345967 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 @@ -24,6 +24,7 @@ import androidx.annotation.WorkerThread import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import java.io.File +import java.text.Collator import java.util.ArrayList import java.util.Locale import kotlinx.coroutines.launch @@ -110,6 +111,18 @@ class ContactsListViewModel @UiThread constructor() : AbstractTopBarViewModel() super.onCleared() } + @UiThread + override fun filter() { + isListFiltered.value = currentFilter.isNotEmpty() + coreContext.postOnCoreThread { + applyFilter( + currentFilter, + if (limitSearchToLinphoneAccounts) corePreferences.defaultDomain else "", + MagicSearch.Source.Friends.toInt() or MagicSearch.Source.LdapServers.toInt() + ) + } + } + @UiThread fun applyCurrentDefaultAccountFilter() { coreContext.postOnCoreThread { core -> @@ -135,63 +148,6 @@ class ContactsListViewModel @UiThread constructor() : AbstractTopBarViewModel() showFavourites.value = showFavourites.value == false } - @WorkerThread - fun processMagicSearchResults(results: Array) { - Log.i("$TAG Processing [${results.size}] results") - - val list = arrayListOf() - val favouritesList = arrayListOf() - var previousLetter = "" - var count = 0 - - for (result in results) { - val friend = result.friend - - val model = if (friend != null) { - coreContext.contactsManager.getContactAvatarModelForFriend(friend) - } else { - coreContext.contactsManager.getContactAvatarModelForAddress(result.address) - } - - val currentLetter = model.friend.name?.get(0).toString() - val displayLetter = previousLetter.isEmpty() || currentLetter != previousLetter - if (currentLetter != previousLetter) { - previousLetter = currentLetter - } - model.firstContactStartingByThatLetter.postValue(displayLetter) - - list.add(model) - count += 1 - - if (friend?.starred == true) { - favouritesList.add(model) - } - - if (count == 20) { - contactsList.postValue(list) - fetchInProgress.postValue(false) - } - } - - favourites.postValue(favouritesList) - contactsList.postValue(list) - fetchInProgress.postValue(false) - - Log.i("$TAG Processed [${results.size}] results") - } - - @UiThread - override fun filter() { - isListFiltered.value = currentFilter.isNotEmpty() - coreContext.postOnCoreThread { - applyFilter( - currentFilter, - if (limitSearchToLinphoneAccounts) corePreferences.defaultDomain else "", - MagicSearch.Source.Friends.toInt() or MagicSearch.Source.LdapServers.toInt() - ) - } - } - @UiThread fun exportContactAsVCard(friend: Friend) { coreContext.postOnCoreThread { @@ -251,26 +207,47 @@ class ContactsListViewModel @UiThread constructor() : AbstractTopBarViewModel() } @WorkerThread - private fun createFriendFromSearchResult(searchResult: SearchResult): Friend { - val searchResultFriend = searchResult.friend - if (searchResultFriend != null) return searchResultFriend + private fun processMagicSearchResults(results: Array) { + Log.i("$TAG Processing [${results.size}] results") - val friend = coreContext.core.createFriend() + val list = arrayListOf() + val favouritesList = arrayListOf() + var count = 0 - val address = searchResult.address - if (address != null) { - friend.address = address - } + for (result in results) { + val friend = result.friend - val number = searchResult.phoneNumber - if (number != null) { - friend.addPhoneNumber(number) + val model = if (friend != null) { + coreContext.contactsManager.getContactAvatarModelForFriend(friend) + } else { + coreContext.contactsManager.getContactAvatarModelForAddress(result.address) + } - if (address != null && address.username == number) { - friend.removeAddress(address) + list.add(model) + count += 1 + + if (friend?.starred == true) { + favouritesList.add(model) + } + + if (count == 20) { + contactsList.postValue(list) + fetchInProgress.postValue(false) } } - return friend + val collator = Collator.getInstance(Locale.getDefault()) + favouritesList.sortWith { model1, model2 -> + collator.compare(model1.friend.name, model2.friend.name) + } + list.sortWith { model1, model2 -> + collator.compare(model1.friend.name, model2.friend.name) + } + + favourites.postValue(favouritesList) + contactsList.postValue(list) + fetchInProgress.postValue(false) + + Log.i("$TAG Processed [${results.size}] results") } } diff --git a/app/src/main/java/org/linphone/ui/main/history/adapter/ContactsAndSuggestionsListAdapter.kt b/app/src/main/java/org/linphone/ui/main/history/adapter/ContactsAndSuggestionsListAdapter.kt index 47d11c693..e73bfc516 100644 --- a/app/src/main/java/org/linphone/ui/main/history/adapter/ContactsAndSuggestionsListAdapter.kt +++ b/app/src/main/java/org/linphone/ui/main/history/adapter/ContactsAndSuggestionsListAdapter.kt @@ -110,9 +110,22 @@ class ContactsAndSuggestionsListAdapter : fun bind(contactOrSuggestionModel: ContactOrSuggestionModel) { with(binding) { model = contactOrSuggestionModel.avatarModel.value + setOnClickListener { contactClickedEvent.value = Event(contactOrSuggestionModel) } + + val previousItem = bindingAdapterPosition - 1 + val previousLetter = if (previousItem >= 0) { + getItem(previousItem).name[0].toString() + } else { + "" + } + + val currentLetter = contactOrSuggestionModel.name[0].toString() + val displayLetter = previousLetter.isEmpty() || currentLetter != previousLetter + firstContactStartingByThatLetter = displayLetter + executePendingBindings() } } 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 5d7e05d4d..907377bea 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 @@ -23,6 +23,8 @@ import androidx.annotation.UiThread import androidx.annotation.WorkerThread import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import java.text.Collator +import java.util.Locale import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.corePreferences import org.linphone.R @@ -178,67 +180,6 @@ abstract class AddressSelectionViewModel @UiThread constructor() : ViewModel() { } } - @WorkerThread - fun processMagicSearchResults(results: Array) { - Log.i("$TAG Processing [${results.size}] results") - - val contactsList = arrayListOf() - val suggestionsList = arrayListOf() - var previousLetter = "" - - for (result in results) { - val address = result.address - if (address != null) { - val friend = coreContext.contactsManager.findContactByAddress(address) - if (friend != null) { - val model = ContactOrSuggestionModel(address, friend) - val avatarModel = coreContext.contactsManager.getContactAvatarModelForAddress( - address - ) - model.avatarModel.postValue(avatarModel) - - val currentLetter = friend.name?.get(0).toString() - val displayLetter = previousLetter.isEmpty() || currentLetter != previousLetter - if (currentLetter != previousLetter) { - previousLetter = currentLetter - } - avatarModel.firstContactStartingByThatLetter.postValue( - displayLetter - ) - - contactsList.add(model) - } else { - // If user-input generated result (always last) already exists, don't show it again - if (result.sourceFlags == MagicSearch.Source.Request.toInt()) { - val found = suggestionsList.find { - it.address.weakEqual(address) - } - if (found != null) { - Log.i( - "$TAG Result generated from user input is a duplicate of an existing solution, preventing double" - ) - continue - } - } - - val model = ContactOrSuggestionModel(address) { - coreContext.startCall(address) - } - - suggestionsList.add(model) - } - } - } - - val list = arrayListOf() - list.addAll(contactsList) - list.addAll(suggestionsList) - contactsAndSuggestionsList.postValue(list) - Log.i( - "$TAG Processed [${results.size}] results, extracted [${suggestionsList.size}] suggestions" - ) - } - @UiThread fun applyFilter(filter: String) { coreContext.postOnCoreThread { @@ -278,4 +219,63 @@ abstract class AddressSelectionViewModel @UiThread constructor() : ViewModel() { aggregation ) } + + @WorkerThread + private fun processMagicSearchResults(results: Array) { + Log.i("$TAG Processing [${results.size}] results") + + val contactsList = arrayListOf() + val suggestionsList = arrayListOf() + + for (result in results) { + val address = result.address + if (address != null) { + val friend = coreContext.contactsManager.findContactByAddress(address) + if (friend != null) { + val model = ContactOrSuggestionModel(address, friend) + val avatarModel = coreContext.contactsManager.getContactAvatarModelForAddress( + address + ) + model.avatarModel.postValue(avatarModel) + + contactsList.add(model) + } else { + // If user-input generated result (always last) already exists, don't show it again + if (result.sourceFlags == MagicSearch.Source.Request.toInt()) { + val found = suggestionsList.find { + it.address.weakEqual(address) + } + if (found != null) { + Log.i( + "$TAG Result generated from user input is a duplicate of an existing solution, preventing double" + ) + continue + } + } + + val model = ContactOrSuggestionModel(address) { + coreContext.startCall(address) + } + + suggestionsList.add(model) + } + } + } + + val collator = Collator.getInstance(Locale.getDefault()) + contactsList.sortWith { model1, model2 -> + collator.compare(model1.name, model2.name) + } + suggestionsList.sortWith { model1, model2 -> + collator.compare(model1.name, model2.name) + } + + val list = arrayListOf() + list.addAll(contactsList) + list.addAll(suggestionsList) + contactsAndSuggestionsList.postValue(list) + Log.i( + "$TAG Processed [${results.size}] results, extracted [${suggestionsList.size}] suggestions" + ) + } } diff --git a/app/src/main/res/layout/contact_list_cell.xml b/app/src/main/res/layout/contact_list_cell.xml index ea4dd3f43..3f568cb4d 100644 --- a/app/src/main/res/layout/contact_list_cell.xml +++ b/app/src/main/res/layout/contact_list_cell.xml @@ -17,6 +17,9 @@ +