Changes regarding contacts: always create & store changes in default account + always use linphone sync account for presence storage
This commit is contained in:
parent
2979ca0934
commit
888e136368
4 changed files with 68 additions and 112 deletions
|
@ -81,14 +81,15 @@ class ContactEditorViewModel(val c: Contact?) : ViewModel(), ContactViewModelInt
|
||||||
if (contact == null) {
|
if (contact == null) {
|
||||||
created = true
|
created = true
|
||||||
contact = if (PermissionHelper.get().hasWriteContactsPermission()) {
|
contact = if (PermissionHelper.get().hasWriteContactsPermission()) {
|
||||||
NativeContact(NativeContactEditor.createAndroidContact(null, null).toString())
|
// Store native contact in default sync account
|
||||||
|
NativeContact(NativeContactEditor.createAndroidContact().toString())
|
||||||
} else {
|
} else {
|
||||||
Contact()
|
Contact()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contact is NativeContact) {
|
if (contact is NativeContact) {
|
||||||
NativeContactEditor(contact, null, null)
|
NativeContactEditor(contact)
|
||||||
.setFirstAndLastNames(firstName.value.orEmpty(), lastName.value.orEmpty())
|
.setFirstAndLastNames(firstName.value.orEmpty(), lastName.value.orEmpty())
|
||||||
.setOrganization(organization.value.orEmpty())
|
.setOrganization(organization.value.orEmpty())
|
||||||
.setPhoneNumbers(numbers.value.orEmpty())
|
.setPhoneNumbers(numbers.value.orEmpty())
|
||||||
|
|
|
@ -247,11 +247,6 @@ class ContactsManager(private val context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initSyncAccount() {
|
private fun initSyncAccount() {
|
||||||
if (!corePreferences.useLinphoneSyncAccount) {
|
|
||||||
Log.w("[Contacts Manager] Linphone sync account disabled, skipping initialization")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val accountManager = context.getSystemService(Context.ACCOUNT_SERVICE) as AccountManager
|
val accountManager = context.getSystemService(Context.ACCOUNT_SERVICE) as AccountManager
|
||||||
val accounts = accountManager.getAccountsByType(context.getString(R.string.sync_account_type))
|
val accounts = accountManager.getAccountsByType(context.getString(R.string.sync_account_type))
|
||||||
if (accounts.isEmpty()) {
|
if (accounts.isEmpty()) {
|
||||||
|
@ -332,13 +327,13 @@ class ContactsManager(private val context: Context) {
|
||||||
for (phoneNumber in contact.phoneNumbers) {
|
for (phoneNumber in contact.phoneNumbers) {
|
||||||
val sipAddress = contact.getContactForPhoneNumberOrAddress(phoneNumber)
|
val sipAddress = contact.getContactForPhoneNumberOrAddress(phoneNumber)
|
||||||
if (sipAddress != null) {
|
if (sipAddress != null) {
|
||||||
Log.d("[Contacts Manager] Found presence information to store in native contact $contact")
|
Log.d("[Contacts Manager] Found presence information to store in native contact $contact under Linphone sync account")
|
||||||
val contactEditor = NativeContactEditor(contact, null, null)
|
val contactEditor = NativeContactEditor(contact, context.getString(R.string.sync_account_name), context.getString(R.string.sync_account_type))
|
||||||
val coroutineScope = CoroutineScope(Dispatchers.Main)
|
val coroutineScope = CoroutineScope(Dispatchers.Main)
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
val deferred = async {
|
val deferred = async {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
contactEditor.setPresenceInformation(phoneNumber, sipAddress).commit()
|
contactEditor.ensureSyncAccountRawIdExists().setPresenceInformation(phoneNumber, sipAddress).commit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
deferred.await()
|
deferred.await()
|
||||||
|
|
|
@ -27,7 +27,6 @@ import android.provider.ContactsContract
|
||||||
import android.provider.ContactsContract.CommonDataKinds
|
import android.provider.ContactsContract.CommonDataKinds
|
||||||
import android.provider.ContactsContract.RawContacts
|
import android.provider.ContactsContract.RawContacts
|
||||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||||
import org.linphone.LinphoneApplication.Companion.corePreferences
|
|
||||||
import org.linphone.R
|
import org.linphone.R
|
||||||
import org.linphone.activities.main.contact.viewmodels.NumberOrAddressEditorViewModel
|
import org.linphone.activities.main.contact.viewmodels.NumberOrAddressEditorViewModel
|
||||||
import org.linphone.core.tools.Log
|
import org.linphone.core.tools.Log
|
||||||
|
@ -36,20 +35,14 @@ import org.linphone.utils.PermissionHelper
|
||||||
|
|
||||||
class NativeContactEditor(
|
class NativeContactEditor(
|
||||||
val contact: NativeContact,
|
val contact: NativeContact,
|
||||||
private var syncAccountName: String?,
|
private var syncAccountName: String? = null,
|
||||||
private var syncAccountType: String?
|
private var syncAccountType: String? = null
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
fun createAndroidContact(accountName: String?, accountType: String?): Long {
|
fun createAndroidContact(accountName: String? = null, accountType: String? = null): Long {
|
||||||
val values = ContentValues()
|
val values = ContentValues()
|
||||||
|
|
||||||
if (accountName == null && accountType == null && corePreferences.useLinphoneSyncAccount) {
|
|
||||||
values.put(RawContacts.ACCOUNT_NAME, AppUtils.getString(R.string.sync_account_name))
|
|
||||||
values.put(RawContacts.ACCOUNT_TYPE, AppUtils.getString(R.string.sync_account_type))
|
|
||||||
} else {
|
|
||||||
values.put(RawContacts.ACCOUNT_NAME, accountName)
|
values.put(RawContacts.ACCOUNT_NAME, accountName)
|
||||||
values.put(RawContacts.ACCOUNT_TYPE, accountType)
|
values.put(RawContacts.ACCOUNT_TYPE, accountType)
|
||||||
}
|
|
||||||
|
|
||||||
val rawContactUri = coreContext.context.contentResolver
|
val rawContactUri = coreContext.context.contentResolver
|
||||||
.insert(RawContacts.CONTENT_URI, values)
|
.insert(RawContacts.CONTENT_URI, values)
|
||||||
|
@ -66,19 +59,13 @@ class NativeContactEditor(
|
||||||
"${ContactsContract.Data.CONTACT_ID} =? AND (${ContactsContract.Data.MIMETYPE} =? OR ${ContactsContract.Data.MIMETYPE} =?) AND data1=?"
|
"${ContactsContract.Data.CONTACT_ID} =? AND (${ContactsContract.Data.MIMETYPE} =? OR ${ContactsContract.Data.MIMETYPE} =?) AND data1=?"
|
||||||
private val presenceUpdateSelection =
|
private val presenceUpdateSelection =
|
||||||
"${ContactsContract.Data.CONTACT_ID} =? AND ${ContactsContract.Data.MIMETYPE} =? AND data3=?"
|
"${ContactsContract.Data.CONTACT_ID} =? AND ${ContactsContract.Data.MIMETYPE} =? AND data3=?"
|
||||||
private val useLinphoneSyncAccount = corePreferences.useLinphoneSyncAccount
|
|
||||||
private val contactUri = ContactsContract.Data.CONTENT_URI
|
private val contactUri = ContactsContract.Data.CONTENT_URI
|
||||||
|
|
||||||
private var rawId: String? = null
|
private var rawId: String? = null
|
||||||
private var linphoneRawId: String? = null
|
private var syncAccountRawId: String? = null
|
||||||
private var pictureByteArray: ByteArray? = null
|
private var pictureByteArray: ByteArray? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (syncAccountName == null && syncAccountType == null && useLinphoneSyncAccount) {
|
|
||||||
syncAccountName = AppUtils.getString(R.string.sync_account_name)
|
|
||||||
syncAccountType = AppUtils.getString(R.string.sync_account_type)
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i("[Native Contact Editor] Using sync account $syncAccountName with type $syncAccountType")
|
Log.i("[Native Contact Editor] Using sync account $syncAccountName with type $syncAccountType")
|
||||||
val contentResolver = coreContext.context.contentResolver
|
val contentResolver = coreContext.context.contentResolver
|
||||||
val cursor = contentResolver.query(
|
val cursor = contentResolver.query(
|
||||||
|
@ -96,40 +83,16 @@ class NativeContactEditor(
|
||||||
}
|
}
|
||||||
|
|
||||||
val accountType = cursor.getString(cursor.getColumnIndex(RawContacts.ACCOUNT_TYPE))
|
val accountType = cursor.getString(cursor.getColumnIndex(RawContacts.ACCOUNT_TYPE))
|
||||||
if (accountType == syncAccountType && linphoneRawId == null) {
|
if (accountType == syncAccountType && syncAccountRawId == null) {
|
||||||
linphoneRawId = cursor.getString(cursor.getColumnIndex(RawContacts._ID))
|
syncAccountRawId = cursor.getString(cursor.getColumnIndex(RawContacts._ID))
|
||||||
Log.d("[Native Contact Editor] Found linphone raw id $linphoneRawId for native contact with id ${contact.nativeId}")
|
Log.d("[Native Contact Editor] Found sync account raw id $syncAccountRawId for native contact with id ${contact.nativeId}")
|
||||||
}
|
}
|
||||||
} while (cursor.moveToNext() && linphoneRawId == null)
|
} while (cursor.moveToNext() && syncAccountRawId == null)
|
||||||
}
|
}
|
||||||
cursor?.close()
|
cursor?.close()
|
||||||
|
|
||||||
// When contact has been created with NativeContactEditor.createAndroidContact this is required
|
// When contact has been created with NativeContactEditor.createAndroidContact this is required
|
||||||
if (rawId == null) rawId = contact.nativeId
|
if (rawId == null) rawId = contact.nativeId
|
||||||
|
|
||||||
if (linphoneRawId == null && useLinphoneSyncAccount) {
|
|
||||||
Log.w("[Native Contact Editor] Linphone raw id not found")
|
|
||||||
val insert = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
|
|
||||||
.withValue(RawContacts.ACCOUNT_TYPE, syncAccountType)
|
|
||||||
.withValue(RawContacts.ACCOUNT_NAME, syncAccountName)
|
|
||||||
.withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DEFAULT)
|
|
||||||
.build()
|
|
||||||
addChanges(insert)
|
|
||||||
val update =
|
|
||||||
ContentProviderOperation.newUpdate(ContactsContract.AggregationExceptions.CONTENT_URI)
|
|
||||||
.withValue(
|
|
||||||
ContactsContract.AggregationExceptions.TYPE,
|
|
||||||
ContactsContract.AggregationExceptions.TYPE_KEEP_TOGETHER
|
|
||||||
)
|
|
||||||
.withValue(ContactsContract.AggregationExceptions.RAW_CONTACT_ID1, rawId)
|
|
||||||
.withValueBackReference(
|
|
||||||
ContactsContract.AggregationExceptions.RAW_CONTACT_ID2,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
.build()
|
|
||||||
addChanges(update)
|
|
||||||
commit()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setFirstAndLastNames(firstName: String, lastName: String): NativeContactEditor {
|
fun setFirstAndLastNames(firstName: String, lastName: String): NativeContactEditor {
|
||||||
|
@ -238,12 +201,8 @@ class NativeContactEditor(
|
||||||
// New address to add
|
// New address to add
|
||||||
addCount++
|
addCount++
|
||||||
val address = sipAddress.newValue.value.orEmpty()
|
val address = sipAddress.newValue.value.orEmpty()
|
||||||
if (useLinphoneSyncAccount) {
|
|
||||||
addLinphoneAddress(address, address)
|
|
||||||
} else {
|
|
||||||
addSipAddress(address)
|
addSipAddress(address)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
sipAddress.toRemove.value == true -> {
|
sipAddress.toRemove.value == true -> {
|
||||||
// Existing address to remove
|
// Existing address to remove
|
||||||
removeCount++
|
removeCount++
|
||||||
|
@ -267,15 +226,41 @@ class NativeContactEditor(
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setPresenceInformation(phoneNumber: String, sipAddress: String): NativeContactEditor {
|
fun ensureSyncAccountRawIdExists(): NativeContactEditor {
|
||||||
Log.d("[Native Contact Editor] Trying to add presence information to contact as ${if (useLinphoneSyncAccount) "phone number" else "SIP address"}")
|
if (syncAccountRawId == null) {
|
||||||
|
Log.w("[Native Contact Editor] Sync account raw id not found")
|
||||||
if (useLinphoneSyncAccount) {
|
val insert = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
|
||||||
setPresenceLinphoneSipAddressForPhoneNumber(sipAddress, phoneNumber)
|
.withValue(RawContacts.ACCOUNT_TYPE, syncAccountType)
|
||||||
} else {
|
.withValue(RawContacts.ACCOUNT_NAME, syncAccountName)
|
||||||
setPresenceSipAddress(sipAddress)
|
.withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DEFAULT)
|
||||||
|
.build()
|
||||||
|
addChanges(insert)
|
||||||
|
val update =
|
||||||
|
ContentProviderOperation.newUpdate(ContactsContract.AggregationExceptions.CONTENT_URI)
|
||||||
|
.withValue(
|
||||||
|
ContactsContract.AggregationExceptions.TYPE,
|
||||||
|
ContactsContract.AggregationExceptions.TYPE_KEEP_TOGETHER
|
||||||
|
)
|
||||||
|
.withValue(ContactsContract.AggregationExceptions.RAW_CONTACT_ID1, rawId)
|
||||||
|
.withValueBackReference(
|
||||||
|
ContactsContract.AggregationExceptions.RAW_CONTACT_ID2,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
addChanges(update)
|
||||||
|
commit()
|
||||||
|
}
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setPresenceInformation(phoneNumber: String, sipAddress: String): NativeContactEditor {
|
||||||
|
if (syncAccountRawId == null) {
|
||||||
|
Log.e("[Native Contact Editor] Can't add presence to contact in Linphone sync account, no raw id")
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d("[Native Contact Editor] Trying to add presence information to contact")
|
||||||
|
setPresenceLinphoneSipAddressForPhoneNumber(sipAddress, phoneNumber)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,9 +272,9 @@ class NativeContactEditor(
|
||||||
val results = contentResolver.applyBatch(ContactsContract.AUTHORITY, changes)
|
val results = contentResolver.applyBatch(ContactsContract.AUTHORITY, changes)
|
||||||
for (result in results) {
|
for (result in results) {
|
||||||
Log.i("[Native Contact Editor] Result is $result")
|
Log.i("[Native Contact Editor] Result is $result")
|
||||||
if (linphoneRawId == null && useLinphoneSyncAccount && result?.uri != null) {
|
if (syncAccountRawId == null && result?.uri != null) {
|
||||||
linphoneRawId = ContentUris.parseId(result.uri).toString()
|
syncAccountRawId = ContentUris.parseId(result.uri).toString()
|
||||||
Log.i("[Native Contact Editor] Linphone raw id is $linphoneRawId")
|
Log.i("[Native Contact Editor] Sync account raw id is $syncAccountRawId")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -362,20 +347,6 @@ class NativeContactEditor(
|
||||||
addChanges(delete)
|
addChanges(delete)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addLinphoneAddress(address: String, detail: String) {
|
|
||||||
val insert = ContentProviderOperation.newInsert(contactUri)
|
|
||||||
.withValue(ContactsContract.Data.RAW_CONTACT_ID, linphoneRawId)
|
|
||||||
.withValue(
|
|
||||||
ContactsContract.Data.MIMETYPE,
|
|
||||||
AppUtils.getString(R.string.linphone_address_mime_type)
|
|
||||||
)
|
|
||||||
.withValue("data1", address) // value
|
|
||||||
.withValue("data2", AppUtils.getString(R.string.app_name)) // summary
|
|
||||||
.withValue("data3", detail) // detail
|
|
||||||
.build()
|
|
||||||
addChanges(insert)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addSipAddress(address: String) {
|
private fun addSipAddress(address: String) {
|
||||||
val insert = ContentProviderOperation.newInsert(contactUri)
|
val insert = ContentProviderOperation.newInsert(contactUri)
|
||||||
.withValue(ContactsContract.Data.RAW_CONTACT_ID, rawId)
|
.withValue(ContactsContract.Data.RAW_CONTACT_ID, rawId)
|
||||||
|
@ -431,7 +402,7 @@ class NativeContactEditor(
|
||||||
ContactsContract.Data.CONTENT_URI,
|
ContactsContract.Data.CONTENT_URI,
|
||||||
arrayOf("data1"),
|
arrayOf("data1"),
|
||||||
"${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? AND data3 = ?",
|
"${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? AND data3 = ?",
|
||||||
arrayOf(linphoneRawId, AppUtils.getString(R.string.linphone_address_mime_type), phoneNumber),
|
arrayOf(syncAccountRawId, AppUtils.getString(R.string.linphone_address_mime_type), phoneNumber),
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
val count = cursor?.count ?: 0
|
val count = cursor?.count ?: 0
|
||||||
|
@ -444,7 +415,7 @@ class NativeContactEditor(
|
||||||
|
|
||||||
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 & SIP address, let's add it")
|
||||||
addLinphoneAddress(sipAddress, phoneNumber)
|
addPresenceLinphoneSipAddressForPhoneNumber(sipAddress, phoneNumber)
|
||||||
} else {
|
} else {
|
||||||
if (data1 != null && data1 == sipAddress) {
|
if (data1 != null && data1 == sipAddress) {
|
||||||
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")
|
||||||
|
@ -455,6 +426,20 @@ class NativeContactEditor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun addPresenceLinphoneSipAddressForPhoneNumber(address: String, detail: String) {
|
||||||
|
val insert = ContentProviderOperation.newInsert(contactUri)
|
||||||
|
.withValue(ContactsContract.Data.RAW_CONTACT_ID, syncAccountRawId)
|
||||||
|
.withValue(
|
||||||
|
ContactsContract.Data.MIMETYPE,
|
||||||
|
AppUtils.getString(R.string.linphone_address_mime_type)
|
||||||
|
)
|
||||||
|
.withValue("data1", address) // value
|
||||||
|
.withValue("data2", AppUtils.getString(R.string.app_name)) // summary
|
||||||
|
.withValue("data3", detail) // detail
|
||||||
|
.build()
|
||||||
|
addChanges(insert)
|
||||||
|
}
|
||||||
|
|
||||||
private fun updatePresenceLinphoneSipAddressForPhoneNumber(
|
private fun updatePresenceLinphoneSipAddressForPhoneNumber(
|
||||||
sipAddress: String,
|
sipAddress: String,
|
||||||
phoneNumber: String
|
phoneNumber: String
|
||||||
|
@ -479,26 +464,6 @@ class NativeContactEditor(
|
||||||
addChanges(update)
|
addChanges(update)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setPresenceSipAddress(sipAddress: String) {
|
|
||||||
val contentResolver = coreContext.context.contentResolver
|
|
||||||
val cursor = contentResolver.query(
|
|
||||||
ContactsContract.Data.CONTENT_URI,
|
|
||||||
arrayOf("data1"),
|
|
||||||
"${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? AND data1 = ?",
|
|
||||||
arrayOf(rawId, CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE, sipAddress),
|
|
||||||
null
|
|
||||||
)
|
|
||||||
val count = cursor?.count ?: 0
|
|
||||||
cursor?.close()
|
|
||||||
|
|
||||||
if (count == 0) {
|
|
||||||
Log.i("[Native Contact Editor] SIP address not found, let's add it")
|
|
||||||
addSipAddress(sipAddress)
|
|
||||||
} else {
|
|
||||||
Log.d("[Native Contact Editor] There is already an entry for this SIP address, skipping")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updatePicture() {
|
private fun updatePicture() {
|
||||||
val value = pictureByteArray
|
val value = pictureByteArray
|
||||||
val id = rawId
|
val id = rawId
|
||||||
|
|
|
@ -227,11 +227,6 @@ class CorePreferences constructor(private val context: Context) {
|
||||||
val contactOrganizationVisible: Boolean
|
val contactOrganizationVisible: Boolean
|
||||||
get() = config.getBool("app", "display_contact_organization", true)
|
get() = config.getBool("app", "display_contact_organization", true)
|
||||||
|
|
||||||
// If enabled, SIP addresses will be stored in a different raw id than the contact and with a custom MIME type
|
|
||||||
// If disabled, contacts will be created in default account & addresses will be stored in native SIP field
|
|
||||||
val useLinphoneSyncAccount: Boolean
|
|
||||||
get() = config.getBool("app", "use_linphone_tag", true)
|
|
||||||
|
|
||||||
private val darkModeAllowed: Boolean
|
private val darkModeAllowed: Boolean
|
||||||
get() = config.getBool("app", "dark_mode_allowed", true)
|
get() = config.getBool("app", "dark_mode_allowed", true)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue