Contacts creator improvements to be able to choose where to store newly created contact
This commit is contained in:
parent
c28f2373d9
commit
03f0f49c71
16 changed files with 329 additions and 98 deletions
|
@ -0,0 +1,44 @@
|
|||
package org.linphone.activities.main.contact.adapters
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.BaseAdapter
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import kotlin.collections.ArrayList
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.R
|
||||
|
||||
class SyncAccountAdapter : BaseAdapter() {
|
||||
private var accounts: ArrayList<Triple<String, String, Drawable>> = arrayListOf()
|
||||
|
||||
init {
|
||||
accounts.addAll(coreContext.contactsManager.getAvailableSyncAccounts())
|
||||
}
|
||||
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
val view: View = convertView ?: LayoutInflater.from(parent.context).inflate(R.layout.contact_sync_account_picker_cell, parent, false)
|
||||
val account = getItem(position)
|
||||
|
||||
val icon = view.findViewById<ImageView>(R.id.account_icon)
|
||||
icon.setImageDrawable(account.third)
|
||||
val name = view.findViewById<TextView>(R.id.account_name)
|
||||
name.text = account.first
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
override fun getItem(position: Int): Triple<String, String, Drawable> {
|
||||
return accounts[position]
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
return position.toLong()
|
||||
}
|
||||
|
||||
override fun getCount(): Int {
|
||||
return accounts.size
|
||||
}
|
||||
}
|
|
@ -47,7 +47,7 @@ import org.linphone.utils.FileUtils
|
|||
import org.linphone.utils.ImageUtils
|
||||
import org.linphone.utils.PermissionHelper
|
||||
|
||||
class ContactEditorFragment : Fragment() {
|
||||
class ContactEditorFragment : Fragment(), SyncAccountPickerFragment.SyncAccountPickedListener {
|
||||
private lateinit var binding: ContactEditorFragmentBinding
|
||||
private lateinit var viewModel: ContactEditorViewModel
|
||||
private lateinit var sharedViewModel: SharedMainViewModel
|
||||
|
@ -86,13 +86,11 @@ class ContactEditorFragment : Fragment() {
|
|||
}
|
||||
|
||||
binding.setSaveChangesClickListener {
|
||||
val savedContact = viewModel.save()
|
||||
if (savedContact is NativeContact) {
|
||||
savedContact.syncValuesFromAndroidContact(requireContext())
|
||||
Log.i("[Contact Editor] Displaying contact $savedContact")
|
||||
navigateToContact(savedContact)
|
||||
if (viewModel.c == null) {
|
||||
Log.i("[Contact Editor] New contact, ask user where to store it")
|
||||
SyncAccountPickerFragment(this).show(childFragmentManager, "SyncAccountPicker")
|
||||
} else {
|
||||
findNavController().popBackStack()
|
||||
saveContact()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,6 +112,13 @@ class ContactEditorFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
override fun onSyncAccountClicked(name: String?, type: String?) {
|
||||
Log.i("[Contact Editor] Using account $name / $type")
|
||||
viewModel.syncAccountName = name
|
||||
viewModel.syncAccountType = type
|
||||
saveContact()
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<out String>,
|
||||
|
@ -142,6 +147,17 @@ class ContactEditorFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun saveContact() {
|
||||
val savedContact = viewModel.save()
|
||||
if (savedContact is NativeContact) {
|
||||
savedContact.syncValuesFromAndroidContact(requireContext())
|
||||
Log.i("[Contact Editor] Displaying contact $savedContact")
|
||||
navigateToContact(savedContact)
|
||||
} else {
|
||||
findNavController().popBackStack()
|
||||
}
|
||||
}
|
||||
|
||||
private fun pickFile() {
|
||||
val cameraIntents = ArrayList<Intent>()
|
||||
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-android
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.linphone.activities.main.contact.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.*
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import org.linphone.R
|
||||
import org.linphone.activities.main.contact.adapters.SyncAccountAdapter
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.databinding.ContactSyncAccountPickerFragmentBinding
|
||||
|
||||
class SyncAccountPickerFragment(private val listener: SyncAccountPickedListener) : DialogFragment() {
|
||||
private lateinit var binding: ContactSyncAccountPickerFragmentBinding
|
||||
private lateinit var adapter: SyncAccountAdapter
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NO_TITLE, R.style.assistant_country_dialog_style)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = ContactSyncAccountPickerFragmentBinding.inflate(inflater, container, false)
|
||||
|
||||
adapter = SyncAccountAdapter()
|
||||
binding.accountsList.adapter = adapter
|
||||
|
||||
binding.accountsList.setOnItemClickListener { _, _, position, _ ->
|
||||
if (position >= 0 && position < adapter.count) {
|
||||
val account = adapter.getItem(position)
|
||||
Log.i("[Sync Account Picker] Picked ${account.first} / ${account.second}")
|
||||
listener.onSyncAccountClicked(account.first, account.second)
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
|
||||
binding.setLocalSyncAccountClickListener {
|
||||
Log.i("[Sync Account Picker] Picked local account")
|
||||
listener.onSyncAccountClicked(null, null)
|
||||
dismiss()
|
||||
}
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
interface SyncAccountPickedListener {
|
||||
fun onSyncAccountClicked(name: String?, type: String?)
|
||||
}
|
||||
}
|
|
@ -29,10 +29,8 @@ import java.io.ByteArrayOutputStream
|
|||
import java.io.IOException
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.LinphoneApplication.Companion.corePreferences
|
||||
import org.linphone.R
|
||||
import org.linphone.contact.*
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.utils.AppUtils
|
||||
import org.linphone.utils.ImageUtils
|
||||
import org.linphone.utils.PermissionHelper
|
||||
|
||||
|
@ -66,6 +64,9 @@ class ContactEditorViewModel(val c: Contact?) : ViewModel(), ContactViewModelInt
|
|||
|
||||
val addresses = MutableLiveData<ArrayList<NumberOrAddressEditorViewModel>>()
|
||||
|
||||
var syncAccountName: String? = null
|
||||
var syncAccountType: String? = null
|
||||
|
||||
init {
|
||||
if (c != null) {
|
||||
contact.value = c
|
||||
|
@ -83,12 +84,7 @@ class ContactEditorViewModel(val c: Contact?) : ViewModel(), ContactViewModelInt
|
|||
if (contact == null) {
|
||||
created = true
|
||||
contact = if (PermissionHelper.get().hasWriteContactsPermission()) {
|
||||
// Store native contact in default sync account
|
||||
if (corePreferences.storeCreatedContactsInAppSyncAccount) {
|
||||
NativeContact(NativeContactEditor.createAndroidContact(AppUtils.getString(R.string.sync_account_name), AppUtils.getString(R.string.sync_account_type)).toString())
|
||||
} else {
|
||||
NativeContact(NativeContactEditor.createAndroidContact(null, null).toString())
|
||||
}
|
||||
NativeContact(NativeContactEditor.createAndroidContact(syncAccountName, syncAccountType).toString())
|
||||
} else {
|
||||
Contact()
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ class ContactViewModel(private val c: Contact) : ErrorReportingViewModel(), Cont
|
|||
override fun onContactUpdated(contact: Contact) {
|
||||
if (c is NativeContact && contact is NativeContact && c.nativeId == contact.nativeId) {
|
||||
Log.d("[Contact] $contact has changed")
|
||||
updateNumbersAndAddresses()
|
||||
updateNumbersAndAddresses(contact)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ class ContactViewModel(private val c: Contact) : ErrorReportingViewModel(), Cont
|
|||
|
||||
init {
|
||||
contact.value = c
|
||||
updateNumbersAndAddresses()
|
||||
updateNumbersAndAddresses(c)
|
||||
coreContext.contactsManager.addListener(contactsUpdatedListener)
|
||||
waitForChatRoomCreation.value = false
|
||||
}
|
||||
|
@ -162,24 +162,24 @@ class ContactViewModel(private val c: Contact) : ErrorReportingViewModel(), Cont
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateNumbersAndAddresses() {
|
||||
private fun updateNumbersAndAddresses(contact: Contact) {
|
||||
val list = arrayListOf<ContactNumberOrAddressViewModel>()
|
||||
for (address in c.sipAddresses) {
|
||||
for (address in contact.sipAddresses) {
|
||||
val value = address.asStringUriOnly()
|
||||
val presenceModel = c.friend?.getPresenceModelForUriOrTel(value)
|
||||
val presenceModel = contact.friend?.getPresenceModelForUriOrTel(value)
|
||||
val hasPresence = presenceModel?.basicStatus == PresenceBasicStatus.Open
|
||||
val isMe = coreContext.core.defaultProxyConfig?.identityAddress?.weakEqual(address) ?: false
|
||||
val secureChatAllowed = !isMe && c.friend?.getPresenceModelForUriOrTel(value)?.hasCapability(FriendCapability.LimeX3Dh) ?: false
|
||||
val secureChatAllowed = !isMe && contact.friend?.getPresenceModelForUriOrTel(value)?.hasCapability(FriendCapability.LimeX3Dh) ?: false
|
||||
val noa = ContactNumberOrAddressViewModel(address, hasPresence, LinphoneUtils.getDisplayName(address), showSecureChat = secureChatAllowed, listener = listener)
|
||||
list.add(noa)
|
||||
}
|
||||
for (number in c.phoneNumbers) {
|
||||
val presenceModel = c.friend?.getPresenceModelForUriOrTel(number)
|
||||
for (number in contact.phoneNumbers) {
|
||||
val presenceModel = contact.friend?.getPresenceModelForUriOrTel(number)
|
||||
val hasPresence = presenceModel != null && presenceModel.basicStatus == PresenceBasicStatus.Open
|
||||
val contactAddress = presenceModel?.contact ?: number
|
||||
val address = coreContext.core.interpretUrl(contactAddress)
|
||||
val isMe = if (address != null) coreContext.core.defaultProxyConfig?.identityAddress?.weakEqual(address) ?: false else false
|
||||
val secureChatAllowed = !isMe && c.friend?.getPresenceModelForUriOrTel(number)?.hasCapability(FriendCapability.LimeX3Dh) ?: false
|
||||
val secureChatAllowed = !isMe && contact.friend?.getPresenceModelForUriOrTel(number)?.hasCapability(FriendCapability.LimeX3Dh) ?: false
|
||||
val noa = ContactNumberOrAddressViewModel(address, hasPresence, number, isSip = false, showSecureChat = secureChatAllowed, listener = listener)
|
||||
list.add(noa)
|
||||
}
|
||||
|
|
|
@ -38,13 +38,6 @@ class ContactsSettingsViewModel : GenericSettingsViewModel() {
|
|||
}
|
||||
val friendListSubscribe = MutableLiveData<Boolean>()
|
||||
|
||||
val contactStorageAppSyncAccountListener = object : SettingListenerStub() {
|
||||
override fun onBoolValueChanged(newValue: Boolean) {
|
||||
prefs.storeCreatedContactsInAppSyncAccount = newValue
|
||||
}
|
||||
}
|
||||
val contactStorageAppSyncAccount = MutableLiveData<Boolean>()
|
||||
|
||||
val nativePresenceListener = object : SettingListenerStub() {
|
||||
override fun onBoolValueChanged(newValue: Boolean) {
|
||||
if (newValue) {
|
||||
|
@ -80,7 +73,6 @@ class ContactsSettingsViewModel : GenericSettingsViewModel() {
|
|||
readContactsPermissionGranted.value = PermissionHelper.get().hasReadContactsPermission()
|
||||
|
||||
friendListSubscribe.value = core.isFriendListSubscriptionEnabled
|
||||
contactStorageAppSyncAccount.value = prefs.storeCreatedContactsInAppSyncAccount
|
||||
nativePresence.value = prefs.storePresenceInNativeContact
|
||||
showOrganization.value = prefs.displayOrganization
|
||||
launcherShortcuts.value = prefs.contactsShortcuts
|
||||
|
|
|
@ -163,4 +163,8 @@ open class Contact : Comparable<Contact> {
|
|||
if (presenceModel != null && presenceModel.basicStatus == PresenceBasicStatus.Open) return presenceModel.contact
|
||||
return null
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "${super.toString()}: name [$fullName]"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,9 +21,11 @@ package org.linphone.contact
|
|||
|
||||
import android.accounts.Account
|
||||
import android.accounts.AccountManager
|
||||
import android.accounts.AuthenticatorDescription
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.database.ContentObserver
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.os.AsyncTask
|
||||
import android.os.AsyncTask.THREAD_POOL_EXECUTOR
|
||||
|
@ -193,7 +195,10 @@ class ContactsManager(private val context: Context) {
|
|||
|
||||
// Restart contacts async fetching
|
||||
fetchContactsAsync()
|
||||
} else {
|
||||
Log.d("[Contacts Manager] Found contact with id [$id]: ${found?.fullName}")
|
||||
}
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
|
@ -249,7 +254,11 @@ class ContactsManager(private val context: Context) {
|
|||
val accountManager = context.getSystemService(Context.ACCOUNT_SERVICE) as AccountManager
|
||||
val accounts = accountManager.getAccountsByType(context.getString(R.string.sync_account_type))
|
||||
if (accounts.isEmpty()) {
|
||||
val newAccount = Account(context.getString(R.string.sync_account_name), context.getString(R.string.sync_account_type))
|
||||
val newAccount = Account(
|
||||
context.getString(R.string.sync_account_name), context.getString(
|
||||
R.string.sync_account_type
|
||||
)
|
||||
)
|
||||
try {
|
||||
accountManager.addAccountExplicitly(newAccount, null, null)
|
||||
Log.i("[Contacts Manager] Contact account added")
|
||||
|
@ -263,19 +272,28 @@ class ContactsManager(private val context: Context) {
|
|||
}
|
||||
}
|
||||
|
||||
fun getAvailableSyncAccounts(): List<Pair<String, String>> {
|
||||
fun getAvailableSyncAccounts(): List<Triple<String, String, Drawable>> {
|
||||
val accountManager = context.getSystemService(Context.ACCOUNT_SERVICE) as AccountManager
|
||||
val packageManager = context.packageManager
|
||||
val syncAdapters = ContentResolver.getSyncAdapterTypes()
|
||||
val available = arrayListOf<Pair<String, String>>()
|
||||
val authenticators: Array<AuthenticatorDescription> = accountManager.authenticatorTypes
|
||||
val available = arrayListOf<Triple<String, String, Drawable>>()
|
||||
|
||||
for (syncAdapter in syncAdapters) {
|
||||
if (syncAdapter.authority == "com.android.contacts" && syncAdapter.isUserVisible) {
|
||||
Log.i("[Contacts Manager] Found sync adapter for com.android.contacts authority: ${syncAdapter.accountType}")
|
||||
val accounts = accountManager.getAccountsByType(syncAdapter.accountType)
|
||||
for (account in accounts) {
|
||||
Log.i("[Contacts Manager] Found account for account type ${syncAdapter.accountType}: ${account.name}")
|
||||
val pair = Pair(account.name, account.type)
|
||||
available.add(pair)
|
||||
if (syncAdapter.supportsUploading() || syncAdapter.accountType == context.getString(R.string.sync_account_type)) {
|
||||
Log.i("[Contacts Manager] Found sync adapter for com.android.contacts authority: ${syncAdapter.accountType}")
|
||||
val accounts = accountManager.getAccountsByType(syncAdapter.accountType)
|
||||
for (account in accounts) {
|
||||
Log.i("[Contacts Manager] Found account for account type ${syncAdapter.accountType}: ${account.name}")
|
||||
for (authenticator in authenticators) {
|
||||
if (authenticator.type == account.type) {
|
||||
val drawable = packageManager.getDrawable(authenticator.packageName, authenticator.smallIconId, null)
|
||||
val triple = Triple(account.name, account.type, drawable)
|
||||
available.add(triple)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -337,12 +355,15 @@ class ContactsManager(private val context: Context) {
|
|||
val sipAddress = contact.getContactForPhoneNumberOrAddress(phoneNumber)
|
||||
if (sipAddress != null) {
|
||||
Log.d("[Contacts Manager] Found presence information to store in native contact $contact under Linphone sync account")
|
||||
val contactEditor = NativeContactEditor(contact, context.getString(R.string.sync_account_name), context.getString(R.string.sync_account_type))
|
||||
val contactEditor = NativeContactEditor(contact)
|
||||
val coroutineScope = CoroutineScope(Dispatchers.Main)
|
||||
coroutineScope.launch {
|
||||
val deferred = async {
|
||||
withContext(Dispatchers.IO) {
|
||||
contactEditor.ensureSyncAccountRawIdExists().setPresenceInformation(phoneNumber, sipAddress).commit()
|
||||
contactEditor.setPresenceInformation(
|
||||
phoneNumber,
|
||||
sipAddress
|
||||
).commit()
|
||||
}
|
||||
}
|
||||
deferred.await()
|
||||
|
|
|
@ -227,4 +227,8 @@ class NativeContact(val nativeId: String, private val lookupKey: String? = null)
|
|||
cursor.close()
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "${super.toString()}: id [$nativeId], name [$fullName]"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ package org.linphone.contact
|
|||
|
||||
import android.content.ContentProviderOperation
|
||||
import android.content.ContentUris
|
||||
import android.content.ContentValues
|
||||
import android.net.Uri
|
||||
import android.provider.ContactsContract
|
||||
import android.provider.ContactsContract.CommonDataKinds
|
||||
|
@ -33,20 +32,33 @@ import org.linphone.core.tools.Log
|
|||
import org.linphone.utils.AppUtils
|
||||
import org.linphone.utils.PermissionHelper
|
||||
|
||||
class NativeContactEditor(
|
||||
val contact: NativeContact,
|
||||
private var syncAccountName: String? = null,
|
||||
private var syncAccountType: String? = null
|
||||
) {
|
||||
class NativeContactEditor(val contact: NativeContact) {
|
||||
companion object {
|
||||
fun createAndroidContact(accountName: String?, accountType: String?): Long {
|
||||
val values = ContentValues()
|
||||
values.put(RawContacts.ACCOUNT_NAME, accountName)
|
||||
values.put(RawContacts.ACCOUNT_TYPE, accountType)
|
||||
Log.i("[Native Contact Editor] Using sync account $accountName with type $accountType")
|
||||
|
||||
val rawContactUri = coreContext.context.contentResolver
|
||||
.insert(RawContacts.CONTENT_URI, values)
|
||||
return ContentUris.parseId(rawContactUri)
|
||||
val changes = arrayListOf<ContentProviderOperation>()
|
||||
changes.add(
|
||||
ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
|
||||
.withValue(RawContacts.ACCOUNT_NAME, accountName)
|
||||
.withValue(RawContacts.ACCOUNT_TYPE, accountType)
|
||||
.build()
|
||||
)
|
||||
val contentResolver = coreContext.context.contentResolver
|
||||
val results = contentResolver.applyBatch(ContactsContract.AUTHORITY, changes)
|
||||
for (result in results) {
|
||||
Log.i("[Native Contact Editor] Contact creation result is ${result.uri}")
|
||||
val cursor = contentResolver.query(result.uri, arrayOf(RawContacts.CONTACT_ID), null, null, null)
|
||||
if (cursor != null) {
|
||||
cursor.moveToNext()
|
||||
val contactId: Long = cursor.getLong(0)
|
||||
Log.i("[Native Contact Editor] New contact id is $contactId")
|
||||
cursor.close()
|
||||
return contactId
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,8 +67,6 @@ class NativeContactEditor(
|
|||
"${ContactsContract.Data.CONTACT_ID} =? AND ${ContactsContract.Data.MIMETYPE} =?"
|
||||
private val phoneNumberSelection =
|
||||
"$selection AND (${CommonDataKinds.Phone.NUMBER}=? OR ${CommonDataKinds.Phone.NORMALIZED_NUMBER}=?)"
|
||||
private val sipAddressSelection =
|
||||
"${ContactsContract.Data.CONTACT_ID} =? AND (${ContactsContract.Data.MIMETYPE} =? OR ${ContactsContract.Data.MIMETYPE} =?) AND data1=?"
|
||||
private val presenceUpdateSelection =
|
||||
"${ContactsContract.Data.CONTACT_ID} =? AND ${ContactsContract.Data.MIMETYPE} =? AND data3=?"
|
||||
private val contactUri = ContactsContract.Data.CONTENT_URI
|
||||
|
@ -66,11 +76,10 @@ class NativeContactEditor(
|
|||
private var pictureByteArray: ByteArray? = null
|
||||
|
||||
init {
|
||||
Log.d("[Native Contact Editor] Using sync account $syncAccountName with type $syncAccountType")
|
||||
val contentResolver = coreContext.context.contentResolver
|
||||
val cursor = contentResolver.query(
|
||||
RawContacts.CONTENT_URI,
|
||||
arrayOf(RawContacts._ID, RawContacts.ACCOUNT_TYPE),
|
||||
arrayOf(RawContacts._ID),
|
||||
"${RawContacts.CONTACT_ID} =?",
|
||||
arrayOf(contact.nativeId),
|
||||
null
|
||||
|
@ -79,20 +88,11 @@ class NativeContactEditor(
|
|||
do {
|
||||
if (rawId == null) {
|
||||
rawId = cursor.getString(cursor.getColumnIndex(RawContacts._ID))
|
||||
Log.d("[Native Contact Editor] Found raw id $rawId for native contact with id ${contact.nativeId}")
|
||||
Log.i("[Native Contact Editor] Found raw id $rawId for native contact with id ${contact.nativeId}")
|
||||
}
|
||||
|
||||
val accountType = cursor.getString(cursor.getColumnIndex(RawContacts.ACCOUNT_TYPE))
|
||||
if (accountType == syncAccountType && syncAccountRawId == null) {
|
||||
syncAccountRawId = cursor.getString(cursor.getColumnIndex(RawContacts._ID))
|
||||
Log.d("[Native Contact Editor] Found sync account raw id $syncAccountRawId for native contact with id ${contact.nativeId}")
|
||||
}
|
||||
} while (cursor.moveToNext() && syncAccountRawId == null)
|
||||
} while (cursor.moveToNext() && rawId == null)
|
||||
}
|
||||
cursor?.close()
|
||||
|
||||
// When contact has been created with NativeContactEditor.createAndroidContact this is required
|
||||
if (rawId == null) rawId = contact.nativeId
|
||||
}
|
||||
|
||||
fun setFirstAndLastNames(firstName: String, lastName: String): NativeContactEditor {
|
||||
|
@ -237,12 +237,34 @@ class NativeContactEditor(
|
|||
return this
|
||||
}
|
||||
|
||||
fun ensureSyncAccountRawIdExists(): NativeContactEditor {
|
||||
fun setPresenceInformation(phoneNumber: String, sipAddress: String): NativeContactEditor {
|
||||
if (syncAccountRawId == null) {
|
||||
Log.w("[Native Contact Editor] Sync account raw id not found")
|
||||
val contentResolver = coreContext.context.contentResolver
|
||||
val cursor = contentResolver.query(
|
||||
RawContacts.CONTENT_URI,
|
||||
arrayOf(RawContacts._ID, RawContacts.ACCOUNT_TYPE),
|
||||
"${RawContacts.CONTACT_ID} =?",
|
||||
arrayOf(contact.nativeId),
|
||||
null
|
||||
)
|
||||
if (cursor?.moveToFirst() == true) {
|
||||
do {
|
||||
val accountType =
|
||||
cursor.getString(cursor.getColumnIndex(RawContacts.ACCOUNT_TYPE))
|
||||
if (accountType == AppUtils.getString(R.string.sync_account_type) && syncAccountRawId == null) {
|
||||
syncAccountRawId = cursor.getString(cursor.getColumnIndex(RawContacts._ID))
|
||||
Log.d("[Native Contact Editor] Found linphone raw id $syncAccountRawId for native contact with id ${contact.nativeId}")
|
||||
}
|
||||
} while (cursor.moveToNext() && syncAccountRawId == null)
|
||||
}
|
||||
cursor?.close()
|
||||
}
|
||||
|
||||
if (syncAccountRawId == null) {
|
||||
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.ACCOUNT_NAME, AppUtils.getString(R.string.sync_account_name))
|
||||
.withValue(RawContacts.ACCOUNT_TYPE, AppUtils.getString(R.string.sync_account_type))
|
||||
.withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DEFAULT)
|
||||
.build()
|
||||
addChanges(insert)
|
||||
|
@ -259,12 +281,9 @@ class NativeContactEditor(
|
|||
)
|
||||
.build()
|
||||
addChanges(update)
|
||||
commit()
|
||||
commit(true)
|
||||
}
|
||||
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
|
||||
|
@ -275,15 +294,15 @@ class NativeContactEditor(
|
|||
return this
|
||||
}
|
||||
|
||||
fun commit() {
|
||||
fun commit(updateSyncAccountRawId: Boolean = false) {
|
||||
if (PermissionHelper.get().hasWriteContactsPermission()) {
|
||||
try {
|
||||
if (changes.isNotEmpty()) {
|
||||
val contentResolver = coreContext.context.contentResolver
|
||||
val results = contentResolver.applyBatch(ContactsContract.AUTHORITY, changes)
|
||||
for (result in results) {
|
||||
Log.i("[Native Contact Editor] Result is $result")
|
||||
if (syncAccountRawId == null && result?.uri != null) {
|
||||
Log.i("[Native Contact Editor] Result is ${result.uri}")
|
||||
if (updateSyncAccountRawId && syncAccountRawId == null && result?.uri != null) {
|
||||
syncAccountRawId = ContentUris.parseId(result.uri).toString()
|
||||
Log.i("[Native Contact Editor] Sync account raw id is $syncAccountRawId")
|
||||
}
|
||||
|
@ -422,7 +441,11 @@ class NativeContactEditor(
|
|||
ContactsContract.Data.CONTENT_URI,
|
||||
arrayOf("data1"),
|
||||
"${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? AND data3 = ?",
|
||||
arrayOf(syncAccountRawId, AppUtils.getString(R.string.linphone_address_mime_type), phoneNumber),
|
||||
arrayOf(
|
||||
syncAccountRawId,
|
||||
AppUtils.getString(R.string.linphone_address_mime_type),
|
||||
phoneNumber
|
||||
),
|
||||
null
|
||||
)
|
||||
val count = cursor?.count ?: 0
|
||||
|
|
|
@ -119,12 +119,6 @@ class CorePreferences constructor(private val context: Context) {
|
|||
|
||||
/* Contacts */
|
||||
|
||||
var storeCreatedContactsInAppSyncAccount: Boolean
|
||||
get() = config.getBool("app", "store_contacts_in_app_sync_account", false)
|
||||
set(value) {
|
||||
config.setBool("app", "store_contacts_in_app_sync_account", value)
|
||||
}
|
||||
|
||||
var storePresenceInNativeContact: Boolean
|
||||
get() = config.getBool("app", "store_presence_in_native_contact", false)
|
||||
set(value) {
|
||||
|
|
22
app/src/main/res/layout/contact_sync_account_picker_cell.xml
Normal file
22
app/src/main/res/layout/contact_sync_account_picker_cell.xml
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:padding="5dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/account_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:adjustViewBounds="true"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/account_name"
|
||||
style="@style/standard_text_font"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingLeft="5dp" />
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<data>
|
||||
<import type="android.view.View"/>
|
||||
<variable
|
||||
name="localSyncAccountClickListener"
|
||||
type="android.view.View.OnClickListener"/>
|
||||
</data>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="10dp">
|
||||
|
||||
<TextView
|
||||
style="@style/standard_text_font"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical|center"
|
||||
android:singleLine="true"
|
||||
android:text="@string/contact_new_choose_sync_account" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ListView
|
||||
android:id="@+id/accountsList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:cacheColorHint="@color/transparent_color"
|
||||
android:divider="@color/light_grey_color"
|
||||
android:dividerHeight="1dp" />
|
||||
|
||||
<TextView
|
||||
android:onClick="@{localSyncAccountClickListener}"
|
||||
style="@style/button_font"
|
||||
android:padding="10dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/contact_local_sync_account"
|
||||
android:gravity="right" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</layout>
|
|
@ -72,14 +72,6 @@
|
|||
linphone:checked="@={viewModel.friendListSubscribe}"
|
||||
linphone:enabled="@{viewModel.readContactsPermissionGranted}"/>
|
||||
|
||||
<include
|
||||
layout="@layout/settings_widget_switch"
|
||||
linphone:title="@{@string/contacts_settings_contact_storage_title}"
|
||||
linphone:subtitle="@{@string/contacts_settings_contact_storage_summary}"
|
||||
linphone:listener="@{viewModel.contactStorageAppSyncAccountListener}"
|
||||
linphone:checked="@={viewModel.contactStorageAppSyncAccount}"
|
||||
linphone:enabled="@{viewModel.readContactsPermissionGranted}"/>
|
||||
|
||||
<include
|
||||
layout="@layout/settings_widget_switch"
|
||||
linphone:title="@{@string/contacts_settings_native_presence_title}"
|
||||
|
|
|
@ -107,6 +107,8 @@
|
|||
<item quantity="one">@string/contact_delete_one_dialog</item>
|
||||
<item quantity="other">@string/contact_delete_many_dialog</item>
|
||||
</plurals>
|
||||
<string name="contact_new_choose_sync_account">Choose where to save the contact</string>
|
||||
<string name="contact_local_sync_account">Store locally</string>
|
||||
|
||||
<!-- Dialer -->
|
||||
<string name="dialer_address_bar_hint">Enter a number or an address</string>
|
||||
|
@ -430,8 +432,6 @@
|
|||
<!-- Contacts settings -->
|
||||
<string name="contacts_settings_friendlist_subscribe_title">Friendlist subscribe</string>
|
||||
<string name="contacts_settings_friendlist_subscribe_summary"></string>
|
||||
<string name="contacts_settings_contact_storage_title">Store new contacts in the app sync account</string>
|
||||
<string name="contacts_settings_contact_storage_summary">If enabled, contacts created via the app will be removed when the app will be uninstalled.</string>
|
||||
<string name="contacts_settings_native_presence_title">Presence information in native contact</string>
|
||||
<string name="contacts_settings_native_presence_summary">Inserting information shortcuts from the &appName; contact into native Android contacts</string>
|
||||
<string name="contacts_settings_show_organization_title">Display contact organization</string>
|
||||
|
|
|
@ -3,4 +3,5 @@
|
|||
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accountType="@string/sync_account_type"
|
||||
android:icon="@mipmap/ic_launcher_round"
|
||||
android:smallIcon="@mipmap/ic_launcher_round"
|
||||
android:label="@string/app_name" />
|
Loading…
Reference in a new issue