mirror of
https://github.com/etesync/android
synced 2024-11-22 07:58:09 +00:00
snap
This commit is contained in:
parent
521cda35f5
commit
7209d634a5
@ -12,6 +12,7 @@ import android.accounts.AccountManager
|
|||||||
import android.accounts.AccountManagerCallback
|
import android.accounts.AccountManagerCallback
|
||||||
import android.accounts.AccountManagerFuture
|
import android.accounts.AccountManagerFuture
|
||||||
import android.accounts.AuthenticatorException
|
import android.accounts.AuthenticatorException
|
||||||
|
import android.annotation.TargetApi
|
||||||
import android.content.ContentProviderClient
|
import android.content.ContentProviderClient
|
||||||
import android.content.ContentResolver
|
import android.content.ContentResolver
|
||||||
import android.content.ContentUris
|
import android.content.ContentUris
|
||||||
@ -47,8 +48,78 @@ import at.bitfire.vcard4android.CachedGroupMembership
|
|||||||
import at.bitfire.vcard4android.ContactsStorageException
|
import at.bitfire.vcard4android.ContactsStorageException
|
||||||
|
|
||||||
|
|
||||||
class LocalAddressBook(protected val context: Context, account: Account, provider: ContentProviderClient?) : AndroidAddressBook(account, provider, LocalGroup.Factory.INSTANCE, LocalContact.Factory.INSTANCE), LocalCollection<LocalResource> {
|
class LocalAddressBook(
|
||||||
private val syncState = Bundle()
|
private val context: Context,
|
||||||
|
account: Account,
|
||||||
|
provider: ContentProviderClient?
|
||||||
|
): AndroidAddressBook<LocalContact, LocalGroup>(account, provider, LocalContact.Factory, LocalGroup.Factory), LocalCollection<LocalAddress> {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val USER_DATA_MAIN_ACCOUNT_TYPE = "real_account_type"
|
||||||
|
val USER_DATA_MAIN_ACCOUNT_NAME = "real_account_name"
|
||||||
|
val USER_DATA_URL = "url"
|
||||||
|
const val USER_DATA_READ_ONLY = "read_only"
|
||||||
|
|
||||||
|
fun create(context: Context, provider: ContentProviderClient, mainAccount: Account, journalEntity: JournalEntity): LocalAddressBook {
|
||||||
|
val info = journalEntity.info
|
||||||
|
val accountManager = AccountManager.get(context)
|
||||||
|
|
||||||
|
val account = Account(accountName(mainAccount, info), App.addressBookAccountType)
|
||||||
|
if (!accountManager.addAccountExplicitly(account, null, initialUserData(mainAccount, info.url.toString())))
|
||||||
|
throw ContactsStorageException("Couldn't create address book account")
|
||||||
|
|
||||||
|
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)
|
||||||
|
.getAccountsByType(App.addressBookAccountType)
|
||||||
|
.map { LocalAddressBook(context, it, provider) }
|
||||||
|
.filter { mainAccount == null || it.mainAccount == mainAccount }
|
||||||
|
.toList()
|
||||||
|
|
||||||
|
|
||||||
|
fun findByUid(context: Context, provider: ContentProviderClient, mainAccount: Account?, uid: String): LocalAddressBook? {
|
||||||
|
val accountManager = AccountManager.get(context)
|
||||||
|
|
||||||
|
for (account in accountManager.getAccountsByType(App.addressBookAccountType)) {
|
||||||
|
val addressBook = LocalAddressBook(context, account, provider)
|
||||||
|
if (addressBook.url == uid && (mainAccount == null || addressBook.mainAccount == mainAccount))
|
||||||
|
return addressBook
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// HELPERS
|
||||||
|
|
||||||
|
fun accountName(mainAccount: Account, info: CollectionInfo): String {
|
||||||
|
val displayName = if (info.displayName != null) info.displayName else info.uid
|
||||||
|
val sb = StringBuilder(displayName)
|
||||||
|
sb.append(" (")
|
||||||
|
.append(mainAccount.name)
|
||||||
|
.append(" ")
|
||||||
|
.append(info.uid!!.substring(0, 4))
|
||||||
|
.append(")")
|
||||||
|
return sb.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun initialUserData(mainAccount: Account, url: String): Bundle {
|
||||||
|
val bundle = Bundle(3)
|
||||||
|
bundle.putString(USER_DATA_MAIN_ACCOUNT_NAME, mainAccount.name)
|
||||||
|
bundle.putString(USER_DATA_MAIN_ACCOUNT_TYPE, mainAccount.type)
|
||||||
|
bundle.putString(USER_DATA_URL, url)
|
||||||
|
return bundle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether contact groups (LocalGroup resources) are included in query results for
|
* Whether contact groups (LocalGroup resources) are included in query results for
|
||||||
@ -57,100 +128,44 @@ class LocalAddressBook(protected val context: Context, account: Account, provide
|
|||||||
*/
|
*/
|
||||||
var includeGroups = true
|
var includeGroups = true
|
||||||
|
|
||||||
/**
|
private var _mainAccount: Account? = null
|
||||||
* Returns an array of local contacts/groups which have been deleted locally. (DELETED != 0).
|
|
||||||
*/
|
|
||||||
override val deleted: Array<LocalResource>
|
|
||||||
@Throws(ContactsStorageException::class)
|
|
||||||
get() {
|
|
||||||
val deleted = LinkedList<LocalResource>()
|
|
||||||
Collections.addAll(deleted, *deletedContacts)
|
|
||||||
if (includeGroups)
|
|
||||||
Collections.addAll(deleted, *deletedGroups)
|
|
||||||
return deleted.toTypedArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array of local contacts/groups which have been changed locally (DIRTY != 0).
|
|
||||||
*/
|
|
||||||
override val dirty: Array<LocalResource>
|
|
||||||
@Throws(ContactsStorageException::class)
|
|
||||||
get() {
|
|
||||||
val dirty = LinkedList<LocalResource>()
|
|
||||||
Collections.addAll(dirty, *dirtyContacts)
|
|
||||||
if (includeGroups)
|
|
||||||
Collections.addAll(dirty, *dirtyGroups)
|
|
||||||
return dirty.toTypedArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array of local contacts which don't have a file name yet.
|
|
||||||
*/
|
|
||||||
override val withoutFileName: Array<LocalResource>
|
|
||||||
@Throws(ContactsStorageException::class)
|
|
||||||
get() {
|
|
||||||
val nameless = LinkedList<LocalResource>()
|
|
||||||
Collections.addAll(nameless, *queryContacts(AndroidContact.COLUMN_FILENAME + " IS NULL", null) as Array<LocalContact>)
|
|
||||||
if (includeGroups)
|
|
||||||
Collections.addAll(nameless, *queryGroups(AndroidGroup.COLUMN_FILENAME + " IS NULL", null) as Array<LocalGroup>)
|
|
||||||
return nameless.toTypedArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
val deletedContacts: Array<LocalContact>
|
|
||||||
@Throws(ContactsStorageException::class)
|
|
||||||
get() = queryContacts(RawContacts.DELETED + "!= 0", null) as Array<LocalContact>
|
|
||||||
|
|
||||||
val dirtyContacts: Array<LocalContact>
|
|
||||||
@Throws(ContactsStorageException::class)
|
|
||||||
get() = queryContacts(RawContacts.DIRTY + "!= 0 AND " + RawContacts.DELETED + "== 0", null) as Array<LocalContact>
|
|
||||||
|
|
||||||
val all: Array<LocalContact>
|
|
||||||
@Throws(ContactsStorageException::class)
|
|
||||||
get() = queryContacts(RawContacts.DELETED + "== 0", null) as Array<LocalContact>
|
|
||||||
|
|
||||||
val deletedGroups: Array<LocalGroup>
|
|
||||||
@Throws(ContactsStorageException::class)
|
|
||||||
get() = queryGroups(Groups.DELETED + "!= 0", null) as Array<LocalGroup>
|
|
||||||
|
|
||||||
val dirtyGroups: Array<LocalGroup>
|
|
||||||
@Throws(ContactsStorageException::class)
|
|
||||||
get() = queryGroups(Groups.DIRTY + "!= 0 AND " + Groups.DELETED + "== 0", null) as Array<LocalGroup>
|
|
||||||
|
|
||||||
var mainAccount: Account
|
var mainAccount: Account
|
||||||
@Throws(ContactsStorageException::class)
|
|
||||||
get() {
|
get() {
|
||||||
val accountManager = AccountManager.get(context)
|
_mainAccount?.let { return it }
|
||||||
val name = accountManager.getUserData(account, USER_DATA_MAIN_ACCOUNT_NAME)
|
|
||||||
val type = accountManager.getUserData(account, USER_DATA_MAIN_ACCOUNT_TYPE)
|
AccountManager.get(context).let { accountManager ->
|
||||||
return if (name != null && type != null)
|
val name = accountManager.getUserData(account, USER_DATA_MAIN_ACCOUNT_NAME)
|
||||||
Account(name, type)
|
val type = accountManager.getUserData(account, USER_DATA_MAIN_ACCOUNT_TYPE)
|
||||||
else
|
if (name != null && type != null)
|
||||||
throw ContactsStorageException("Address book doesn't exist anymore")
|
return Account(name, type)
|
||||||
|
else
|
||||||
|
throw IllegalStateException("Address book doesn't exist anymore")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@Throws(ContactsStorageException::class)
|
set(newMainAccount) {
|
||||||
set(mainAccount) {
|
AccountManager.get(context).let { accountManager ->
|
||||||
val accountManager = AccountManager.get(context)
|
accountManager.setUserData(account, USER_DATA_MAIN_ACCOUNT_NAME, newMainAccount.name)
|
||||||
accountManager.setUserData(account, USER_DATA_MAIN_ACCOUNT_NAME, mainAccount.name)
|
accountManager.setUserData(account, USER_DATA_MAIN_ACCOUNT_TYPE, newMainAccount.type)
|
||||||
accountManager.setUserData(account, USER_DATA_MAIN_ACCOUNT_TYPE, mainAccount.type)
|
}
|
||||||
|
|
||||||
|
_mainAccount = newMainAccount
|
||||||
}
|
}
|
||||||
|
|
||||||
var url: String?
|
var url: String
|
||||||
@Throws(ContactsStorageException::class)
|
get() = AccountManager.get(context).getUserData(account, USER_DATA_URL)
|
||||||
get() {
|
?: throw IllegalStateException("Address book has no URL")
|
||||||
val accountManager = AccountManager.get(context)
|
set(url) = AccountManager.get(context).setUserData(account, USER_DATA_URL, url)
|
||||||
return accountManager.getUserData(account, USER_DATA_URL)
|
|
||||||
}
|
var readOnly: Boolean
|
||||||
@Throws(ContactsStorageException::class)
|
get() = AccountManager.get(context).getUserData(account, USER_DATA_READ_ONLY) != null
|
||||||
set(url) {
|
set(readOnly) = AccountManager.get(context).setUserData(account, USER_DATA_READ_ONLY, if (readOnly) "1" else null)
|
||||||
val accountManager = AccountManager.get(context)
|
|
||||||
accountManager.setUserData(account, USER_DATA_URL, url)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(AuthenticatorException::class, OperationCanceledException::class, IOException::class, ContactsStorageException::class, android.accounts.OperationCanceledException::class)
|
|
||||||
fun update(journalEntity: JournalEntity) {
|
fun update(journalEntity: JournalEntity) {
|
||||||
val info = journalEntity.info
|
val info = journalEntity.info
|
||||||
val newAccountName = accountName(mainAccount, info)
|
val newAccountName = accountName(mainAccount, info)
|
||||||
if (account.name != newAccountName && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
if (account.name != newAccountName && Build.VERSION.SDK_INT >= 21) {
|
||||||
val accountManager = AccountManager.get(context)
|
val accountManager = AccountManager.get(context)
|
||||||
val future = accountManager.renameAccount(account, newAccountName, {
|
val future = accountManager.renameAccount(account, newAccountName, {
|
||||||
try {
|
try {
|
||||||
@ -168,22 +183,61 @@ class LocalAddressBook(protected val context: Context, account: Account, provide
|
|||||||
account = future.result
|
account = future.result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
App.log.info("Address book write permission? = ${journalEntity.isReadOnly}")
|
||||||
|
readOnly = journalEntity.isReadOnly
|
||||||
|
|
||||||
// make sure it will still be synchronized when contacts are updated
|
// make sure it will still be synchronized when contacts are updated
|
||||||
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true)
|
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun delete() {
|
fun delete() {
|
||||||
val accountManager = AccountManager.get(context)
|
val accountManager = AccountManager.get(context)
|
||||||
AndroidCompat.removeAccount(accountManager, account)
|
@Suppress("DEPRECATION")
|
||||||
|
if (Build.VERSION.SDK_INT >= 22)
|
||||||
|
accountManager.removeAccount(account, null, null, null)
|
||||||
|
else
|
||||||
|
accountManager.removeAccount(account, null, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(ContactsStorageException::class, FileNotFoundException::class)
|
fun findAll(): List<LocalAddress> = queryContacts(RawContacts.DELETED + "== 0", null)
|
||||||
fun findContactByUID(uid: String): LocalContact {
|
|
||||||
val contacts = queryContacts(LocalContact.COLUMN_UID + "=?", arrayOf(uid)) as Array<LocalContact>
|
/**
|
||||||
if (contacts.size == 0)
|
* Returns an array of local contacts/groups which have been deleted locally. (DELETED != 0).
|
||||||
throw FileNotFoundException()
|
* @throws RemoteException on content provider errors
|
||||||
return contacts[0]
|
*/
|
||||||
}
|
override fun findDeleted() =
|
||||||
|
if (includeGroups)
|
||||||
|
findDeletedContacts() + findDeletedGroups()
|
||||||
|
else
|
||||||
|
findDeletedContacts()
|
||||||
|
|
||||||
|
fun findDeletedContacts() = queryContacts("${RawContacts.DELETED}!=0", null)
|
||||||
|
fun findDeletedGroups() = queryGroups("${Groups.DELETED}!=0", null)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of local contacts/groups which have been changed locally (DIRTY != 0).
|
||||||
|
* @throws RemoteException on content provider errors
|
||||||
|
*/
|
||||||
|
override fun findDirty() =
|
||||||
|
if (includeGroups)
|
||||||
|
findDirtyContacts() + findDirtyGroups()
|
||||||
|
else
|
||||||
|
findDirtyContacts()
|
||||||
|
|
||||||
|
fun findDirtyContacts() = queryContacts("${RawContacts.DIRTY}!=0", null)
|
||||||
|
fun findDirtyGroups() = queryGroups("${Groups.DIRTY}!=0", null)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of local contacts which don't have a file name yet.
|
||||||
|
*/
|
||||||
|
override fun findWithoutFileName() =
|
||||||
|
if (includeGroups)
|
||||||
|
findWithoutFileNameContacts() + findWithoutFileNameGroups()
|
||||||
|
else
|
||||||
|
findWithoutFileNameContacts()
|
||||||
|
|
||||||
|
fun findWithoutFileNameContacts() = queryContacts("${AndroidContact.COLUMN_FILENAME} IS NULL", null)
|
||||||
|
fun findWithoutFileNameGroups() = queryGroups("${AndroidGroup.COLUMN_FILENAME} IS NULL", null)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queries all contacts with DIRTY flag and checks whether their data checksum has changed, i.e.
|
* Queries all contacts with DIRTY flag and checks whether their data checksum has changed, i.e.
|
||||||
@ -192,52 +246,39 @@ class LocalAddressBook(protected val context: Context, account: Account, provide
|
|||||||
* whose contact data checksum has not changed.
|
* whose contact data checksum has not changed.
|
||||||
* @return number of "really dirty" contacts
|
* @return number of "really dirty" contacts
|
||||||
*/
|
*/
|
||||||
@Throws(ContactsStorageException::class)
|
|
||||||
fun verifyDirty(): Int {
|
fun verifyDirty(): Int {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N || Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||||
App.log.severe("verifyDirty() should not be called on Android <7")
|
throw IllegalStateException("verifyDirty() should not be called on Android != 7")
|
||||||
|
|
||||||
var reallyDirty = 0
|
var reallyDirty = 0
|
||||||
for (contact in dirtyContacts) {
|
for (contact in findDirtyContacts()) {
|
||||||
try {
|
val lastHash = contact.getLastHashCode()
|
||||||
val lastHash = contact.lastHashCode
|
val currentHash = contact.dataHashCode()
|
||||||
val currentHash = contact.dataHashCode()
|
if (lastHash == currentHash) {
|
||||||
if (lastHash == currentHash) {
|
// hash is code still the same, contact is not "really dirty" (only metadata been have changed)
|
||||||
// hash is code still the same, contact is not "really dirty" (only metadata been have changed)
|
App.log.log(Level.FINE, "Contact data hash has not changed, resetting dirty flag", contact)
|
||||||
App.log.log(Level.FINE, "Contact data hash has not changed, resetting dirty flag", contact)
|
contact.resetDirty()
|
||||||
contact.resetDirty()
|
} else {
|
||||||
} else {
|
App.log.log(Level.FINE, "Contact data has changed from hash $lastHash to $currentHash", contact)
|
||||||
App.log.log(Level.FINE, "Contact data has changed from hash $lastHash to $currentHash", contact)
|
reallyDirty++
|
||||||
reallyDirty++
|
|
||||||
}
|
|
||||||
} catch (e: FileNotFoundException) {
|
|
||||||
throw ContactsStorageException("Couldn't calculate hash code", e)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (includeGroups)
|
if (includeGroups)
|
||||||
reallyDirty += dirtyGroups.size
|
reallyDirty += findDirtyGroups().size
|
||||||
|
|
||||||
return reallyDirty
|
return reallyDirty
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(ContactsStorageException::class)
|
override fun findByUid(uid: String): LocalAddress? = findContactByUID(uid)
|
||||||
override fun getByUid(uid: String): LocalResource? {
|
|
||||||
val ret = queryContacts(AndroidContact.COLUMN_FILENAME + " =? ", arrayOf(uid)) as Array<LocalContact>
|
|
||||||
return if (ret != null && ret.size > 0) {
|
|
||||||
ret[0]
|
|
||||||
} else null
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(ContactsStorageException::class)
|
|
||||||
override fun count(): Long {
|
override fun count(): Long {
|
||||||
try {
|
try {
|
||||||
val cursor = provider.query(syncAdapterURI(RawContacts.CONTENT_URI), null, null, null, null)
|
val cursor = provider?.query(syncAdapterURI(RawContacts.CONTENT_URI), null, null, null, null)
|
||||||
try {
|
try {
|
||||||
return cursor.count.toLong()
|
return cursor?.count.toLong()
|
||||||
} finally {
|
} finally {
|
||||||
cursor.close()
|
cursor?.close()
|
||||||
}
|
}
|
||||||
} catch (e: RemoteException) {
|
} catch (e: RemoteException) {
|
||||||
throw ContactsStorageException("Couldn't query contacts", e)
|
throw ContactsStorageException("Couldn't query contacts", e)
|
||||||
@ -245,7 +286,6 @@ class LocalAddressBook(protected val context: Context, account: Account, provide
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(ContactsStorageException::class)
|
|
||||||
internal fun getByGroupMembership(groupID: Long): Array<LocalContact> {
|
internal fun getByGroupMembership(groupID: Long): Array<LocalContact> {
|
||||||
try {
|
try {
|
||||||
val cursor = provider.query(syncAdapterURI(ContactsContract.Data.CONTENT_URI),
|
val cursor = provider.query(syncAdapterURI(ContactsContract.Data.CONTENT_URI),
|
||||||
@ -271,11 +311,10 @@ class LocalAddressBook(protected val context: Context, account: Account, provide
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Throws(ContactsStorageException::class)
|
|
||||||
fun deleteAll() {
|
fun deleteAll() {
|
||||||
try {
|
try {
|
||||||
provider.delete(syncAdapterURI(RawContacts.CONTENT_URI), null, null)
|
provider?.delete(syncAdapterURI(RawContacts.CONTENT_URI), null, null)
|
||||||
provider.delete(syncAdapterURI(Groups.CONTENT_URI), null, null)
|
provider?.delete(syncAdapterURI(Groups.CONTENT_URI), null, null)
|
||||||
} catch (e: RemoteException) {
|
} catch (e: RemoteException) {
|
||||||
throw ContactsStorageException("Couldn't delete all local contacts and groups", e)
|
throw ContactsStorageException("Couldn't delete all local contacts and groups", e)
|
||||||
}
|
}
|
||||||
@ -283,57 +322,38 @@ class LocalAddressBook(protected val context: Context, account: Account, provide
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* special group operations */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the first group with the given title. If there is no group with this
|
* Finds the first group with the given title. If there is no group with this
|
||||||
* title, a new group is created.
|
* title, a new group is created.
|
||||||
* @param title title of the group to look for
|
* @param title title of the group to look for
|
||||||
* @return id of the group with given title
|
* @return id of the group with given title
|
||||||
* @throws ContactsStorageException on contact provider errors
|
* @throws RemoteException on content provider errors
|
||||||
*/
|
*/
|
||||||
@Throws(ContactsStorageException::class)
|
|
||||||
fun findOrCreateGroup(title: String): Long {
|
fun findOrCreateGroup(title: String): Long {
|
||||||
try {
|
provider!!.query(syncAdapterURI(Groups.CONTENT_URI), arrayOf(Groups._ID),
|
||||||
val cursor = provider.query(syncAdapterURI(Groups.CONTENT_URI),
|
"${Groups.TITLE}=?", arrayOf(title), null)?.use { cursor ->
|
||||||
arrayOf(Groups._ID),
|
if (cursor.moveToNext())
|
||||||
Groups.TITLE + "=?", arrayOf(title), null)
|
return cursor.getLong(0)
|
||||||
try {
|
|
||||||
if (cursor != null && cursor.moveToNext())
|
|
||||||
return cursor.getLong(0)
|
|
||||||
} finally {
|
|
||||||
cursor!!.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
val values = ContentValues()
|
|
||||||
values.put(Groups.TITLE, title)
|
|
||||||
val uri = provider.insert(syncAdapterURI(Groups.CONTENT_URI), values)
|
|
||||||
return ContentUris.parseId(uri)
|
|
||||||
} catch (e: RemoteException) {
|
|
||||||
throw ContactsStorageException("Couldn't find local contact group", e)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val values = ContentValues(1)
|
||||||
|
values.put(Groups.TITLE, title)
|
||||||
|
val uri = provider.insert(syncAdapterURI(Groups.CONTENT_URI), values)
|
||||||
|
return ContentUris.parseId(uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(ContactsStorageException::class)
|
|
||||||
fun removeEmptyGroups() {
|
fun removeEmptyGroups() {
|
||||||
// find groups without members
|
// find groups without members
|
||||||
/** should be done using [Groups.SUMMARY_COUNT], but it's not implemented in Android yet */
|
/** should be done using {@link Groups.SUMMARY_COUNT}, but it's not implemented in Android yet */
|
||||||
for (group in queryGroups(null, null) as Array<LocalGroup>)
|
queryGroups(null, null).filter { it.getMembers().isEmpty() }.forEach { group ->
|
||||||
if (group.members.size == 0) {
|
App.log.log(Level.FINE, "Deleting group", group)
|
||||||
App.log.log(Level.FINE, "Deleting group", group)
|
group.delete()
|
||||||
group.delete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(ContactsStorageException::class)
|
|
||||||
fun removeGroups() {
|
|
||||||
try {
|
|
||||||
provider.delete(syncAdapterURI(Groups.CONTENT_URI), null, null)
|
|
||||||
} catch (e: RemoteException) {
|
|
||||||
throw ContactsStorageException("Couldn't remove all groups", e)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Fix all of the etags of all of the non-dirty contacts to be non-null.
|
/** Fix all of the etags of all of the non-dirty contacts to be non-null.
|
||||||
* Currently set to all ones. */
|
* Currently set to all ones. */
|
||||||
@Throws(ContactsStorageException::class)
|
@Throws(ContactsStorageException::class)
|
||||||
@ -352,81 +372,4 @@ class LocalAddressBook(protected val context: Context, account: Account, provide
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
protected val USER_DATA_MAIN_ACCOUNT_TYPE = "real_account_type"
|
|
||||||
protected val USER_DATA_MAIN_ACCOUNT_NAME = "real_account_name"
|
|
||||||
protected val USER_DATA_URL = "url"
|
|
||||||
|
|
||||||
|
|
||||||
@Throws(ContactsStorageException::class)
|
|
||||||
fun find(context: Context, provider: ContentProviderClient, mainAccount: Account?): Array<LocalAddressBook> {
|
|
||||||
val accountManager = AccountManager.get(context)
|
|
||||||
|
|
||||||
val result = LinkedList<LocalAddressBook>()
|
|
||||||
for (account in accountManager.getAccountsByType(App.addressBookAccountType)) {
|
|
||||||
val addressBook = LocalAddressBook(context, account, provider)
|
|
||||||
if (mainAccount == null || addressBook.mainAccount == mainAccount)
|
|
||||||
result.add(addressBook)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.toTypedArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(ContactsStorageException::class)
|
|
||||||
fun findByUid(context: Context, provider: ContentProviderClient, mainAccount: Account?, uid: String): LocalAddressBook? {
|
|
||||||
val accountManager = AccountManager.get(context)
|
|
||||||
|
|
||||||
for (account in accountManager.getAccountsByType(App.addressBookAccountType)) {
|
|
||||||
val addressBook = LocalAddressBook(context, account, provider)
|
|
||||||
if (addressBook.url == uid && (mainAccount == null || addressBook.mainAccount == mainAccount))
|
|
||||||
return addressBook
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(ContactsStorageException::class)
|
|
||||||
fun create(context: Context, provider: ContentProviderClient, mainAccount: Account, journalEntity: JournalEntity): LocalAddressBook {
|
|
||||||
val info = journalEntity.info
|
|
||||||
val accountManager = AccountManager.get(context)
|
|
||||||
|
|
||||||
val account = Account(accountName(mainAccount, info), App.addressBookAccountType)
|
|
||||||
if (!accountManager.addAccountExplicitly(account, null, null))
|
|
||||||
throw ContactsStorageException("Couldn't create address book account")
|
|
||||||
|
|
||||||
setUserData(accountManager, account, mainAccount, info.uid!!)
|
|
||||||
val addressBook = LocalAddressBook(context, account, provider)
|
|
||||||
addressBook.mainAccount = mainAccount
|
|
||||||
addressBook.url = info.uid
|
|
||||||
|
|
||||||
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true)
|
|
||||||
|
|
||||||
return addressBook
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// SETTINGS
|
|
||||||
|
|
||||||
// XXX: Workaround a bug in Android where passing a bundle to addAccountExplicitly doesn't work.
|
|
||||||
fun setUserData(accountManager: AccountManager, account: Account, mainAccount: Account, url: String) {
|
|
||||||
accountManager.setUserData(account, USER_DATA_MAIN_ACCOUNT_NAME, mainAccount.name)
|
|
||||||
accountManager.setUserData(account, USER_DATA_MAIN_ACCOUNT_TYPE, mainAccount.type)
|
|
||||||
accountManager.setUserData(account, USER_DATA_URL, url)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HELPERS
|
|
||||||
|
|
||||||
fun accountName(mainAccount: Account, info: CollectionInfo): String {
|
|
||||||
val displayName = if (info.displayName != null) info.displayName else info.uid
|
|
||||||
val sb = StringBuilder(displayName)
|
|
||||||
sb.append(" (")
|
|
||||||
.append(mainAccount.name)
|
|
||||||
.append(" ")
|
|
||||||
.append(info.uid!!.substring(0, 4))
|
|
||||||
.append(")")
|
|
||||||
return sb.toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@ class LocalCalendar protected constructor(account: Account, provider: ContentPro
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Throws(CalendarStorageException::class)
|
@Throws(CalendarStorageException::class)
|
||||||
override fun getByUid(uid: String): LocalEvent? {
|
override fun findByUid(uid: String): LocalEvent? {
|
||||||
val ret = queryEvents(Events._SYNC_ID + " =? ", arrayOf(uid)) as Array<LocalEvent>
|
val ret = queryEvents(Events._SYNC_ID + " =? ", arrayOf(uid)) as Array<LocalEvent>
|
||||||
return if (ret != null && ret.size > 0) {
|
return if (ret != null && ret.size > 0) {
|
||||||
ret[0]
|
ret[0]
|
||||||
|
@ -8,21 +8,13 @@
|
|||||||
|
|
||||||
package com.etesync.syncadapter.resource
|
package com.etesync.syncadapter.resource
|
||||||
|
|
||||||
import java.io.FileNotFoundException
|
interface LocalCollection<out T: LocalResource<*>> {
|
||||||
|
fun findDeleted(): List<T>
|
||||||
|
fun findDirty(): List<T>
|
||||||
|
fun findWithoutFileName(): List<T>
|
||||||
|
|
||||||
import at.bitfire.ical4android.CalendarStorageException
|
fun findByUid(uid: String): T?
|
||||||
import at.bitfire.vcard4android.ContactsStorageException
|
|
||||||
|
|
||||||
interface LocalCollection<T> {
|
|
||||||
|
|
||||||
val deleted: Array<T>
|
|
||||||
val withoutFileName: Array<T>
|
|
||||||
/** Dirty *non-deleted* entries */
|
|
||||||
val dirty: Array<T>
|
|
||||||
|
|
||||||
@Throws(CalendarStorageException::class, ContactsStorageException::class)
|
|
||||||
fun getByUid(uid: String): T?
|
|
||||||
|
|
||||||
@Throws(CalendarStorageException::class, ContactsStorageException::class)
|
|
||||||
fun count(): Long
|
fun count(): Long
|
||||||
}
|
}
|
||||||
|
@ -199,7 +199,7 @@ class LocalGroup : AndroidGroup, LocalAddress {
|
|||||||
* Marks all members of the current group as dirty.
|
* Marks all members of the current group as dirty.
|
||||||
*/
|
*/
|
||||||
fun markMembersDirty() {
|
fun markMembersDirty() {
|
||||||
] val batch = BatchOperation(addressBook.provider!!)
|
val batch = BatchOperation(addressBook.provider!!)
|
||||||
|
|
||||||
for (member in getMembers())
|
for (member in getMembers())
|
||||||
batch.enqueue(BatchOperation.Operation(
|
batch.enqueue(BatchOperation.Operation(
|
||||||
|
@ -65,7 +65,7 @@ class LocalTaskList protected constructor(account: Account, provider: TaskProvid
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Throws(CalendarStorageException::class)
|
@Throws(CalendarStorageException::class)
|
||||||
override fun getByUid(uid: String): LocalTask? {
|
override fun findByUid(uid: String): LocalTask? {
|
||||||
val ret = queryTasks(Tasks._SYNC_ID + " =? ", arrayOf(uid)) as Array<LocalTask>
|
val ret = queryTasks(Tasks._SYNC_ID + " =? ", arrayOf(uid)) as Array<LocalTask>
|
||||||
return if (ret != null && ret.size > 0) {
|
return if (ret != null && ret.size > 0) {
|
||||||
ret[0]
|
ret[0]
|
||||||
|
@ -38,12 +38,9 @@ import org.apache.commons.codec.Charsets
|
|||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
|
||||||
import java.text.DateFormat
|
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
import java.util.Date
|
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.TimeZone
|
import java.util.TimeZone
|
||||||
|
|
||||||
@ -112,7 +109,7 @@ constructor(context: Context, account: Account, settings: AccountSettings, extra
|
|||||||
}
|
}
|
||||||
|
|
||||||
val event = events[0]
|
val event = events[0]
|
||||||
val local = localCollection!!.getByUid(event.uid) as LocalEvent?
|
val local = localCollection!!.findByUid(event.uid) as LocalEvent?
|
||||||
|
|
||||||
if (cEntry.isAction(SyncEntry.Actions.ADD) || cEntry.isAction(SyncEntry.Actions.CHANGE)) {
|
if (cEntry.isAction(SyncEntry.Actions.ADD) || cEntry.isAction(SyncEntry.Actions.CHANGE)) {
|
||||||
processEvent(event, local)
|
processEvent(event, local)
|
||||||
|
@ -41,7 +41,6 @@ import org.apache.commons.io.IOUtils
|
|||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
|
||||||
import java.util.logging.Level
|
import java.util.logging.Level
|
||||||
|
|
||||||
import at.bitfire.ical4android.CalendarStorageException
|
import at.bitfire.ical4android.CalendarStorageException
|
||||||
@ -49,10 +48,7 @@ import at.bitfire.vcard4android.BatchOperation
|
|||||||
import at.bitfire.vcard4android.Contact
|
import at.bitfire.vcard4android.Contact
|
||||||
import at.bitfire.vcard4android.ContactsStorageException
|
import at.bitfire.vcard4android.ContactsStorageException
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
|
||||||
import okhttp3.ResponseBody
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -163,7 +159,7 @@ constructor(context: Context, account: Account, settings: AccountSettings, extra
|
|||||||
App.log.warning("Received multiple VCards, using first one")
|
App.log.warning("Received multiple VCards, using first one")
|
||||||
|
|
||||||
val contact = contacts[0]
|
val contact = contacts[0]
|
||||||
val local = localCollection!!.getByUid(contact.uid) as LocalResource?
|
val local = localCollection!!.findByUid(contact.uid) as LocalResource?
|
||||||
|
|
||||||
|
|
||||||
if (cEntry.isAction(SyncEntry.Actions.ADD) || cEntry.isAction(SyncEntry.Actions.CHANGE)) {
|
if (cEntry.isAction(SyncEntry.Actions.ADD) || cEntry.isAction(SyncEntry.Actions.CHANGE)) {
|
||||||
|
Loading…
Reference in New Issue
Block a user