Commit e2b5ce7b authored by Ricki Hirner's avatar Ricki Hirner 🐑

Better handling of contact group method change

* ask for confirmation when group method is changed in account settings activity
* reset address books/force reload when contact group method has changed since last sync
parent 3d537b44
......@@ -210,7 +210,7 @@ class Settings: Service(), Provider.Observer {
override fun close() {
try {
serviceConn.let { context.unbindService(it) }
context.unbindService(serviceConn)
} catch(e: Exception) {
Logger.log.log(Level.SEVERE, "Couldn't unbind Settings service", e)
}
......
......@@ -14,6 +14,7 @@ import android.content.ContentResolver
import android.content.Context
import android.content.SyncResult
import android.os.Bundle
import android.provider.ContactsContract
import at.bitfire.davdroid.AccountSettings
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.resource.LocalAddressBook
......@@ -22,6 +23,10 @@ import java.util.logging.Level
class ContactsSyncAdapterService: SyncAdapterService() {
companion object {
val PREVIOUS_GROUP_METHOD = "previous_group_method"
}
override fun syncAdapter() = ContactsSyncAdapter(this)
......@@ -32,8 +37,24 @@ class ContactsSyncAdapterService: SyncAdapterService() {
override fun sync(settings: ISettings, account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) {
try {
val addressBook = LocalAddressBook(context, account, provider)
val accountSettings = AccountSettings(context, settings, addressBook.getMainAccount())
// handle group method change
val groupMethod = accountSettings.getGroupMethod().name
accountSettings.accountManager.getUserData(account, PREVIOUS_GROUP_METHOD)?.let { previousGroupMethod ->
if (previousGroupMethod != groupMethod) {
Logger.log.info("Group method changed, deleting all local contacts/groups")
// delete all local contacts and groups so that they will be downloaded again
provider.delete(addressBook.syncAdapterURI(ContactsContract.RawContacts.CONTENT_URI), null, null)
provider.delete(addressBook.syncAdapterURI(ContactsContract.Groups.CONTENT_URI), null, null)
// reset CTag
addressBook.setCTag(null)
}
}
accountSettings.accountManager.setUserData(account, PREVIOUS_GROUP_METHOD, groupMethod)
if (!extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL) && !checkSyncConditions(accountSettings))
return
......
......@@ -51,7 +51,7 @@ import java.util.logging.Level
* dirty, too (because they have to be uploaded without the respective category). This
* is done in {@link #prepareDirty()}. Empty groups can be deleted without further processing,
* which is done in {@link #postProcess()} because groups may become empty after downloading
* updated remoted contacts.</li>
* updated remote contacts.</li>
* <li>Groups as separate VCards: individual and group contacts (with a list of member UIDs) are
* distinguished. When a local group is dirty, its members don't need to be set to dirty.
* <ol>
......@@ -86,13 +86,15 @@ class ContactsSyncManager(
private val localAddressBook: LocalAddressBook
): SyncManager(context, account, settings, extras, authority, syncResult, "addressBook") {
private val MAX_MULTIGET = 10
companion object {
private val MAX_MULTIGET = 10
}
private var readOnly = false
var numDiscarded = 0
private val readOnly = localAddressBook.getReadOnly()
private var numDiscarded = 0
private var hasVCard4 = false
private lateinit var groupMethod: GroupMethod
private val groupMethod = settings.getGroupMethod()
init {
......@@ -124,8 +126,6 @@ class ContactsSyncManager(
collectionURL = HttpUrl.parse(localAddressBook.getURL()) ?: return false
davCollection = DavAddressBook(httpClient.okHttpClient, collectionURL)
readOnly = localAddressBook.getReadOnly()
return true
}
......@@ -137,9 +137,8 @@ class ContactsSyncManager(
}
Logger.log.info("Server advertises VCard/4 support: $hasVCard4")
groupMethod = settings.getGroupMethod()
Logger.log.info("Contact group method: $groupMethod")
// in case of GROUP_VCARDs, treat groups as contacts in the local address book
localAddressBook.includeGroups = groupMethod == GroupMethod.GROUP_VCARDS
}
......@@ -390,10 +389,8 @@ class ContactsSyncManager(
val newData = contacts.first()
if (groupMethod == GroupMethod.CATEGORIES && newData.group) {
groupMethod = GroupMethod.GROUP_VCARDS
Logger.log.warning("Received group VCard although group method is CATEGORIES. Deleting all groups; new group method: $groupMethod")
localAddressBook.removeGroups()
settings.setGroupMethod(groupMethod)
Logger.log.warning("Received group VCard although group method is CATEGORIES. Saving as regular contact")
newData.group = false
}
// update local contact, if it exists
......
......@@ -48,12 +48,13 @@ abstract class SyncAdapterService: Service() {
Thread.currentThread().contextClassLoader = context.classLoader
// load app settings
val settings = Settings.getInstance(context)
if (settings == null) {
syncResult.databaseError = true
Logger.log.severe("Couldn't connect to Settings service, aborting sync")
return
} else settings.use { settings ->
Settings.getInstance(context).use { settings ->
if (settings == null) {
syncResult.databaseError = true
Logger.log.severe("Couldn't connect to Settings service, aborting sync")
return
}
sync(settings, account, extras, authority, provider, syncResult)
}
Logger.log.info("Sync for $authority complete")
......
......@@ -135,13 +135,13 @@ abstract class SyncManager(
Logger.log.info("Checking sync state")
if (checkSyncState()) {
syncPhase = SYNC_PHASE_LIST_LOCAL
Logger.log.info("Listing local entries")
Logger.log.info("Listing local resources")
listLocal()
if (Thread.interrupted())
return
syncPhase = SYNC_PHASE_LIST_REMOTE
Logger.log.info("Listing remote entries")
Logger.log.info("Listing remote resources")
listRemote()
if (Thread.interrupted())
......@@ -385,6 +385,7 @@ abstract class SyncManager(
resource.fileName?.let { resources[it] = resource }
}
localResources = resources
Logger.log.info("Found ${localResources.size} local resources")
}
/**
......
......@@ -191,8 +191,22 @@ class AccountSettingsActivity: AppCompatActivity() {
else {
it.isEnabled = true
it.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, groupMethod ->
accountSettings.setGroupMethod(GroupMethod.valueOf(groupMethod as String))
loaderManager.restartLoader(0, arguments, this)
AlertDialog.Builder(activity)
.setIcon(R.drawable.ic_error_dark)
.setTitle(R.string.settings_contact_group_method_change)
.setMessage(R.string.settings_contact_group_method_change_reload_contacts)
.setPositiveButton(android.R.string.ok, { _, _ ->
// change group method
accountSettings.setGroupMethod(GroupMethod.valueOf(groupMethod as String))
loaderManager.restartLoader(0, arguments, this)
// reload all contacts
val args = Bundle(1)
args.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true)
ContentResolver.requestSync(account, getString(R.string.address_books_authority), args)
})
.setNegativeButton(android.R.string.cancel, null)
.show()
false
}
}
......
......@@ -209,6 +209,8 @@
<item>Groups are separate VCards</item>
<item>Groups are per-contact categories</item>
</string-array>
<string name="settings_contact_group_method_change">Change group method</string>
<string name="settings_contact_group_method_change_reload_contacts">This requires to reload all contacts. Unsaved local changes will be dismissed.</string>
<string name="settings_caldav">CalDAV</string>
<string name="settings_sync_time_range_past">Past event time limit</string>
<string name="settings_sync_time_range_past_none">All events will be synchronized</string>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment