diff --git a/app/src/main/java/com/etesync/syncadapter/resource/LocalAddressBook.kt b/app/src/main/java/com/etesync/syncadapter/resource/LocalAddressBook.kt index 485611f8..8b6cb31d 100644 --- a/app/src/main/java/com/etesync/syncadapter/resource/LocalAddressBook.kt +++ b/app/src/main/java/com/etesync/syncadapter/resource/LocalAddressBook.kt @@ -22,6 +22,7 @@ import at.bitfire.vcard4android.* import com.etesync.syncadapter.App import com.etesync.syncadapter.model.CollectionInfo import com.etesync.syncadapter.model.JournalEntity +import java.io.FileNotFoundException import java.util.* import java.util.logging.Level @@ -179,7 +180,14 @@ class LocalAddressBook( accountManager.removeAccount(account, null, null) } - override fun findAll(): List = queryContacts(RawContacts.DELETED + "== 0", null) + override fun findAll(): List = + if (includeGroups) + findAllContacts() + findAllGroups() + else + findAllContacts() + + fun findAllContacts() = queryContacts("${RawContacts.DELETED}==0", null) + fun findAllGroups() = queryGroups("${Groups.DELETED}==0", null) /** * Returns an array of local contacts/groups which have been deleted locally. (DELETED != 0). @@ -259,6 +267,10 @@ class LocalAddressBook( } } + fun findGroupById(id: Long): LocalGroup = + queryGroups("${Groups._ID}=?", arrayOf(id.toString())).firstOrNull() + ?: throw FileNotFoundException() + override fun count(): Long { try { val cursor = provider?.query(syncAdapterURI(RawContacts.CONTENT_URI), null, null, null, null) diff --git a/app/src/main/java/com/etesync/syncadapter/resource/LocalGroup.kt b/app/src/main/java/com/etesync/syncadapter/resource/LocalGroup.kt index 65bdf8f6..99e6fb18 100644 --- a/app/src/main/java/com/etesync/syncadapter/resource/LocalGroup.kt +++ b/app/src/main/java/com/etesync/syncadapter/resource/LocalGroup.kt @@ -47,16 +47,6 @@ class LocalGroup : AndroidGroup, LocalAddress { val batch = BatchOperation(addressBook.provider) while (cursor.moveToNext()) { val id = cursor.getLong(0) - Constants.log.fine("Assigning members to group $id") - - // required for workaround for Android 7 which sets DIRTY flag when only meta-data is changed - val changeContactIDs = HashSet() - - // delete all memberships and cached memberships for this group - for (contact in addressBook.getByGroupMembership(id)) { - contact.removeGroupMemberships(batch) - changeContactIDs += contact.id!! - } // extract list of member UIDs val members = LinkedList() @@ -71,26 +61,13 @@ class LocalGroup : AndroidGroup, LocalAddress { } // insert memberships - for (uid in members) { + val membersIds = members.map {uid -> Constants.log.fine("Assigning member: $uid") - addressBook.findContactByUID(uid)?.let { member -> - member.addToGroup(batch, id) - changeContactIDs += member.id!! - } ?: Constants.log.warning("Group member not found: $uid") + (addressBook.findByUid(uid) as LocalContact).id!! } - if (LocalContact.HASH_HACK) - // workaround for Android 7 which sets DIRTY flag when only meta-data is changed - changeContactIDs - .map { addressBook.findContactByID(it) } - .forEach { it.updateHashCode(batch) } - - // remove pending memberships - batch.enqueue(BatchOperation.Operation( - ContentProviderOperation.newUpdate(addressBook.syncAdapterURI(ContentUris.withAppendedId(Groups.CONTENT_URI, id))) - .withValue(COLUMN_PENDING_MEMBERS, null) - .withYieldAllowed(true) - )) + val group = addressBook.findGroupById(id) + group.setMembers(batch, membersIds) batch.commit() } @@ -98,6 +75,8 @@ class LocalGroup : AndroidGroup, LocalAddress { } } + private var saveAsDirty = false // When true, the resource will be saved as dirty + override val uuid: String? get() = fileName @@ -133,6 +112,10 @@ class LocalGroup : AndroidGroup, LocalAddress { } finally { members.recycle() } + + if (saveAsDirty) { + values.put(Groups.DIRTY, true) + } return values } @@ -187,6 +170,54 @@ class LocalGroup : AndroidGroup, LocalAddress { addressBook.provider!!.update(groupSyncUri(), values, null, null) } + private fun setMembers(batch: BatchOperation, members: List) { + val id = id!! + val addressBook = this.addressBook as LocalAddressBook + Constants.log.fine("Assigning members to group $id") + + // required for workaround for Android 7 which sets DIRTY flag when only meta-data is changed + val changeContactIDs = HashSet() + + // delete all memberships and cached memberships for this group + for (contact in addressBook.getByGroupMembership(id)) { + contact.removeGroupMemberships(batch) + changeContactIDs += contact.id!! + } + + // insert memberships + for (memberId in members) { + Constants.log.fine("Assigning member: $memberId") + addressBook.findContactByID(memberId).let { member -> + member.addToGroup(batch, id) + changeContactIDs += member.id!! + } ?: Constants.log.warning("Group member not found: $memberId") + } + + if (LocalContact.HASH_HACK) + // workaround for Android 7 which sets DIRTY flag when only meta-data is changed + changeContactIDs + .map { addressBook.findContactByID(it) } + .forEach { it.updateHashCode(batch) } + + // remove pending memberships + batch.enqueue(BatchOperation.Operation( + ContentProviderOperation.newUpdate(addressBook.syncAdapterURI(ContentUris.withAppendedId(Groups.CONTENT_URI, id))) + .withValue(COLUMN_PENDING_MEMBERS, null) + .withYieldAllowed(true) + )) + } + + fun createAsDirty(members: List): Uri { + saveAsDirty = true + + val ret = this.add() + + val batch = BatchOperation(addressBook.provider!!) + setMembers(batch, members) + batch.commit() + + return ret + } // helpers diff --git a/app/src/main/java/com/etesync/syncadapter/ui/importlocal/ImportActivity.kt b/app/src/main/java/com/etesync/syncadapter/ui/importlocal/ImportActivity.kt index a1ce3dee..2cf9215f 100644 --- a/app/src/main/java/com/etesync/syncadapter/ui/importlocal/ImportActivity.kt +++ b/app/src/main/java/com/etesync/syncadapter/ui/importlocal/ImportActivity.kt @@ -26,7 +26,7 @@ class ImportActivity : BaseActivity(), SelectImportMethod, ResultFragment.OnImpo title = getString(R.string.import_dialog_title) - account = intent.extras!!.getParcelable(EXTRA_ACCOUNT) + account = intent.extras!!.getParcelable(EXTRA_ACCOUNT)!! info = intent.extras!!.getSerializable(EXTRA_COLLECTION_INFO) as CollectionInfo if (savedInstanceState == null) diff --git a/app/src/main/java/com/etesync/syncadapter/ui/importlocal/LocalContactImportFragment.kt b/app/src/main/java/com/etesync/syncadapter/ui/importlocal/LocalContactImportFragment.kt index 8e2822a3..60f2e38d 100644 --- a/app/src/main/java/com/etesync/syncadapter/ui/importlocal/LocalContactImportFragment.kt +++ b/app/src/main/java/com/etesync/syncadapter/ui/importlocal/LocalContactImportFragment.kt @@ -21,13 +21,16 @@ import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView import at.bitfire.vcard4android.ContactsStorageException +import com.etesync.syncadapter.App import com.etesync.syncadapter.Constants.KEY_ACCOUNT import com.etesync.syncadapter.Constants.KEY_COLLECTION_INFO import com.etesync.syncadapter.R import com.etesync.syncadapter.model.CollectionInfo import com.etesync.syncadapter.resource.LocalAddressBook import com.etesync.syncadapter.resource.LocalContact +import com.etesync.syncadapter.resource.LocalGroup import java.util.* +import kotlin.collections.HashMap class LocalContactImportFragment : Fragment() { @@ -132,9 +135,11 @@ class LocalContactImportFragment : Fragment() { try { val addressBook = LocalAddressBook.findByUid(context!!, context!!.contentResolver.acquireContentProviderClient(ContactsContract.RawContacts.CONTENT_URI)!!, - account, info.uid!!) - val localContacts = localAddressBook.findAll() - val total = localContacts.size + account, info.uid!!)!! + val localContacts = localAddressBook.findAllContacts() + val localGroups = localAddressBook.findAllGroups() + val oldIdToNewId = HashMap() + val total = localContacts.size + localGroups.size progressDialog!!.max = total result.total = total.toLong() var progress = 0 @@ -142,8 +147,27 @@ class LocalContactImportFragment : Fragment() { val contact = currentLocalContact.contact try { - val localContact = LocalContact(addressBook!!, contact!!, null, null) + val localContact = LocalContact(addressBook, contact!!, null, null) localContact.createAsDirty() + oldIdToNewId[currentLocalContact.id!!] = localContact.id!! + result.added++ + } catch (e: ContactsStorageException) { + e.printStackTrace() + result.e = e + } + + publishProgress(++progress) + } + for (currentLocalGroup in localGroups) { + val group = currentLocalGroup.contact + + try { + val localGroup = LocalGroup(addressBook, group!!, null, null) + val members = currentLocalGroup.getMembers().map { it -> + oldIdToNewId[it]!! + } + + localGroup.createAsDirty(members) result.added++ } catch (e: ContactsStorageException) { e.printStackTrace()