Fixed presence storage in native contacts + prevent duplicated SIP addresses in same contact + improved contact fetching used memory

This commit is contained in:
Sylvain Berfini 2022-06-08 15:39:00 +02:00
parent 6222758126
commit ed03a721a5
4 changed files with 38 additions and 49 deletions

View file

@ -119,7 +119,6 @@ class ContactsSettingsFragment : GenericSettingFragment<SettingsContactsFragment
if (granted) { if (granted) {
Log.i("[Contacts Settings] WRITE_CONTACTS permission granted") Log.i("[Contacts Settings] WRITE_CONTACTS permission granted")
corePreferences.storePresenceInNativeContact = true corePreferences.storePresenceInNativeContact = true
coreContext.contactsManager.storePresenceInformationForAllContacts()
} else { } else {
Log.w("[Contacts Settings] WRITE_CONTACTS permission denied") Log.w("[Contacts Settings] WRITE_CONTACTS permission denied")
} }

View file

@ -38,10 +38,7 @@ import kotlinx.coroutines.withContext
import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R import org.linphone.R
import org.linphone.core.Factory import org.linphone.core.*
import org.linphone.core.Friend
import org.linphone.core.GlobalState
import org.linphone.core.SubscribePolicy
import org.linphone.core.tools.Log import org.linphone.core.tools.Log
import org.linphone.utils.PhoneNumberUtils import org.linphone.utils.PhoneNumberUtils
@ -76,7 +73,7 @@ class ContactLoader : LoaderManager.LoaderCallbacks<Cursor> {
projection, projection,
selection, selection,
null, null,
null ContactsContract.Data.CONTACT_ID + " ASC"
) )
} }
@ -101,7 +98,9 @@ class ContactLoader : LoaderManager.LoaderCallbacks<Cursor> {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
try { try {
// Cursor can be null now that we are on a different dispatcher according to Crashlytics // Cursor can be null now that we are on a different dispatcher according to Crashlytics
val friendsPhoneNumbers = HashMap<String, List<String>>() val friendsPhoneNumbers = arrayListOf<String>()
val friendsAddresses = arrayListOf<Address>()
var previousId = ""
while (cursor != null && !cursor.isClosed && cursor.moveToNext()) { while (cursor != null && !cursor.isClosed && cursor.moveToNext()) {
try { try {
val id: String = val id: String =
@ -123,6 +122,12 @@ class ContactLoader : LoaderManager.LoaderCallbacks<Cursor> {
val lookupKey = val lookupKey =
cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Contacts.LOOKUP_KEY)) cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Contacts.LOOKUP_KEY))
if (previousId.isEmpty() || previousId != id) {
friendsPhoneNumbers.clear()
friendsAddresses.clear()
previousId = id
}
val friend = friends[id] ?: core.createFriend() val friend = friends[id] ?: core.createFriend()
friend.refKey = id friend.refKey = id
if (friend.name.isNullOrEmpty()) { if (friend.name.isNullOrEmpty()) {
@ -143,9 +148,6 @@ class ContactLoader : LoaderManager.LoaderCallbacks<Cursor> {
friend.incSubscribePolicy = SubscribePolicy.SPDeny friend.incSubscribePolicy = SubscribePolicy.SPDeny
} }
if (!friendsPhoneNumbers.containsKey(id)) {
friendsPhoneNumbers[id] = arrayListOf()
}
when (mime) { when (mime) {
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE -> { ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE -> {
val label = PhoneNumberUtils.addressBookLabelTypeToVcardParamString( val label = PhoneNumberUtils.addressBookLabelTypeToVcardParamString(
@ -165,26 +167,32 @@ class ContactLoader : LoaderManager.LoaderCallbacks<Cursor> {
if (number != null) { if (number != null) {
if ( if (
friendsPhoneNumbers[id].orEmpty().find { friendsPhoneNumbers.find {
PhoneNumberUtils.arePhoneNumberWeakEqual(it, number) PhoneNumberUtils.arePhoneNumberWeakEqual(it, number)
} == null } == null
) { ) {
val phoneNumber = Factory.instance() val phoneNumber = Factory.instance()
.createFriendPhoneNumber(number, label) .createFriendPhoneNumber(number, label)
friend.addPhoneNumberWithLabel(phoneNumber) friend.addPhoneNumberWithLabel(phoneNumber)
friendsPhoneNumbers[id] = friendsPhoneNumbers[id].orEmpty().plus(number) friendsPhoneNumbers.add(number)
} }
} }
} }
linphoneMime, ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE -> { linphoneMime, ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE -> {
if (data1 == null) continue if (data1 == null) continue
val address = core.interpretUrl(data1) ?: continue val address = core.interpretUrl(data1) ?: continue
if (
friendsAddresses.find {
it.weakEqual(address)
} == null
) {
friend.addAddress(address) friend.addAddress(address)
friendsAddresses.add(address)
}
} }
ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE -> { ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE -> {
if (data1 == null) continue if (data1 == null) continue
val vCard = friend.vcard friend.organization = data1
vCard?.organization = data1
} }
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE -> { ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE -> {
if (data2 == null && data3 == null) continue if (data2 == null && data3 == null) continue
@ -199,7 +207,6 @@ class ContactLoader : LoaderManager.LoaderCallbacks<Cursor> {
Log.e("[Contacts Loader] Exception: $e") Log.e("[Contacts Loader] Exception: $e")
} }
} }
friendsPhoneNumbers.clear()
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
if (core.globalState == GlobalState.Shutdown || core.globalState == GlobalState.Off) { if (core.globalState == GlobalState.Shutdown || core.globalState == GlobalState.Off) {

View file

@ -275,40 +275,19 @@ class ContactsManager(private val context: Context) {
notifyListeners(friend) notifyListeners(friend)
} }
@Synchronized
fun storePresenceInformationForAllContacts() {
if (corePreferences.storePresenceInNativeContact && PermissionHelper.get().hasWriteContactsPermission()) {
for (list in coreContext.core.friendsLists) {
for (friend in list.friends) {
val id = friend.refKey
if (id != null) {
storePresenceInNativeContact(friend)
}
}
}
}
}
private fun storePresenceInNativeContact(friend: Friend) { private fun storePresenceInNativeContact(friend: Friend) {
val contactEditor = NativeContactEditor(friend)
for (phoneNumber in friend.phoneNumbers) { for (phoneNumber in friend.phoneNumbers) {
val sipAddress = friend.getContactForPhoneNumberOrAddress(phoneNumber) val sipAddress = friend.getContactForPhoneNumberOrAddress(phoneNumber)
if (sipAddress != null) { if (sipAddress != null) {
Log.d("[Contacts Manager] Found presence information to store in native contact $friend under Linphone sync account") Log.d("[Contacts Manager] Found presence information to store in native contact $friend under Linphone sync account")
val contactEditor = NativeContactEditor(friend)
val coroutineScope = CoroutineScope(Dispatchers.Main)
coroutineScope.launch {
val deferred = async {
withContext(Dispatchers.IO) {
contactEditor.setPresenceInformation( contactEditor.setPresenceInformation(
phoneNumber, phoneNumber,
sipAddress sipAddress
).commit() )
}
}
deferred.await()
}
} }
} }
contactEditor.commit()
} }
fun createFriendFromSearchResult(searchResult: SearchResult): Friend { fun createFriendFromSearchResult(searchResult: SearchResult): Friend {

View file

@ -103,7 +103,7 @@ class NativeContactEditor(val friend: Friend) {
if (rawId == null) { if (rawId == null) {
try { try {
rawId = cursor.getString(cursor.getColumnIndexOrThrow(RawContacts._ID)) rawId = cursor.getString(cursor.getColumnIndexOrThrow(RawContacts._ID))
Log.i("[Native Contact Editor] Found raw id $rawId for native contact with id ${friend.refKey}") Log.d("[Native Contact Editor] Found raw id $rawId for native contact with id ${friend.refKey}")
} catch (iae: IllegalArgumentException) { } catch (iae: IllegalArgumentException) {
Log.e("[Native Contact Editor] Exception: $iae") Log.e("[Native Contact Editor] Exception: $iae")
} }
@ -484,15 +484,19 @@ class NativeContactEditor(val friend: Friend) {
} else null } else null
cursor?.close() cursor?.close()
val address = if (sipAddress.endsWith(";user=phone")) {
sipAddress.substring(0, sipAddress.length - ";user=phone".length)
} else sipAddress
if (count == 0) { if (count == 0) {
Log.i("[Native Contact Editor] No existing presence information found for this phone number & SIP address, let's add it") Log.i("[Native Contact Editor] No existing presence information found for this phone number ($phoneNumber) & SIP address ($address), let's add it")
addPresenceLinphoneSipAddressForPhoneNumber(sipAddress, phoneNumber) addPresenceLinphoneSipAddressForPhoneNumber(address, phoneNumber)
} else { } else {
if (data1 != null && data1 == sipAddress) { if (data1 != null && data1 == address) {
Log.d("[Native Contact Editor] There is already an entry for this phone number and SIP address, skipping") Log.d("[Native Contact Editor] There is already an entry for this phone number and SIP address, skipping")
} else { } else {
Log.w("[Native Contact Editor] There is already an entry for this phone number but not for the same SIP address") Log.w("[Native Contact Editor] There is already an entry for this phone number ($phoneNumber) but not for the same SIP address ($data1 != $address)")
updatePresenceLinphoneSipAddressForPhoneNumber(sipAddress, phoneNumber) updatePresenceLinphoneSipAddressForPhoneNumber(address, phoneNumber)
} }
} }
} }