mirror of
https://github.com/etesync/android
synced 2024-12-23 15:18:14 +00:00
AddressBooksSyncAdapter: implement syncing etebase address books
This commit is contained in:
parent
d6a0958d16
commit
deb1bb831b
@ -20,6 +20,7 @@ import android.provider.ContactsContract.Groups
|
|||||||
import android.provider.ContactsContract.RawContacts
|
import android.provider.ContactsContract.RawContacts
|
||||||
import at.bitfire.vcard4android.*
|
import at.bitfire.vcard4android.*
|
||||||
import com.etesync.syncadapter.App
|
import com.etesync.syncadapter.App
|
||||||
|
import com.etesync.syncadapter.CachedCollection
|
||||||
import com.etesync.syncadapter.log.Logger
|
import com.etesync.syncadapter.log.Logger
|
||||||
import com.etesync.syncadapter.model.CollectionInfo
|
import com.etesync.syncadapter.model.CollectionInfo
|
||||||
import com.etesync.syncadapter.model.JournalEntity
|
import com.etesync.syncadapter.model.JournalEntity
|
||||||
@ -70,6 +71,35 @@ class LocalAddressBook(
|
|||||||
return addressBook
|
return addressBook
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun create(context: Context, provider: ContentProviderClient, mainAccount: Account, cachedCollection: CachedCollection): LocalAddressBook {
|
||||||
|
val col = cachedCollection.col
|
||||||
|
val accountManager = AccountManager.get(context)
|
||||||
|
|
||||||
|
val account = Account(accountName(mainAccount, cachedCollection), App.addressBookAccountType)
|
||||||
|
val userData = initialUserData(mainAccount, col.uid)
|
||||||
|
Logger.log.log(Level.INFO, "Creating local address book $account", userData)
|
||||||
|
if (!accountManager.addAccountExplicitly(account, null, userData))
|
||||||
|
throw IllegalStateException("Couldn't create address book account")
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
||||||
|
// Android < 7 seems to lose the initial user data sometimes, so set it a second time
|
||||||
|
// https://forums.bitfire.at/post/11644
|
||||||
|
userData.keySet().forEach { key ->
|
||||||
|
accountManager.setUserData(account, key, userData.getString(key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val addressBook = LocalAddressBook(context, account, provider)
|
||||||
|
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true)
|
||||||
|
|
||||||
|
val values = ContentValues(2)
|
||||||
|
values.put(ContactsContract.Settings.SHOULD_SYNC, 1)
|
||||||
|
values.put(ContactsContract.Settings.UNGROUPED_VISIBLE, 1)
|
||||||
|
addressBook.settings = values
|
||||||
|
|
||||||
|
return addressBook
|
||||||
|
}
|
||||||
|
|
||||||
fun find(context: Context, provider: ContentProviderClient?, mainAccount: Account?) = AccountManager.get(context)
|
fun find(context: Context, provider: ContentProviderClient?, mainAccount: Account?) = AccountManager.get(context)
|
||||||
.getAccountsByType(App.addressBookAccountType)
|
.getAccountsByType(App.addressBookAccountType)
|
||||||
@ -103,6 +133,19 @@ class LocalAddressBook(
|
|||||||
return sb.toString()
|
return sb.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun accountName(mainAccount: Account, cachedCollection: CachedCollection): String {
|
||||||
|
val col = cachedCollection.col
|
||||||
|
val meta = cachedCollection.meta
|
||||||
|
val displayName = meta.name
|
||||||
|
val sb = StringBuilder(displayName)
|
||||||
|
sb.append(" (")
|
||||||
|
.append(mainAccount.name)
|
||||||
|
.append(" ")
|
||||||
|
.append(col.uid.substring(0, 4))
|
||||||
|
.append(")")
|
||||||
|
return sb.toString()
|
||||||
|
}
|
||||||
|
|
||||||
fun initialUserData(mainAccount: Account, url: String): Bundle {
|
fun initialUserData(mainAccount: Account, url: String): Bundle {
|
||||||
val bundle = Bundle(3)
|
val bundle = Bundle(3)
|
||||||
bundle.putString(USER_DATA_MAIN_ACCOUNT_NAME, mainAccount.name)
|
bundle.putString(USER_DATA_MAIN_ACCOUNT_NAME, mainAccount.name)
|
||||||
@ -181,6 +224,37 @@ class LocalAddressBook(
|
|||||||
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true)
|
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun update(cachedCollection: CachedCollection) {
|
||||||
|
val col = cachedCollection.col
|
||||||
|
val newAccountName = accountName(mainAccount, cachedCollection)
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
if (account.name != newAccountName && Build.VERSION.SDK_INT >= 21) {
|
||||||
|
val accountManager = AccountManager.get(context)
|
||||||
|
val future = accountManager.renameAccount(account, newAccountName, {
|
||||||
|
try {
|
||||||
|
// update raw contacts to new account name
|
||||||
|
if (provider != null) {
|
||||||
|
val values = ContentValues(1)
|
||||||
|
values.put(RawContacts.ACCOUNT_NAME, newAccountName)
|
||||||
|
provider.update(syncAdapterURI(RawContacts.CONTENT_URI), values, RawContacts.ACCOUNT_NAME + "=? AND " + RawContacts.ACCOUNT_TYPE + "=?",
|
||||||
|
arrayOf(account.name, account.type))
|
||||||
|
}
|
||||||
|
} catch (e: RemoteException) {
|
||||||
|
Logger.log.log(Level.WARNING, "Couldn't re-assign contacts to new account name", e)
|
||||||
|
}
|
||||||
|
}, null)
|
||||||
|
account = future.result
|
||||||
|
}
|
||||||
|
|
||||||
|
readOnly = col.accessLevel == "ro"
|
||||||
|
Logger.log.info("Address book write permission? = ${!readOnly}")
|
||||||
|
|
||||||
|
// make sure it will still be synchronized when contacts are updated
|
||||||
|
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true)
|
||||||
|
}
|
||||||
|
|
||||||
fun delete() {
|
fun delete() {
|
||||||
val accountManager = AccountManager.get(context)
|
val accountManager = AccountManager.get(context)
|
||||||
|
|
||||||
|
@ -15,10 +15,7 @@ import android.content.*
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.ContactsContract
|
import android.provider.ContactsContract
|
||||||
import at.bitfire.vcard4android.ContactsStorageException
|
import at.bitfire.vcard4android.ContactsStorageException
|
||||||
import com.etesync.syncadapter.AccountSettings
|
import com.etesync.syncadapter.*
|
||||||
import com.etesync.syncadapter.App
|
|
||||||
import com.etesync.syncadapter.Constants
|
|
||||||
import com.etesync.syncadapter.R
|
|
||||||
import com.etesync.syncadapter.log.Logger
|
import com.etesync.syncadapter.log.Logger
|
||||||
import com.etesync.syncadapter.model.CollectionInfo
|
import com.etesync.syncadapter.model.CollectionInfo
|
||||||
import com.etesync.syncadapter.model.JournalEntity
|
import com.etesync.syncadapter.model.JournalEntity
|
||||||
@ -53,7 +50,11 @@ class AddressBooksSyncAdapterService : SyncAdapterService() {
|
|||||||
|
|
||||||
RefreshCollections(account, CollectionInfo.Type.ADDRESS_BOOK).run()
|
RefreshCollections(account, CollectionInfo.Type.ADDRESS_BOOK).run()
|
||||||
|
|
||||||
updateLocalAddressBooks(contactsProvider, account)
|
if (settings.isLegacy) {
|
||||||
|
legacyUpdateLocalAddressBooks(contactsProvider, account)
|
||||||
|
} else {
|
||||||
|
updateLocalAddressBooks(contactsProvider, account, settings)
|
||||||
|
}
|
||||||
|
|
||||||
contactsProvider.release()
|
contactsProvider.release()
|
||||||
|
|
||||||
@ -69,9 +70,52 @@ class AddressBooksSyncAdapterService : SyncAdapterService() {
|
|||||||
Logger.log.info("Address book sync complete")
|
Logger.log.info("Address book sync complete")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateLocalAddressBooks(provider: ContentProviderClient, account: Account, settings: AccountSettings) {
|
||||||
|
val remote = HashMap<String, CachedCollection>()
|
||||||
|
val etebaseLocalCache = EtebaseLocalCache.getInstance(context, account.name)
|
||||||
|
val collections: List<CachedCollection>
|
||||||
|
synchronized(etebaseLocalCache) {
|
||||||
|
val httpClient = HttpClient.Builder(context, settings).setForeground(false).build()
|
||||||
|
val etebase = EtebaseLocalCache.getEtebase(context, httpClient.okHttpClient, settings)
|
||||||
|
val colMgr = etebase.collectionManager
|
||||||
|
|
||||||
|
collections = etebaseLocalCache.collectionList(colMgr).filter { it.meta.collectionType == Constants.ETEBASE_TYPE_ADDRESS_BOOK }
|
||||||
|
}
|
||||||
|
|
||||||
|
for (collection in collections) {
|
||||||
|
remote[collection.col.uid] = collection
|
||||||
|
}
|
||||||
|
|
||||||
|
val local = LocalAddressBook.find(context, provider, account)
|
||||||
|
|
||||||
|
val updateColors = settings.manageCalendarColors
|
||||||
|
|
||||||
|
// delete obsolete local calendar
|
||||||
|
for (addressBook in local) {
|
||||||
|
val url = addressBook.url
|
||||||
|
val collection = remote[url]
|
||||||
|
if (collection == null) {
|
||||||
|
Logger.log.fine("Deleting obsolete local addressBook $url")
|
||||||
|
addressBook.delete()
|
||||||
|
} else {
|
||||||
|
// remote CollectionInfo found for this local collection, update data
|
||||||
|
Logger.log.fine("Updating local addressBook $url")
|
||||||
|
addressBook.update(collection)
|
||||||
|
// we already have a local addressBook for this remote collection, don't take into consideration anymore
|
||||||
|
remote.remove(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create new local calendars
|
||||||
|
for (url in remote.keys) {
|
||||||
|
val cachedCollection = remote[url]!!
|
||||||
|
Logger.log.info("Adding local calendar list $cachedCollection")
|
||||||
|
LocalAddressBook.create(context, provider, account, cachedCollection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Throws(ContactsStorageException::class, AuthenticatorException::class, OperationCanceledException::class, IOException::class)
|
@Throws(ContactsStorageException::class, AuthenticatorException::class, OperationCanceledException::class, IOException::class)
|
||||||
private fun updateLocalAddressBooks(provider: ContentProviderClient, account: Account) {
|
private fun legacyUpdateLocalAddressBooks(provider: ContentProviderClient, account: Account) {
|
||||||
val context = context
|
val context = context
|
||||||
val data = (getContext().applicationContext as App).data
|
val data = (getContext().applicationContext as App).data
|
||||||
val service = JournalModel.Service.fetchOrCreate(data, account.name, CollectionInfo.Type.ADDRESS_BOOK)
|
val service = JournalModel.Service.fetchOrCreate(data, account.name, CollectionInfo.Type.ADDRESS_BOOK)
|
||||||
|
Loading…
Reference in New Issue
Block a user