Fixed contact lookup from background

This commit is contained in:
Sylvain Berfini 2023-09-21 17:26:49 +02:00
parent 4d2d01195a
commit 262d6d551b
3 changed files with 136 additions and 13 deletions

View file

@ -326,7 +326,7 @@ class ContactLoader : LoaderManager.LoaderCallbacks<Cursor> {
fl.updateSubscriptions()
Log.i("$TAG Subscription(s) updated")
coreContext.contactsManager.onContactsLoaded()
coreContext.contactsManager.onNativeContactsLoaded()
}
} catch (sde: StaleDataException) {
Log.e("$TAG State Data Exception: $sde")

View file

@ -19,11 +19,17 @@
*/
package org.linphone.contacts
import android.Manifest
import android.content.ContentUris
import android.content.Context
import android.content.pm.PackageManager
import android.database.Cursor
import android.graphics.Bitmap
import android.net.Uri
import android.provider.ContactsContract
import androidx.annotation.UiThread
import androidx.annotation.WorkerThread
import androidx.core.app.ActivityCompat
import androidx.core.app.Person
import androidx.core.graphics.drawable.IconCompat
import androidx.loader.app.LoaderManager
@ -51,6 +57,8 @@ class ContactsManager @UiThread constructor(context: Context) {
val contactAvatar: IconCompat
private var nativeContactsLoaded = false
private val listeners = arrayListOf<ContactsListener>()
private val friendListListener: FriendListListenerStub = object : FriendListListenerStub() {
@ -94,20 +102,31 @@ class ContactsManager @UiThread constructor(context: Context) {
@WorkerThread
fun addListener(listener: ContactsListener) {
if (coreContext.isReady()) {
listeners.add(listener)
// Post again to prevent ConcurrentModificationException
coreContext.postOnCoreThread {
try {
listeners.add(listener)
} catch (cme: ConcurrentModificationException) {
}
}
}
@WorkerThread
fun removeListener(listener: ContactsListener) {
if (coreContext.isReady()) {
listeners.remove(listener)
// Post again to prevent ConcurrentModificationException
coreContext.postOnCoreThread {
try {
listeners.remove(listener)
} catch (cme: ConcurrentModificationException) {
}
}
}
}
@UiThread
fun onContactsLoaded() {
fun onNativeContactsLoaded() {
nativeContactsLoaded = true
coreContext.postOnCoreThread {
notifyContactsListChanged()
}
@ -131,14 +150,17 @@ class ContactsManager @UiThread constructor(context: Context) {
@WorkerThread
fun findContactByAddress(address: Address): Friend? {
Log.i("$TAG Looking for friend with address [${address.asStringUriOnly()}]")
val username = address.username
val usernameIsPhoneNumber = !username.isNullOrEmpty() && username.startsWith("+")
return coreContext.core.findFriend(address) ?: if (usernameIsPhoneNumber) {
coreContext.core.findFriendByPhoneNumber(
username!!
val found = coreContext.core.findFriend(address)
return found ?: if (!username.isNullOrEmpty() && username.startsWith("+")) {
Log.i("$TAG Looking for friend with phone number [$username]")
val foundUsingPhoneNumber = coreContext.core.findFriendByPhoneNumber(
username
)
foundUsingPhoneNumber ?: findNativeContact(address)
} else {
null
findNativeContact(address)
}
}
@ -158,6 +180,103 @@ class ContactsManager @UiThread constructor(context: Context) {
}
}
@WorkerThread
fun findNativeContact(address: Address): Friend? {
Log.i(
"$TAG Looking for native contact with address [${address.asStringUriOnly()}] or phone number [${address.username}]"
)
if (nativeContactsLoaded) {
Log.w(
"$TAG Native contacts already loaded, no need to search further, no native contact matches address [${address.asStringUriOnly()}]"
)
return null
}
val context = coreContext.context
if (ActivityCompat.checkSelfPermission(
context,
Manifest.permission.READ_CONTACTS
) == PackageManager.PERMISSION_GRANTED
) {
val number: String = address.username.orEmpty()
val sipUri: String = address.asStringUriOnly()
try {
val selection = "${ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER} LIKE ? OR ${ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS} LIKE ?"
val cursor: Cursor? = context.contentResolver.query(
ContactsContract.Data.CONTENT_URI,
arrayOf(
ContactsContract.Data.CONTACT_ID,
ContactsContract.Contacts.LOOKUP_KEY,
ContactsContract.Data.DISPLAY_NAME_PRIMARY
),
selection,
arrayOf(number, sipUri),
null
)
if (cursor != null && cursor.moveToNext()) {
val friend = coreContext.core.createFriend()
friend.edit()
do {
val id: String =
cursor.getString(
cursor.getColumnIndexOrThrow(ContactsContract.Data.CONTACT_ID)
)
friend.refKey = id
if (friend.name.isNullOrEmpty()) {
val displayName: String? =
cursor.getString(
cursor.getColumnIndexOrThrow(
ContactsContract.Data.DISPLAY_NAME_PRIMARY
)
)
friend.name = displayName
}
if (friend.photo.isNullOrEmpty()) {
val picture = Uri.withAppendedPath(
ContentUris.withAppendedId(
ContactsContract.Contacts.CONTENT_URI,
id.toLong()
),
ContactsContract.Contacts.Photo.CONTENT_DIRECTORY
).toString()
friend.photo = picture
}
if (friend.nativeUri.isNullOrEmpty()) {
val lookupKey =
cursor.getString(
cursor.getColumnIndexOrThrow(
ContactsContract.Contacts.LOOKUP_KEY
)
)
friend.nativeUri =
"${ContactsContract.Contacts.CONTENT_LOOKUP_URI}/$lookupKey"
}
} while (cursor.moveToNext())
friend.address = address
friend.done()
Log.i("$TAG Found native contact [${friend.name}] with address [$sipUri]")
cursor.close()
return friend
}
Log.w("$TAG Failed to find native contact with address [$sipUri]")
return null
} catch (e: IllegalArgumentException) {
Log.e("$TAG Failed to search for native contact with address [$sipUri]: $e")
}
} else {
Log.w("$TAG READ_CONTACTS permission not granted, can't check native address book")
}
return null
}
@WorkerThread
fun getMePerson(localAddress: Address): Person {
val account = coreContext.core.accountList.find {

View file

@ -176,7 +176,7 @@ class NotificationsManager @MainThread constructor(private val context: Context)
}
val chatRoomPeerAddress = chatRoom.peerAddress.asStringUriOnly()
var notifiable: Notifiable? = chatNotificationsMap[chatRoomPeerAddress]
val notifiable: Notifiable? = chatNotificationsMap[chatRoomPeerAddress]
if (notifiable == null) {
Log.i("$TAG No notification for chat room [$chatRoomPeerAddress], nothing to do")
return
@ -489,7 +489,9 @@ class NotificationsManager @MainThread constructor(private val context: Context)
Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED
) {
Log.i("$TAG Notifying [$id] with tag [$tag]")
Log.i(
"$TAG Notifying using ID [$id] and ${if (tag == null) "without tag" else "with tag [$tag]"}"
)
try {
notificationManager.notify(tag, id, notification)
} catch (iae: IllegalArgumentException) {
@ -509,7 +511,9 @@ class NotificationsManager @MainThread constructor(private val context: Context)
@WorkerThread
fun cancelNotification(id: Int, tag: String? = null) {
Log.i("$TAG Canceling [$id] with tag [$tag]")
Log.i(
"$TAG Canceling notification with ID [$id] and ${if (tag == null) "without tag" else "with tag [$tag]"}"
)
notificationManager.cancel(tag, id)
}