From 213e62d12533c8f4c405f6db3c0d6d7b168603fb Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Thu, 21 Nov 2024 11:54:10 +0100 Subject: [PATCH] Added remote contact directory lookup if friend isn't found locally --- .../org/linphone/contacts/ContactsManager.kt | 123 +++++++++++--- .../notifications/NotificationsManager.kt | 157 ++++++++++++++++-- .../chat/fragment/ConversationInfoFragment.kt | 6 +- .../ui/main/chat/model/ParticipantModel.kt | 11 +- .../viewmodel/ConversationInfoViewModel.kt | 13 +- .../chat/viewmodel/ConversationViewModel.kt | 3 + .../viewmodel/ConversationsListViewModel.kt | 4 + .../contacts/viewmodel/ContactViewModel.kt | 3 + .../viewmodel/ContactsListViewModel.kt | 25 ++- .../ui/main/history/model/CallLogModel.kt | 2 +- .../history/viewmodel/HistoryListViewModel.kt | 4 + .../viewmodel/AddressSelectionViewModel.kt | 4 + .../main/res/layout/chat_info_fragment.xml | 4 +- 13 files changed, 308 insertions(+), 51 deletions(-) diff --git a/app/src/main/java/org/linphone/contacts/ContactsManager.kt b/app/src/main/java/org/linphone/contacts/ContactsManager.kt index 65c8521ef..cf926707a 100644 --- a/app/src/main/java/org/linphone/contacts/ContactsManager.kt +++ b/app/src/main/java/org/linphone/contacts/ContactsManager.kt @@ -52,6 +52,8 @@ import org.linphone.core.Factory import org.linphone.core.Friend import org.linphone.core.FriendList import org.linphone.core.FriendListListenerStub +import org.linphone.core.MagicSearch +import org.linphone.core.MagicSearchListenerStub import org.linphone.core.SecurityLevel import org.linphone.core.tools.Log import org.linphone.ui.main.MainActivity @@ -70,7 +72,8 @@ class ContactsManager @UiThread constructor() { private const val TAG = "[Contacts Manager]" private const val DELAY_BEFORE_RELOADING_CONTACTS_AFTER_PRESENCE_RECEIVED = 1000L // 1 second - private const val FRIEND_LIST_TEMPORARY_STORED = "TempNativeContacts" + private const val FRIEND_LIST_TEMPORARY_STORED_NATIVE = "TempNativeContacts" + private const val FRIEND_LIST_TEMPORARY_STORED_REMOTE_DIRECTORY = "TempRemoteDirectoryContacts" } private var nativeContactsLoaded = false @@ -88,6 +91,36 @@ class ContactsManager @UiThread constructor() { private var loadContactsOnlyFromDefaultDirectory = true + private lateinit var magicSearch: MagicSearch + + private val magicSearchListener = object : MagicSearchListenerStub() { + @WorkerThread + override fun onSearchResultsReceived(magicSearch: MagicSearch) { + val results = magicSearch.lastSearch + Log.i("$TAG [${results.size}] magic search results available") + + if (results.isNotEmpty()) { + val result = results.first() { + it.friend != null + } + if (result != null) { + val friend = result.friend!! + Log.i("$TAG Found matching friend in source [${result.sourceFlags}]") + + // Store friend in app's cache to be re-used in call history, conversations, etc... + val temporaryFriendList = getTemporaryFriendList(native = false) + temporaryFriendList.addFriend(friend) + newContactAdded(friend) + Log.i("$TAG Stored discovered friend in temporary friend list, for later use") + + for (listener in listeners) { + listener.onContactFoundInRemoteDirectory(friend) + } + } + } + } + } + private val friendListListener: FriendListListenerStub = object : FriendListListenerStub() { @WorkerThread override fun onNewSipAddressDiscovered( @@ -106,7 +139,7 @@ class ContactsManager @UiThread constructor() { friend.addAddress(address) friend.done() - newContactAddedWithSipUri(sipUri) + newContactAddedWithSipUri(friend, sipUri) } else { Log.e("$TAG Failed to parse SIP URI [$sipUri] as Address!") } @@ -182,7 +215,7 @@ class ContactsManager @UiThread constructor() { } @WorkerThread - private fun newContactAddedWithSipUri(sipUri: String) { + private fun newContactAddedWithSipUri(friend: Friend, sipUri: String) { if (unknownContactsAvatarsMap.keys.contains(sipUri)) { Log.d("$TAG Found SIP URI [$sipUri] in unknownContactsAvatarsMap, removing it") val oldModel = unknownContactsAvatarsMap[sipUri] @@ -195,13 +228,19 @@ class ContactsManager @UiThread constructor() { val oldModel = knownContactsAvatarsMap[sipUri] val address = Factory.instance().createAddress(sipUri) oldModel?.update(address) + } else { + Log.i( + "$TAG New contact added with SIP URI [$sipUri] but no avatar yet, let's create it" + ) + val model = ContactAvatarModel(friend) + knownContactsAvatarsMap[sipUri] = model } } @WorkerThread fun newContactAdded(friend: Friend) { for (sipAddress in friend.addresses) { - newContactAddedWithSipUri(sipAddress.asStringUriOnly()) + newContactAddedWithSipUri(friend, sipAddress.asStringUriOnly()) } conferenceAvatarMap.values.forEach(ContactAvatarModel::destroy) @@ -240,14 +279,12 @@ class ContactsManager @UiThread constructor() { Log.i("$TAG Native contacts have been loaded, cleaning avatars maps") val core = coreContext.core - val found = core.getFriendListByName(FRIEND_LIST_TEMPORARY_STORED) - if (found != null) { - val count = found.friends.size - Log.i( - "$TAG Found temporary friend list with [$count] friends, removing it as no longer necessary" - ) - core.removeFriendList(found) - } + val found = getTemporaryFriendList(native = true) + val count = found.friends.size + Log.i( + "$TAG Found temporary friend list with [$count] friends, removing it as no longer necessary" + ) + core.removeFriendList(found) knownContactsAvatarsMap.values.forEach(ContactAvatarModel::destroy) knownContactsAvatarsMap.clear() @@ -296,6 +333,21 @@ class ContactsManager @UiThread constructor() { return found } + // Start an async query in Magic Search in case LDAP or remote CardDAV is configured + val remoteContactDirectories = coreContext.core.remoteContactDirectories + if (remoteContactDirectories.isNotEmpty()) { + Log.i( + "$TAG SIP URI [$sipUri] not found in locally stored Friends, trying LDAP/CardDAV remote directory" + ) + magicSearch.resetSearchCache() + magicSearch.getContactsListAsync( + username, + address.domain, + MagicSearch.Source.LdapServers.toInt() or MagicSearch.Source.RemoteCardDAV.toInt(), + MagicSearch.Aggregation.Friend + ) + } + val sipAddress = if (sipUri.startsWith("sip:")) { sipUri.substring("sip:".length) } else if (sipUri.startsWith("sips:")) { @@ -435,6 +487,17 @@ class ContactsManager @UiThread constructor() { return avatar } + @WorkerThread + fun isContactAvailable(friend: Friend): Boolean { + return !friend.refKey.isNullOrEmpty() && !isContactTemporary(friend) + } + + @WorkerThread + fun isContactTemporary(friend: Friend): Boolean { + val friendList = friend.friendList + return friendList == null || friendList.displayName == FRIEND_LIST_TEMPORARY_STORED_NATIVE || friendList.displayName == FRIEND_LIST_TEMPORARY_STORED_REMOTE_DIRECTORY + } + @WorkerThread fun onCoreStarted(core: Core) { loadContactsOnlyFromDefaultDirectory = corePreferences.fetchContactsFromDefaultDirectory @@ -444,6 +507,10 @@ class ContactsManager @UiThread constructor() { list.addListener(friendListListener) } + magicSearch = core.createMagicSearch() + magicSearch.limitedSearch = false + magicSearch.addListener(magicSearchListener) + val context = coreContext.context if (ActivityCompat.checkSelfPermission( context, @@ -467,12 +534,31 @@ class ContactsManager @UiThread constructor() { @WorkerThread fun onCoreStopped(core: Core) { coroutineScope.cancel() + + magicSearch.removeListener(magicSearchListener) core.removeListener(coreListener) + for (list in core.friendsLists) { list.removeListener(friendListListener) } } + @WorkerThread + fun getTemporaryFriendList(native: Boolean): FriendList { + val core = coreContext.core + val name = if (native) FRIEND_LIST_TEMPORARY_STORED_NATIVE else FRIEND_LIST_TEMPORARY_STORED_REMOTE_DIRECTORY + val temporaryFriendList = core.getFriendListByName(name) ?: core.createFriendList() + if (temporaryFriendList.displayName.isNullOrEmpty()) { + temporaryFriendList.isDatabaseStorageEnabled = false + temporaryFriendList.displayName = name + core.addFriendList(temporaryFriendList) + Log.i( + "$TAG Created temporary friend list with name [$name]" + ) + } + return temporaryFriendList + } + @WorkerThread fun findNativeContact(address: String, username: String, searchAsPhoneNumber: Boolean): Friend? { if (nativeContactsLoaded) { @@ -499,16 +585,7 @@ class ContactsManager @UiThread constructor() { "$TAG Looking for native contact with address [$address] ${if (searchAsPhoneNumber) "or phone number [$username]" else ""}" ) - val temporaryFriendList = core.getFriendListByName(FRIEND_LIST_TEMPORARY_STORED) ?: core.createFriendList() - if (temporaryFriendList.displayName.isNullOrEmpty()) { - temporaryFriendList.isDatabaseStorageEnabled = false - temporaryFriendList.displayName = FRIEND_LIST_TEMPORARY_STORED - core.addFriendList(temporaryFriendList) - Log.i( - "$TAG Created temporary friend list with name [$FRIEND_LIST_TEMPORARY_STORED]" - ) - } - + val temporaryFriendList = getTemporaryFriendList(native = true) try { val selection = if (searchAsPhoneNumber) { "${ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER} LIKE ? OR ${ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS} LIKE ? OR ${ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS} LIKE ? OR ${ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS} LIKE ?" @@ -649,6 +726,8 @@ class ContactsManager @UiThread constructor() { interface ContactsListener { fun onContactsLoaded() + + fun onContactFoundInRemoteDirectory(friend: Friend) } } diff --git a/app/src/main/java/org/linphone/notifications/NotificationsManager.kt b/app/src/main/java/org/linphone/notifications/NotificationsManager.kt index 700efd1fb..54c282900 100644 --- a/app/src/main/java/org/linphone/notifications/NotificationsManager.kt +++ b/app/src/main/java/org/linphone/notifications/NotificationsManager.kt @@ -50,6 +50,7 @@ import org.linphone.LinphoneApplication.Companion.corePreferences import org.linphone.R import org.linphone.compatibility.Compatibility import org.linphone.contacts.AvatarGenerator +import org.linphone.contacts.ContactsManager.ContactsListener import org.linphone.contacts.getAvatarBitmap import org.linphone.contacts.getPerson import org.linphone.core.Address @@ -59,10 +60,12 @@ import org.linphone.core.ChatMessageListener import org.linphone.core.ChatMessageListenerStub import org.linphone.core.ChatMessageReaction import org.linphone.core.ChatRoom +import org.linphone.core.ConferenceParams import org.linphone.core.Core import org.linphone.core.CoreInCallService import org.linphone.core.CoreKeepAliveThirdPartyAccountsService import org.linphone.core.CoreListenerStub +import org.linphone.core.Factory import org.linphone.core.Friend import org.linphone.core.MediaDirection import org.linphone.core.tools.Log @@ -116,6 +119,57 @@ class NotificationsManager @MainThread constructor(private val context: Context) private var currentlyDisplayedChatRoomId: String = "" + private val contactsListener = object : ContactsListener { + @WorkerThread + override fun onContactsLoaded() { } + + @WorkerThread + override fun onContactFoundInRemoteDirectory(friend: Friend) { + val addresses = friend.addresses + Log.i( + "$TAG Found contact [${friend.name}] in remote directory with [${addresses.size}] addresses" + ) + + for ((remoteAddress, notifiable) in callNotificationsMap.entries) { + val parsedAddress = Factory.instance().createAddress(remoteAddress) + parsedAddress ?: continue + val addressMatch = addresses.find { + it.weakEqual(parsedAddress) + } + if (addressMatch != null) { + Log.i( + "$TAG Found call [${addressMatch.asStringUriOnly()}] with contact in notifications, updating it" + ) + updateCallNotification(notifiable, addressMatch, friend) + } + } + + for ((remoteAddress, notifiable) in chatNotificationsMap.entries) { + var updated = false + var peerAddress: Address? = null + for (message in notifiable.messages) { + val parsedAddress = Factory.instance().createAddress(message.senderAddress) + parsedAddress ?: continue + val addressMatch = addresses.find { + it.weakEqual(parsedAddress) + } + if (addressMatch != null) { + peerAddress = addressMatch + updated = true + message.sender = friend.name ?: LinphoneUtils.getDisplayName(addressMatch) + message.friend = friend + } + } + if (updated && peerAddress != null) { + Log.i( + "$TAG Found conversation [${peerAddress.asStringUriOnly()}] with contact in notifications, updating it" + ) + updateConversationNotification(notifiable, peerAddress) + } + } + } + } + private val coreListener = object : CoreListenerStub() { @WorkerThread override fun onCallStateChanged( @@ -468,12 +522,15 @@ class NotificationsManager @MainThread constructor(private val context: Context) } core.addListener(coreListener) + + coreContext.contactsManager.addListener(contactsListener) } @WorkerThread fun onCoreStopped(core: Core) { Log.i("$TAG Getting destroyed, clearing foreground Service & call notifications") core.removeListener(coreListener) + coreContext.contactsManager.removeListener(contactsListener) } @WorkerThread @@ -499,7 +556,7 @@ class NotificationsManager @MainThread constructor(private val context: Context) } @WorkerThread - private fun showCallNotification(call: Call, isIncoming: Boolean) { + private fun showCallNotification(call: Call, isIncoming: Boolean, friend: Friend? = null) { val notifiable = getNotifiableForCall(call) val callNotificationIntent = Intent(context, CallActivity::class.java) @@ -518,11 +575,11 @@ class NotificationsManager @MainThread constructor(private val context: Context) ) val notification = createCallNotification( - context, call, notifiable, pendingIntent, - isIncoming + isIncoming, + friend ) if (isIncoming) { currentlyRingingCallRemoteAddress = call.remoteAddress @@ -801,6 +858,7 @@ class NotificationsManager @MainThread constructor(private val context: Context) text, contact, displayName, + address.asStringUriOnly(), message.time * 1000, /* Linphone timestamps are in seconds */ isOutgoing = false, isReaction = true, @@ -826,6 +884,33 @@ class NotificationsManager @MainThread constructor(private val context: Context) } } + @WorkerThread + private fun updateConversationNotification(notifiable: Notifiable, remoteAddress: Address) { + val localAddress = Factory.instance().createAddress(notifiable.localIdentity.orEmpty()) + localAddress ?: return + val params: ConferenceParams? = null + val chatRoom: ChatRoom? = coreContext.core.searchChatRoom( + params, + localAddress, + remoteAddress, + arrayOfNulls
(0) + ) + chatRoom ?: return + + val me = coreContext.contactsManager.getMePerson(chatRoom.localAddress) + val pendingIntent = getChatRoomPendingIntent(chatRoom, notifiable.notificationId) + val notification = createMessageNotification( + notifiable, + pendingIntent, + LinphoneUtils.getChatRoomId(chatRoom), + me + ) + Log.i( + "$TAG Updating chat notification with ID [${notifiable.notificationId}]" + ) + notify(notifiable.notificationId, notification, CHAT_TAG) + } + @WorkerThread private fun notify(id: Int, notification: Notification, tag: String? = null) { if (ActivityCompat.checkSelfPermission( @@ -876,7 +961,7 @@ class NotificationsManager @MainThread constructor(private val context: Context) var notifiable: Notifiable? = callNotificationsMap[address] if (notifiable == null) { notifiable = Notifiable(getNotificationIdForCall(call)) - notifiable.remoteAddress = call.remoteAddress.asStringUriOnly() + notifiable.remoteAddress = address callNotificationsMap[address] = notifiable } @@ -890,10 +975,12 @@ class NotificationsManager @MainThread constructor(private val context: Context) val displayName = contact?.name ?: LinphoneUtils.getDisplayName(message.fromAddress) val text = LinphoneUtils.getPlainTextDescribingMessage(message) + val address = message.fromAddress val notifiableMessage = NotifiableMessage( text, contact, displayName, + address.asStringUriOnly(), message.time * 1000, /* Linphone timestamps are in seconds */ isOutgoing = message.isOutgoing ) @@ -922,11 +1009,11 @@ class NotificationsManager @MainThread constructor(private val context: Context) @WorkerThread private fun createCallNotification( - context: Context, call: Call, notifiable: Notifiable, pendingIntent: PendingIntent?, - isIncoming: Boolean + isIncoming: Boolean, + friend: Friend? = null ): Notification { val declineIntent = getCallDeclinePendingIntent(notifiable) val answerIntent = getCallAnswerPendingIntent(notifiable) @@ -953,8 +1040,8 @@ class NotificationsManager @MainThread constructor(private val context: Context) .setImportant(false) .build() } else { - val contact = - coreContext.contactsManager.findContactByAddress(remoteAddress) + val contact = friend + ?: coreContext.contactsManager.findContactByAddress(remoteAddress) val displayName = contact?.name ?: LinphoneUtils.getDisplayName(remoteAddress) getPerson(contact, displayName) @@ -1036,6 +1123,47 @@ class NotificationsManager @MainThread constructor(private val context: Context) return builder.build() } + @WorkerThread + private fun updateCallNotification( + notifiable: Notifiable, + remoteAddress: Address, + friend: Friend + ) { + val call = coreContext.core.getCallByRemoteAddress2(remoteAddress) + if (call == null) { + Log.w( + "$TAG Failed to find call with remote SIP URI [${remoteAddress.asStringUriOnly()}]" + ) + return + } + val isIncoming = LinphoneUtils.isCallIncoming(call.state) + + val notification = notificationsMap[notifiable.notificationId] + if (notification == null) { + Log.w( + "$TAG Failed to find notification with ID [${notifiable.notificationId}], creating a new one" + ) + showCallNotification(call, isIncoming, friend) + return + } + + val pendingIntent = notification.fullScreenIntent + val newNotification = createCallNotification( + call, + notifiable, + pendingIntent, + isIncoming, + friend + ) + if (isIncoming) { + Log.i("$TAG Updating incoming call notification with ID [$INCOMING_CALL_ID]") + notify(INCOMING_CALL_ID, newNotification) + } else { + Log.i("$TAG Updating call notification with ID [${notifiable.notificationId}]") + notify(notifiable.notificationId, newNotification) + } + } + @WorkerThread private fun createMessageNotification( notifiable: Notifiable, @@ -1078,6 +1206,9 @@ class NotificationsManager @MainThread constructor(private val context: Context) style.conversationTitle = if (notifiable.isGroup) notifiable.groupTitle else lastPerson?.name style.isGroupConversation = notifiable.isGroup + Log.i( + "$TAG Conversation is ${if (style.isGroupConversation) "group" else "1-1"} with title [${style.conversationTitle}]" + ) val largeIcon = lastPersonAvatar val notificationBuilder = NotificationCompat.Builder( @@ -1131,7 +1262,7 @@ class NotificationsManager @MainThread constructor(private val context: Context) val notifiable: Notifiable? = chatNotificationsMap[address] if (notifiable != null) { Log.i( - "$TAG Dismissing notification for conversation $chatRoom with id ${notifiable.notificationId}" + "$TAG Dismissing notification for conversation [${chatRoom.peerAddress.asStringUriOnly()}] with id ${notifiable.notificationId}" ) notifiable.messages.clear() cancelNotification(notifiable.notificationId, CHAT_TAG) @@ -1193,6 +1324,7 @@ class NotificationsManager @MainThread constructor(private val context: Context) text, null, notifiable.myself ?: LinphoneUtils.getDisplayName(senderAddress), + senderAddress.asStringUriOnly(), System.currentTimeMillis(), isOutgoing = true ) @@ -1459,9 +1591,10 @@ class NotificationsManager @MainThread constructor(private val context: Context) } class NotifiableMessage( - var message: String, - val friend: Friend?, - val sender: String, + val message: String, + var friend: Friend?, + var sender: String, + val senderAddress: String, val time: Long, var filePath: Uri? = null, var fileMime: String? = null, diff --git a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationInfoFragment.kt b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationInfoFragment.kt index a4c7dff9d..16dde38b4 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationInfoFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationInfoFragment.kt @@ -349,8 +349,8 @@ class ConversationInfoFragment : SlidingPaneChildFragment() { val isAdmin = participantModel.isParticipantAdmin popupView.isParticipantAdmin = isAdmin popupView.isMeAdmin = participantModel.isMyselfAdmin - val friendRefKey = participantModel.avatarModel.friend.refKey - popupView.isParticipantContact = !friendRefKey.isNullOrEmpty() + val friendRefKey = participantModel.refKey + popupView.isParticipantContact = participantModel.friendAvailable popupView.setRemoveParticipantClickListener { Log.i("$TAG Trying to remove participant [$address]") @@ -372,7 +372,7 @@ class ConversationInfoFragment : SlidingPaneChildFragment() { popupView.setSeeContactProfileClickListener { Log.i("$TAG Trying to display participant [$address] contact page") - if (!friendRefKey.isNullOrEmpty()) { + if (friendRefKey.isNotEmpty()) { sharedViewModel.navigateToContactsEvent.value = Event(true) sharedViewModel.showContactEvent.value = Event(friendRefKey) } else { diff --git a/app/src/main/java/org/linphone/ui/main/chat/model/ParticipantModel.kt b/app/src/main/java/org/linphone/ui/main/chat/model/ParticipantModel.kt index c966dc074..ae6ecef86 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/model/ParticipantModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/model/ParticipantModel.kt @@ -24,6 +24,7 @@ import androidx.annotation.UiThread import androidx.annotation.WorkerThread import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.core.Address +import org.linphone.ui.main.contacts.model.ContactAvatarModel class ParticipantModel @WorkerThread constructor( val address: Address, @@ -36,7 +37,15 @@ class ParticipantModel @WorkerThread constructor( ) { val sipUri = address.asStringUriOnly() - val avatarModel = coreContext.contactsManager.getContactAvatarModelForAddress(address) + val avatarModel: ContactAvatarModel = coreContext.contactsManager.getContactAvatarModelForAddress( + address + ) + + val refKey: String = avatarModel.friend.refKey.orEmpty() + + val friendAvailable: Boolean = coreContext.contactsManager.isContactAvailable( + avatarModel.friend + ) @UiThread fun onClicked() { diff --git a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationInfoViewModel.kt b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationInfoViewModel.kt index d8b816dd7..069494c74 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationInfoViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationInfoViewModel.kt @@ -76,6 +76,8 @@ class ConversationInfoViewModel @UiThread constructor() : AbstractConversationVi val oneToOneParticipantRefKey = MutableLiveData() + val friendAvailable = MutableLiveData() + val groupLeftEvent: MutableLiveData> by lazy { MutableLiveData>() } @@ -201,6 +203,9 @@ class ConversationInfoViewModel @UiThread constructor() : AbstractConversationVi Log.i("$TAG Contacts have been (re)loaded, updating list") computeParticipantsList() } + + @WorkerThread + override fun onContactFoundInRemoteDirectory(friend: Friend) { } } init { @@ -497,7 +502,13 @@ class ConversationInfoViewModel @UiThread constructor() : AbstractConversationVi sipUri.postValue(uri) val friend = coreContext.contactsManager.findContactByAddress(address) - oneToOneParticipantRefKey.postValue(friend?.refKey ?: "") + if (friend == null) { + oneToOneParticipantRefKey.postValue("") + friendAvailable.postValue(false) + } else { + oneToOneParticipantRefKey.postValue(friend.refKey) + friendAvailable.postValue(coreContext.contactsManager.isContactAvailable(friend)) + } } ephemeralLifetime.postValue( diff --git a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationViewModel.kt b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationViewModel.kt index a625554dc..2f3108acf 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationViewModel.kt @@ -301,6 +301,9 @@ class ConversationViewModel @UiThread constructor() : AbstractConversationViewMo } } } + + @WorkerThread + override fun onContactFoundInRemoteDirectory(friend: Friend) { } } init { diff --git a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationsListViewModel.kt b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationsListViewModel.kt index 91c3f7bfd..2ca3e9e9c 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationsListViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationsListViewModel.kt @@ -29,6 +29,7 @@ import org.linphone.core.ChatMessage import org.linphone.core.ChatRoom import org.linphone.core.Core import org.linphone.core.CoreListenerStub +import org.linphone.core.Friend import org.linphone.core.tools.Log import org.linphone.ui.main.chat.model.ConversationModel import org.linphone.ui.main.viewmodel.AbstractMainViewModel @@ -86,6 +87,9 @@ class ConversationsListViewModel @UiThread constructor() : AbstractMainViewModel model.updateLastMessage() } } + + @WorkerThread + override fun onContactFoundInRemoteDirectory(friend: Friend) { } } init { 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 47226bf70..0c6fd76f7 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 @@ -186,6 +186,9 @@ class ContactViewModel @UiThread constructor() : GenericViewModel() { refreshContactInfo() } } + + @WorkerThread + override fun onContactFoundInRemoteDirectory(friend: Friend) { } } private val chatRoomListener = object : ChatRoomListenerStub() { 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 a6d8d8c87..b4d3488f1 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 @@ -90,6 +90,9 @@ class ContactsListViewModel @UiThread constructor() : AbstractMainViewModel() { domainFilter ) } + + @WorkerThread + override fun onContactFoundInRemoteDirectory(friend: Friend) { } } init { @@ -281,15 +284,19 @@ class ContactsListViewModel @UiThread constructor() : AbstractMainViewModel() { for (result in results) { val friend = result.friend - if (friend != null && friend.refKey.orEmpty().isEmpty()) { - if (friend.vcard != null) { - friend.vcard?.generateUniqueId() - friend.refKey = friend.vcard?.uid - } else { - Log.w( - "$TAG Friend [${friend.name}] found in SearchResults doesn't have a refKey, using name instead" - ) - friend.refKey = friend.name + if (friend != null) { + if (coreContext.contactsManager.isContactTemporary(friend)) continue + + if (friend.refKey.orEmpty().isEmpty()) { + if (friend.vcard != null) { + friend.vcard?.generateUniqueId() + friend.refKey = friend.vcard?.uid + } else { + Log.w( + "$TAG Friend [${friend.name}] found in SearchResults doesn't have a refKey, using name instead" + ) + friend.refKey = friend.name + } } } diff --git a/app/src/main/java/org/linphone/ui/main/history/model/CallLogModel.kt b/app/src/main/java/org/linphone/ui/main/history/model/CallLogModel.kt index 9a0d95c83..dfd605859 100644 --- a/app/src/main/java/org/linphone/ui/main/history/model/CallLogModel.kt +++ b/app/src/main/java/org/linphone/ui/main/history/model/CallLogModel.kt @@ -93,7 +93,7 @@ class CallLogModel @WorkerThread constructor(private val callLog: CallLog) { avatarModel = coreContext.contactsManager.getContactAvatarModelForAddress(address) val friend = avatarModel.friend friendRefKey = friend.refKey - friendExists = !friendRefKey.isNullOrEmpty() + friendExists = coreContext.contactsManager.isContactAvailable(friend) } displayedAddress = if (corePreferences.onlyDisplaySipUriUsername) { avatarModel.friend.address?.username ?: address.username ?: "" diff --git a/app/src/main/java/org/linphone/ui/main/history/viewmodel/HistoryListViewModel.kt b/app/src/main/java/org/linphone/ui/main/history/viewmodel/HistoryListViewModel.kt index bb480311a..5b2f4abf5 100644 --- a/app/src/main/java/org/linphone/ui/main/history/viewmodel/HistoryListViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/history/viewmodel/HistoryListViewModel.kt @@ -27,6 +27,7 @@ import org.linphone.contacts.ContactsManager import org.linphone.core.CallLog import org.linphone.core.Core import org.linphone.core.CoreListenerStub +import org.linphone.core.Friend import org.linphone.core.GlobalState import org.linphone.core.tools.Log import org.linphone.ui.main.history.model.CallLogModel @@ -72,6 +73,9 @@ class HistoryListViewModel @UiThread constructor() : AbstractMainViewModel() { Log.i("$TAG Contacts have been (re)loaded, updating list") computeCallLogsList(currentFilter) } + + @WorkerThread + override fun onContactFoundInRemoteDirectory(friend: Friend) { } } init { 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 13aab7f24..7de2eafea 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 @@ -30,6 +30,7 @@ import org.linphone.R import org.linphone.contacts.ContactsManager import org.linphone.core.Address import org.linphone.core.ChatRoom +import org.linphone.core.Friend import org.linphone.core.MagicSearch import org.linphone.core.MagicSearchListenerStub import org.linphone.core.SearchResult @@ -87,6 +88,9 @@ abstract class AddressSelectionViewModel @UiThread constructor() : DefaultAccoun magicSearchSourceFlags ) } + + @WorkerThread + override fun onContactFoundInRemoteDirectory(friend: Friend) { } } init { diff --git a/app/src/main/res/layout/chat_info_fragment.xml b/app/src/main/res/layout/chat_info_fragment.xml index 49ed5f770..d998bbd6b 100644 --- a/app/src/main/res/layout/chat_info_fragment.xml +++ b/app/src/main/res/layout/chat_info_fragment.xml @@ -400,7 +400,7 @@ android:drawableStart="@drawable/address_book" android:onClick="@{goToContactClickListener}" android:text="@string/conversation_info_menu_go_to_contact" - android:visibility="@{!viewModel.isGroup && viewModel.oneToOneParticipantRefKey.length() != 0 ? View.VISIBLE : View.GONE, default=gone}" + android:visibility="@{!viewModel.isGroup && viewModel.friendAvailable ? View.VISIBLE : View.GONE, default=gone}" app:layout_constraintEnd_toEndOf="@id/actions_background" app:layout_constraintStart_toStartOf="@id/actions_background" app:layout_constraintTop_toTopOf="@id/actions_background" /> @@ -424,7 +424,7 @@ android:drawableStart="@drawable/user_plus" android:onClick="@{addToContactsClickListener}" android:text="@string/conversation_info_menu_add_to_contacts" - android:visibility="@{!viewModel.isGroup && viewModel.oneToOneParticipantRefKey.length() == 0 ? View.VISIBLE : View.GONE, default=gone}" + android:visibility="@{!viewModel.isGroup && !viewModel.friendAvailable ? View.VISIBLE : View.GONE, default=gone}" app:layout_constraintEnd_toEndOf="@id/actions_background" app:layout_constraintStart_toStartOf="@id/actions_background" app:layout_constraintTop_toBottomOf="@id/action_see_contact" />