mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-17 11:28:06 +00:00
Fixed issues related to contacts being added/edited/removed
This commit is contained in:
parent
3864d54936
commit
523b762cac
9 changed files with 115 additions and 43 deletions
|
|
@ -89,17 +89,7 @@ class ContactsManager @UiThread constructor() {
|
|||
Log.d(
|
||||
"$TAG Newly discovered SIP Address [$sipUri] for friend [${friend.name}] in list [${friendList.displayName}]"
|
||||
)
|
||||
|
||||
if (unknownContactsAvatarsMap.keys.contains(sipUri)) {
|
||||
Log.d("$TAG Found SIP Address in unknownContactsAvatarsMap, removing it")
|
||||
val oldModel = unknownContactsAvatarsMap[sipUri]
|
||||
oldModel?.destroy()
|
||||
unknownContactsAvatarsMap.remove(sipUri)
|
||||
} else if (knownContactsAvatarsMap.keys.contains(sipUri)) {
|
||||
Log.d("$TAG Found SIP Address in knownContactsAvatarsMap, forcing presence update")
|
||||
val oldModel = knownContactsAvatarsMap[sipUri]
|
||||
oldModel?.updatePresence()
|
||||
}
|
||||
newContactAddedWithSipUri(sipUri)
|
||||
|
||||
reloadContactsJob = coroutineScope.launch {
|
||||
delay(DELAY_BEFORE_RELOADING_CONTACTS_AFTER_PRESENCE_RECEIVED)
|
||||
|
|
@ -162,6 +152,49 @@ class ContactsManager @UiThread constructor() {
|
|||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun newContactAddedWithSipUri(sipUri: String) {
|
||||
if (unknownContactsAvatarsMap.keys.contains(sipUri)) {
|
||||
Log.d("$TAG Found SIP URI [$sipUri] in unknownContactsAvatarsMap, removing it")
|
||||
val oldModel = unknownContactsAvatarsMap[sipUri]
|
||||
oldModel?.destroy()
|
||||
unknownContactsAvatarsMap.remove(sipUri)
|
||||
} else if (knownContactsAvatarsMap.keys.contains(sipUri)) {
|
||||
Log.d(
|
||||
"$TAG Found SIP URI [$sipUri] in knownContactsAvatarsMap, forcing presence update"
|
||||
)
|
||||
val oldModel = knownContactsAvatarsMap[sipUri]
|
||||
oldModel?.update()
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun newContactAdded(friend: Friend) {
|
||||
for (sipAddress in friend.addresses) {
|
||||
newContactAddedWithSipUri(sipAddress.asStringUriOnly())
|
||||
}
|
||||
|
||||
conferenceAvatarMap.values.forEach(ContactAvatarModel::destroy)
|
||||
conferenceAvatarMap.clear()
|
||||
coreContext.contactsManager.notifyContactsListChanged()
|
||||
}
|
||||
|
||||
fun contactRemoved(friend: Friend) {
|
||||
for (sipAddress in friend.addresses) {
|
||||
val sipUri = sipAddress.asStringUriOnly()
|
||||
if (knownContactsAvatarsMap.keys.contains(sipUri)) {
|
||||
Log.d("$TAG Found SIP URI [$sipUri] in knownContactsAvatarsMap, removing it")
|
||||
val oldModel = knownContactsAvatarsMap[sipUri]
|
||||
oldModel?.destroy()
|
||||
knownContactsAvatarsMap.remove(sipUri)
|
||||
}
|
||||
}
|
||||
|
||||
conferenceAvatarMap.values.forEach(ContactAvatarModel::destroy)
|
||||
conferenceAvatarMap.clear()
|
||||
coreContext.contactsManager.notifyContactsListChanged()
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun onNativeContactsLoaded() {
|
||||
nativeContactsLoaded = true
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import androidx.recyclerview.widget.DiffUtil
|
|||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.linphone.R
|
||||
import org.linphone.core.ConsolidatedPresence
|
||||
import org.linphone.databinding.ContactFavouriteListCellBinding
|
||||
import org.linphone.databinding.ContactListCellBinding
|
||||
import org.linphone.ui.main.contacts.model.ContactAvatarModel
|
||||
|
|
@ -138,13 +137,11 @@ class ContactsListAdapter(
|
|||
|
||||
private class ContactDiffCallback : DiffUtil.ItemCallback<ContactAvatarModel>() {
|
||||
override fun areItemsTheSame(oldItem: ContactAvatarModel, newItem: ContactAvatarModel): Boolean {
|
||||
return oldItem.id == newItem.id && oldItem.contactName == newItem.contactName
|
||||
return oldItem.id == newItem.id
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: ContactAvatarModel, newItem: ContactAvatarModel): Boolean {
|
||||
return oldItem.presenceStatus.value == newItem.presenceStatus.value &&
|
||||
oldItem.isFavourite.value == newItem.isFavourite.value &&
|
||||
(newItem.presenceStatus.value == ConsolidatedPresence.Busy || newItem.presenceStatus.value == ConsolidatedPresence.Online)
|
||||
return false // oldItem & newItem are always the same because fetched from cache, so return false to force refresh
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -230,6 +230,7 @@ class ContactsListFragment : AbstractTopBarFragment() {
|
|||
{ // onDelete
|
||||
coreContext.postOnCoreThread {
|
||||
Log.w("$TAG Removing friend [${model.name.value}]")
|
||||
coreContext.contactsManager.contactRemoved(model.friend)
|
||||
model.friend.remove()
|
||||
coreContext.contactsManager.notifyContactsListChanged()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,11 +71,18 @@ class EditContactFragment : SlidingPaneChildFragment() {
|
|||
private val pickMedia = registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
|
||||
if (uri != null) {
|
||||
Log.i("$TAG Picture picked [$uri]")
|
||||
// TODO FIXME: use a better file name
|
||||
val localFileName = FileUtils.getFileStoragePath("temp.jpg", true)
|
||||
val localFileName = FileUtils.getFileStoragePath(
|
||||
viewModel.getPictureFileName(),
|
||||
true,
|
||||
overrideExisting = true
|
||||
)
|
||||
lifecycleScope.launch {
|
||||
if (FileUtils.copyFile(uri, localFileName)) {
|
||||
viewModel.picturePath.postValue(localFileName.absolutePath)
|
||||
val newPath = FileUtils.getProperFilePath(
|
||||
localFileName.absolutePath
|
||||
)
|
||||
Log.i("$TAG Copied file [$uri] to [$newPath]")
|
||||
viewModel.picturePath.value = newPath
|
||||
} else {
|
||||
Log.e(
|
||||
"$TAG Failed to copy file from [$uri] to [${localFileName.absolutePath}]"
|
||||
|
|
|
|||
|
|
@ -67,11 +67,16 @@ class NewContactFragment : GenericFragment() {
|
|||
private val pickMedia = registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
|
||||
if (uri != null) {
|
||||
Log.i("$TAG Picture picked [$uri]")
|
||||
// TODO FIXME: use a better file name
|
||||
val localFileName = FileUtils.getFileStoragePath("temp.jpg", true)
|
||||
val localFileName = FileUtils.getFileStorageCacheDir(
|
||||
ContactNewOrEditViewModel.TEMP_PICTURE_NAME
|
||||
)
|
||||
lifecycleScope.launch {
|
||||
if (FileUtils.copyFile(uri, localFileName)) {
|
||||
viewModel.picturePath.postValue(localFileName.absolutePath)
|
||||
val newPath = FileUtils.getProperFilePath(
|
||||
localFileName.absolutePath
|
||||
)
|
||||
Log.i("$TAG Copied file [$uri] to [$newPath]")
|
||||
viewModel.picturePath.value = newPath
|
||||
} else {
|
||||
Log.e(
|
||||
"$TAG Failed to copy file from [$uri] to [${localFileName.absolutePath}]"
|
||||
|
|
|
|||
|
|
@ -69,14 +69,7 @@ class ContactAvatarModel @WorkerThread constructor(val friend: Friend) : Abstrac
|
|||
friend.addListener(friendListener)
|
||||
}
|
||||
|
||||
isFavourite.postValue(friend.starred)
|
||||
initials.postValue(AppUtils.getInitials(friend.name.orEmpty()))
|
||||
trust.postValue(SecurityLevel.Encrypted) // TODO FIXME: use API
|
||||
showTrust.postValue(coreContext.core.defaultAccount?.isInSecureMode())
|
||||
images.postValue(arrayListOf(getAvatarUri(friend).toString()))
|
||||
|
||||
name.postValue(friend.name)
|
||||
computePresence()
|
||||
update()
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
|
|
@ -87,8 +80,14 @@ class ContactAvatarModel @WorkerThread constructor(val friend: Friend) : Abstrac
|
|||
}
|
||||
|
||||
@WorkerThread
|
||||
fun updatePresence() {
|
||||
Log.i("$TAG Force update presence information for friend [${friend.name}]")
|
||||
fun update() {
|
||||
isFavourite.postValue(friend.starred)
|
||||
initials.postValue(AppUtils.getInitials(friend.name.orEmpty()))
|
||||
trust.postValue(SecurityLevel.Encrypted) // TODO FIXME: use API
|
||||
showTrust.postValue(coreContext.core.defaultAccount?.isInSecureMode())
|
||||
images.postValue(arrayListOf(getAvatarUri(friend).toString()))
|
||||
|
||||
name.postValue(friend.name)
|
||||
computePresence()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,10 +19,14 @@
|
|||
*/
|
||||
package org.linphone.ui.main.contacts.viewmodel
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.annotation.AnyThread
|
||||
import androidx.annotation.UiThread
|
||||
import androidx.annotation.WorkerThread
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.contacts.ContactLoader.Companion.LINPHONE_ADDRESS_BOOK_FRIEND_LIST
|
||||
import org.linphone.core.Friend
|
||||
|
|
@ -36,10 +40,14 @@ import org.linphone.utils.FileUtils
|
|||
class ContactNewOrEditViewModel @UiThread constructor() : ViewModel() {
|
||||
companion object {
|
||||
private const val TAG = "[Contact New/Edit View Model]"
|
||||
|
||||
const val TEMP_PICTURE_NAME = "new_contact_temp_picture.jpg"
|
||||
}
|
||||
|
||||
private lateinit var friend: Friend
|
||||
|
||||
val id = MutableLiveData<String>()
|
||||
|
||||
val isEdit = MutableLiveData<Boolean>()
|
||||
|
||||
val picturePath = MutableLiveData<String>()
|
||||
|
|
@ -89,6 +97,8 @@ class ContactNewOrEditViewModel @UiThread constructor() : ViewModel() {
|
|||
// TODO ? What to do when vCard is null
|
||||
}
|
||||
|
||||
id.postValue(friend.refKey ?: friend.vcard?.uid)
|
||||
|
||||
val photo = friend.photo.orEmpty()
|
||||
if (photo.isNotEmpty()) {
|
||||
picturePath.postValue(photo)
|
||||
|
|
@ -114,6 +124,12 @@ class ContactNewOrEditViewModel @UiThread constructor() : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
fun getPictureFileName(): String {
|
||||
val name = id.value?.replace(" ", "_") ?: "${firstName.value.orEmpty().trim()}_${lastName.value.orEmpty().trim()}"
|
||||
return "$name.jpg"
|
||||
}
|
||||
|
||||
@UiThread
|
||||
fun saveChanges() {
|
||||
var check = true
|
||||
|
|
@ -140,9 +156,9 @@ class ContactNewOrEditViewModel @UiThread constructor() : ViewModel() {
|
|||
}
|
||||
val fn = firstName.value.orEmpty().trim()
|
||||
val ln = lastName.value.orEmpty().trim()
|
||||
friend.name = "$fn $ln"
|
||||
|
||||
friend.edit()
|
||||
friend.name = "$fn $ln"
|
||||
|
||||
val vCard = friend.vcard
|
||||
if (vCard != null) {
|
||||
|
|
@ -151,7 +167,22 @@ class ContactNewOrEditViewModel @UiThread constructor() : ViewModel() {
|
|||
|
||||
val picture = picturePath.value.orEmpty()
|
||||
if (picture.isNotEmpty()) {
|
||||
friend.photo = FileUtils.getProperFilePath(picture)
|
||||
if (picture.contains(TEMP_PICTURE_NAME)) {
|
||||
val newFile = FileUtils.getFileStoragePath(
|
||||
getPictureFileName(),
|
||||
true,
|
||||
overrideExisting = true
|
||||
)
|
||||
val oldFile = Uri.parse(FileUtils.getProperFilePath(picture))
|
||||
viewModelScope.launch {
|
||||
FileUtils.copyFile(oldFile, newFile)
|
||||
}
|
||||
val newPicture = FileUtils.getProperFilePath(newFile.absolutePath)
|
||||
Log.i("$TAG Temporary picture [$picture] copied to [$newPicture]")
|
||||
friend.photo = newPicture
|
||||
} else {
|
||||
friend.photo = FileUtils.getProperFilePath(picture)
|
||||
}
|
||||
} else {
|
||||
friend.photo = null
|
||||
}
|
||||
|
|
@ -215,7 +246,7 @@ class ContactNewOrEditViewModel @UiThread constructor() : ViewModel() {
|
|||
friend.done()
|
||||
}
|
||||
|
||||
coreContext.contactsManager.notifyContactsListChanged()
|
||||
coreContext.contactsManager.newContactAdded(friend)
|
||||
|
||||
saveChangesEvent.postValue(
|
||||
Event(if (status == Status.OK) friend.refKey.orEmpty() else "")
|
||||
|
|
|
|||
|
|
@ -354,6 +354,7 @@ class ContactViewModel @UiThread constructor() : ViewModel() {
|
|||
coreContext.postOnCoreThread {
|
||||
if (::friend.isInitialized) {
|
||||
Log.w("$TAG Deleting friend [$friend]")
|
||||
coreContext.contactsManager.contactRemoved(friend)
|
||||
friend.remove()
|
||||
coreContext.contactsManager.notifyContactsListChanged()
|
||||
contactRemovedEvent.postValue(Event(true))
|
||||
|
|
|
|||
|
|
@ -79,10 +79,10 @@ class ContactsListViewModel @UiThread constructor() : AbstractTopBarViewModel()
|
|||
override fun onContactsLoaded() {
|
||||
Log.i("$TAG Contacts have been (re)loaded, updating list")
|
||||
magicSearch.resetSearchCache()
|
||||
|
||||
applyFilter(
|
||||
currentFilter,
|
||||
if (limitSearchToLinphoneAccounts) corePreferences.defaultDomain else "",
|
||||
MagicSearch.Source.Friends.toInt() or MagicSearch.Source.LdapServers.toInt()
|
||||
if (limitSearchToLinphoneAccounts) corePreferences.defaultDomain else ""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -118,8 +118,7 @@ class ContactsListViewModel @UiThread constructor() : AbstractTopBarViewModel()
|
|||
coreContext.postOnCoreThread {
|
||||
applyFilter(
|
||||
currentFilter,
|
||||
if (limitSearchToLinphoneAccounts) corePreferences.defaultDomain else "",
|
||||
MagicSearch.Source.Friends.toInt() or MagicSearch.Source.LdapServers.toInt()
|
||||
if (limitSearchToLinphoneAccounts) corePreferences.defaultDomain else ""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -179,8 +178,7 @@ class ContactsListViewModel @UiThread constructor() : AbstractTopBarViewModel()
|
|||
@WorkerThread
|
||||
private fun applyFilter(
|
||||
filter: String,
|
||||
domain: String,
|
||||
sources: Int
|
||||
domain: String
|
||||
) {
|
||||
if (contactsList.value.orEmpty().isEmpty()) {
|
||||
fetchInProgress.postValue(true)
|
||||
|
|
@ -197,12 +195,12 @@ class ContactsListViewModel @UiThread constructor() : AbstractTopBarViewModel()
|
|||
previousFilter = filter
|
||||
|
||||
Log.i(
|
||||
"$TAG Asking Magic search for contacts matching filter [$filter], domain [$domain] and in sources [$sources]"
|
||||
"$TAG Asking Magic search for contacts matching filter [$filter], domain [$domain] and in sources Friends/LDAP"
|
||||
)
|
||||
magicSearch.getContactsListAsync(
|
||||
filter,
|
||||
domain,
|
||||
sources,
|
||||
MagicSearch.Source.Friends.toInt() or MagicSearch.Source.LdapServers.toInt(),
|
||||
MagicSearch.Aggregation.Friend
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue