Added remote contact directory lookup if friend isn't found locally

This commit is contained in:
Sylvain Berfini 2024-11-21 11:54:10 +01:00
parent 8d78f2b698
commit 213e62d125
13 changed files with 308 additions and 51 deletions

View file

@ -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)
}
}

View file

@ -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<Address>(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,

View file

@ -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 {

View file

@ -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() {

View file

@ -76,6 +76,8 @@ class ConversationInfoViewModel @UiThread constructor() : AbstractConversationVi
val oneToOneParticipantRefKey = MutableLiveData<String>()
val friendAvailable = MutableLiveData<Boolean>()
val groupLeftEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
@ -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(

View file

@ -301,6 +301,9 @@ class ConversationViewModel @UiThread constructor() : AbstractConversationViewMo
}
}
}
@WorkerThread
override fun onContactFoundInRemoteDirectory(friend: Friend) { }
}
init {

View file

@ -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 {

View file

@ -186,6 +186,9 @@ class ContactViewModel @UiThread constructor() : GenericViewModel() {
refreshContactInfo()
}
}
@WorkerThread
override fun onContactFoundInRemoteDirectory(friend: Friend) { }
}
private val chatRoomListener = object : ChatRoomListenerStub() {

View file

@ -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
}
}
}

View file

@ -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 ?: ""

View file

@ -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 {

View file

@ -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 {

View file

@ -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 &amp;&amp; viewModel.oneToOneParticipantRefKey.length() != 0 ? View.VISIBLE : View.GONE, default=gone}"
android:visibility="@{!viewModel.isGroup &amp;&amp; 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 &amp;&amp; viewModel.oneToOneParticipantRefKey.length() == 0 ? View.VISIBLE : View.GONE, default=gone}"
android:visibility="@{!viewModel.isGroup &amp;&amp; !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" />