mirror of
https://github.com/etesync/android
synced 2024-11-21 23:48:11 +00:00
Upgrade vcard4android and ical4android.
This commit is contained in:
parent
c7d75277b5
commit
521cda35f5
@ -18,7 +18,7 @@ android {
|
||||
defaultConfig {
|
||||
applicationId "com.etesync.syncadapter"
|
||||
|
||||
minSdkVersion 16
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 26
|
||||
|
||||
versionCode 43
|
||||
|
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright © Ricki Hirner (bitfire web engineering).
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the GNU Public License v3.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
|
||||
package com.etesync.syncadapter.resource
|
||||
|
||||
import at.bitfire.vcard4android.Contact
|
||||
|
||||
interface LocalAddress: LocalResource<Contact> {
|
||||
|
||||
fun resetDeleted()
|
||||
|
||||
}
|
@ -42,7 +42,14 @@ import ezvcard.VCardVersion
|
||||
|
||||
import at.bitfire.vcard4android.GroupMethod.GROUP_VCARDS
|
||||
|
||||
class LocalContact : AndroidContact, LocalResource {
|
||||
class LocalContact : AndroidContact, LocalAddress {
|
||||
companion object {
|
||||
init {
|
||||
Contact.productID = Constants.PRODID_BASE + " ez-vcard/" + Ezvcard.VERSION
|
||||
}
|
||||
|
||||
internal const val COLUMN_HASHCODE = ContactsContract.RawContacts.SYNC3
|
||||
}
|
||||
|
||||
private var saveAsDirty = false // When true, the resource will be saved as dirty
|
||||
|
||||
@ -57,7 +64,6 @@ class LocalContact : AndroidContact, LocalResource {
|
||||
get() = TextUtils.isEmpty(eTag)
|
||||
|
||||
override val content: String
|
||||
@Throws(IOException::class, ContactsStorageException::class)
|
||||
get() {
|
||||
val contact: Contact
|
||||
contact = this.contact!!
|
||||
@ -70,93 +76,60 @@ class LocalContact : AndroidContact, LocalResource {
|
||||
return os.toString()
|
||||
}
|
||||
|
||||
val lastHashCode: Int
|
||||
@Throws(ContactsStorageException::class)
|
||||
get() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
|
||||
App.log.severe("getLastHashCode() should not be called on Android <7")
|
||||
constructor(addressBook: AndroidAddressBook<LocalContact,*>, values: ContentValues)
|
||||
: super(addressBook, values) {}
|
||||
|
||||
try {
|
||||
val c = addressBook.provider.query(rawContactSyncURI(), arrayOf(COLUMN_HASHCODE), null, null, null)
|
||||
try {
|
||||
return if (c == null || !c.moveToNext() || c.isNull(0)) 0 else c.getInt(0)
|
||||
} finally {
|
||||
c?.close()
|
||||
}
|
||||
} catch (e: RemoteException) {
|
||||
throw ContactsStorageException("Could't read last hash code", e)
|
||||
}
|
||||
constructor(addressBook: AndroidAddressBook<LocalContact, *>, contact: Contact, uuid: String?, eTag: String?)
|
||||
: super(addressBook, contact, uuid, eTag) {}
|
||||
|
||||
}
|
||||
|
||||
|
||||
constructor(addressBook: AndroidAddressBook, id: Long, uuid: String?, eTag: String?) : super(addressBook, id, uuid, eTag) {}
|
||||
|
||||
constructor(addressBook: AndroidAddressBook, contact: Contact, uuid: String?, eTag: String?) : super(addressBook, contact, uuid, eTag) {}
|
||||
|
||||
@Throws(ContactsStorageException::class)
|
||||
fun resetDirty() {
|
||||
val values = ContentValues(1)
|
||||
values.put(ContactsContract.RawContacts.DIRTY, 0)
|
||||
try {
|
||||
addressBook.provider.update(rawContactSyncURI(), values, null, null)
|
||||
} catch (e: RemoteException) {
|
||||
throw ContactsStorageException("Couldn't clear dirty flag", e)
|
||||
}
|
||||
|
||||
addressBook.provider?.update(rawContactSyncURI(), values, null, null)
|
||||
}
|
||||
|
||||
override fun resetDeleted() {
|
||||
val values = ContentValues(1)
|
||||
values.put(ContactsContract.Groups.DELETED, 0)
|
||||
addressBook.provider?.update(rawContactSyncURI(), values, null, null)
|
||||
}
|
||||
|
||||
@Throws(ContactsStorageException::class)
|
||||
override fun clearDirty(eTag: String) {
|
||||
try {
|
||||
val values = ContentValues(3)
|
||||
values.put(AndroidContact.COLUMN_ETAG, eTag)
|
||||
values.put(ContactsContract.RawContacts.DIRTY, 0)
|
||||
val values = ContentValues(3)
|
||||
values.put(AndroidContact.COLUMN_ETAG, eTag)
|
||||
values.put(ContactsContract.RawContacts.DIRTY, 0)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
// workaround for Android 7 which sets DIRTY flag when only meta-data is changed
|
||||
val hashCode = dataHashCode()
|
||||
values.put(COLUMN_HASHCODE, hashCode)
|
||||
App.log.finer("Clearing dirty flag with eTag = $eTag, contact hash = $hashCode")
|
||||
}
|
||||
|
||||
addressBook.provider.update(rawContactSyncURI(), values, null, null)
|
||||
|
||||
this.eTag = eTag
|
||||
} catch (e: FileNotFoundException) {
|
||||
throw ContactsStorageException("Couldn't clear dirty flag", e)
|
||||
} catch (e: RemoteException) {
|
||||
throw ContactsStorageException("Couldn't clear dirty flag", e)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
// workaround for Android 7 which sets DIRTY flag when only meta-data is changed
|
||||
val hashCode = dataHashCode()
|
||||
values.put(COLUMN_HASHCODE, hashCode)
|
||||
App.log.finer("Clearing dirty flag with eTag = $eTag, contact hash = $hashCode")
|
||||
}
|
||||
|
||||
addressBook.provider?.update(rawContactSyncURI(), values, null, null)
|
||||
|
||||
this.eTag = eTag
|
||||
}
|
||||
|
||||
@Throws(ContactsStorageException::class)
|
||||
override fun prepareForUpload() {
|
||||
try {
|
||||
val uid = UUID.randomUUID().toString()
|
||||
val uid = UUID.randomUUID().toString()
|
||||
|
||||
val values = ContentValues(2)
|
||||
values.put(AndroidContact.COLUMN_FILENAME, uid)
|
||||
values.put(AndroidContact.COLUMN_UID, uid)
|
||||
addressBook.provider.update(rawContactSyncURI(), values, null, null)
|
||||
|
||||
fileName = uid
|
||||
} catch (e: RemoteException) {
|
||||
throw ContactsStorageException("Couldn't update UID", e)
|
||||
}
|
||||
val values = ContentValues(2)
|
||||
values.put(AndroidContact.COLUMN_FILENAME, uid)
|
||||
values.put(AndroidContact.COLUMN_UID, uid)
|
||||
addressBook.provider.update(rawContactSyncURI(), values, null, null)
|
||||
|
||||
fileName = uid
|
||||
}
|
||||
|
||||
override fun populateData(mimeType: String, row: ContentValues) {
|
||||
when (mimeType) {
|
||||
CachedGroupMembership.CONTENT_ITEM_TYPE -> cachedGroupMemberships.add(row.getAsLong(CachedGroupMembership.GROUP_ID))
|
||||
GroupMembership.CONTENT_ITEM_TYPE -> groupMemberships.add(row.getAsLong(GroupMembership.GROUP_ROW_ID))
|
||||
UnknownProperties.CONTENT_ITEM_TYPE -> contact.unknownProperties = row.getAsString(UnknownProperties.UNKNOWN_PROPERTIES)
|
||||
UnknownProperties.CONTENT_ITEM_TYPE -> contact?.unknownProperties = row.getAsString(UnknownProperties.UNKNOWN_PROPERTIES)
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(ContactsStorageException::class)
|
||||
override fun insertDataRows(batch: BatchOperation) {
|
||||
super.insertDataRows(batch)
|
||||
|
||||
@ -176,16 +149,14 @@ class LocalContact : AndroidContact, LocalResource {
|
||||
|
||||
}
|
||||
|
||||
@Throws(ContactsStorageException::class)
|
||||
fun updateAsDirty(contact: Contact): Int {
|
||||
fun updateAsDirty(contact: Contact): Uri {
|
||||
saveAsDirty = true
|
||||
return this.update(contact)
|
||||
}
|
||||
|
||||
@Throws(ContactsStorageException::class)
|
||||
fun createAsDirty(): Uri {
|
||||
saveAsDirty = true
|
||||
return this.create()
|
||||
return this.add()
|
||||
}
|
||||
|
||||
override fun buildContact(builder: ContentProviderOperation.Builder, update: Boolean) {
|
||||
@ -195,55 +166,56 @@ class LocalContact : AndroidContact, LocalResource {
|
||||
|
||||
/**
|
||||
* Calculates a hash code from the contact's data (VCard) and group memberships.
|
||||
* Attention: re-reads [.contact] from the database, discarding all changes in memory
|
||||
* Attention: re-reads {@link #contact} from the database, discarding all changes in memory
|
||||
* @return hash code of contact data (including group memberships)
|
||||
*/
|
||||
@Throws(FileNotFoundException::class, ContactsStorageException::class)
|
||||
fun dataHashCode(): Int {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
|
||||
App.log.severe("dataHashCode() should not be called on Android <7")
|
||||
internal fun dataHashCode(): Int {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N || Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
throw IllegalStateException("dataHashCode() should not be called on Android != 7")
|
||||
|
||||
// reset contact so that getContact() reads from database
|
||||
contact = null
|
||||
|
||||
// groupMemberships is filled by getContact()
|
||||
val dataHash = getContact().hashCode()
|
||||
val dataHash = contact!!.hashCode()
|
||||
val groupHash = groupMemberships.hashCode()
|
||||
App.log.finest("Calculated data hash = $dataHash, group memberships hash = $groupHash")
|
||||
return dataHash xor groupHash
|
||||
}
|
||||
|
||||
@Throws(ContactsStorageException::class)
|
||||
fun updateHashCode(batch: BatchOperation?) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
|
||||
App.log.severe("updateHashCode() should not be called on Android <7")
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N || Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
throw IllegalStateException("updateHashCode() should not be called on Android != 7")
|
||||
|
||||
val values = ContentValues(1)
|
||||
try {
|
||||
val hashCode = dataHashCode()
|
||||
App.log.fine("Storing contact hash = $hashCode")
|
||||
values.put(COLUMN_HASHCODE, hashCode)
|
||||
val hashCode = dataHashCode()
|
||||
App.log.fine("Storing contact hash = $hashCode")
|
||||
values.put(COLUMN_HASHCODE, hashCode)
|
||||
|
||||
if (batch == null)
|
||||
addressBook.provider.update(rawContactSyncURI(), values, null, null)
|
||||
else {
|
||||
val builder = ContentProviderOperation
|
||||
.newUpdate(rawContactSyncURI())
|
||||
.withValues(values)
|
||||
batch.enqueue(BatchOperation.Operation(builder))
|
||||
}
|
||||
} catch (e: FileNotFoundException) {
|
||||
throw ContactsStorageException("Couldn't store contact checksum", e)
|
||||
} catch (e: RemoteException) {
|
||||
throw ContactsStorageException("Couldn't store contact checksum", e)
|
||||
if (batch == null)
|
||||
addressBook.provider!!.update(rawContactSyncURI(), values, null, null)
|
||||
else {
|
||||
val builder = ContentProviderOperation
|
||||
.newUpdate(rawContactSyncURI())
|
||||
.withValues(values)
|
||||
batch.enqueue(BatchOperation.Operation(builder))
|
||||
}
|
||||
}
|
||||
|
||||
fun getLastHashCode(): Int {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N || Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
throw IllegalStateException("getLastHashCode() should not be called on Android != 7")
|
||||
|
||||
addressBook.provider!!.query(rawContactSyncURI(), arrayOf(COLUMN_HASHCODE), null, null, null)?.use { c ->
|
||||
if (c.moveToNext() && !c.isNull(0))
|
||||
return c.getInt(0)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
fun addToGroup(batch: BatchOperation, groupID: Long) {
|
||||
assertID()
|
||||
|
||||
fun addToGroup(batch: BatchOperation, groupID: Long) {
|
||||
batch.enqueue(BatchOperation.Operation(
|
||||
ContentProviderOperation.newInsert(dataSyncURI())
|
||||
.withValue(GroupMembership.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE)
|
||||
@ -263,7 +235,6 @@ class LocalContact : AndroidContact, LocalResource {
|
||||
}
|
||||
|
||||
fun removeGroupMemberships(batch: BatchOperation) {
|
||||
assertID()
|
||||
batch.enqueue(BatchOperation.Operation(
|
||||
ContentProviderOperation.newDelete(dataSyncURI())
|
||||
.withSelection(
|
||||
@ -284,9 +255,8 @@ class LocalContact : AndroidContact, LocalResource {
|
||||
* @throws ContactsStorageException on contact provider errors
|
||||
* @throws FileNotFoundException if the current contact can't be found
|
||||
*/
|
||||
@Throws(ContactsStorageException::class, FileNotFoundException::class)
|
||||
fun getCachedGroupMemberships(): Set<Long> {
|
||||
getContact()
|
||||
contact
|
||||
return cachedGroupMemberships
|
||||
}
|
||||
|
||||
@ -296,37 +266,16 @@ class LocalContact : AndroidContact, LocalResource {
|
||||
* @throws ContactsStorageException on contact provider errors
|
||||
* @throws FileNotFoundException if the current contact can't be found
|
||||
*/
|
||||
@Throws(ContactsStorageException::class, FileNotFoundException::class)
|
||||
fun getGroupMemberships(): Set<Long> {
|
||||
getContact()
|
||||
contact
|
||||
return groupMemberships
|
||||
}
|
||||
|
||||
|
||||
// factory
|
||||
|
||||
internal class Factory : AndroidContactFactory() {
|
||||
|
||||
override fun newInstance(addressBook: AndroidAddressBook, id: Long, fileName: String, eTag: String): LocalContact {
|
||||
return LocalContact(addressBook, id, fileName, eTag)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<LocalContact?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val INSTANCE = Factory()
|
||||
}
|
||||
|
||||
object Factory: AndroidContactFactory<LocalContact> {
|
||||
override fun fromProvider(addressBook: AndroidAddressBook<LocalContact, *>, values: ContentValues) =
|
||||
LocalContact(addressBook, values)
|
||||
}
|
||||
|
||||
companion object {
|
||||
init {
|
||||
Contact.productID = Constants.PRODID_BASE + " ez-vcard/" + Ezvcard.VERSION
|
||||
}
|
||||
|
||||
val COLUMN_HASHCODE = ContactsContract.RawContacts.SYNC3
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,36 +8,34 @@
|
||||
|
||||
package com.etesync.syncadapter.resource
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.content.ContentProviderOperation
|
||||
import android.content.ContentValues
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.RemoteException
|
||||
import android.provider.CalendarContract
|
||||
import android.provider.CalendarContract.Events
|
||||
import android.text.TextUtils
|
||||
|
||||
import at.bitfire.ical4android.*
|
||||
import at.bitfire.ical4android.Constants.ical4jVersion
|
||||
import at.bitfire.vcard4android.ContactsStorageException
|
||||
import com.etesync.syncadapter.App
|
||||
import com.etesync.syncadapter.Constants
|
||||
|
||||
import net.fortuna.ical4j.model.property.ProdId
|
||||
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.IOException
|
||||
import java.util.UUID
|
||||
import java.util.*
|
||||
import java.util.logging.Level
|
||||
|
||||
import at.bitfire.ical4android.AndroidCalendar
|
||||
import at.bitfire.ical4android.AndroidEvent
|
||||
import at.bitfire.ical4android.AndroidEventFactory
|
||||
import at.bitfire.ical4android.CalendarStorageException
|
||||
import at.bitfire.ical4android.Event
|
||||
import at.bitfire.vcard4android.ContactsStorageException
|
||||
class LocalEvent : AndroidEvent, LocalResource<Event> {
|
||||
companion object {
|
||||
init {
|
||||
ICalendar.prodId = ProdId(Constants.PRODID_BASE + " ical4j/" + ical4jVersion)
|
||||
}
|
||||
|
||||
@TargetApi(17)
|
||||
class LocalEvent : AndroidEvent, LocalResource {
|
||||
internal const val COLUMN_ETAG = CalendarContract.Events.SYNC_DATA1
|
||||
internal const val COLUMN_UID = Events.UID_2445
|
||||
internal const val COLUMN_SEQUENCE = CalendarContract.Events.SYNC_DATA3
|
||||
}
|
||||
|
||||
private var saveAsDirty = false // When true, the resource will be saved as dirty
|
||||
|
||||
@ -47,12 +45,11 @@ class LocalEvent : AndroidEvent, LocalResource {
|
||||
var weAreOrganizer = true
|
||||
|
||||
override val content: String
|
||||
@Throws(IOException::class, ContactsStorageException::class, CalendarStorageException::class)
|
||||
get() {
|
||||
App.log.log(Level.FINE, "Preparing upload of event " + fileName!!, getEvent())
|
||||
App.log.log(Level.FINE, "Preparing upload of event " + fileName!!, event)
|
||||
|
||||
val os = ByteArrayOutputStream()
|
||||
getEvent().write(os)
|
||||
event?.write(os)
|
||||
|
||||
return os.toString()
|
||||
}
|
||||
@ -64,34 +61,27 @@ class LocalEvent : AndroidEvent, LocalResource {
|
||||
val uuid: String?
|
||||
get() = fileName
|
||||
|
||||
constructor(calendar: AndroidCalendar, event: Event, fileName: String?, eTag: String?) : super(calendar, event) {
|
||||
constructor(calendar: AndroidCalendar<*>, event: Event, fileName: String?, eTag: String?) : super(calendar, event) {
|
||||
this.fileName = fileName
|
||||
this.eTag = eTag
|
||||
}
|
||||
|
||||
protected constructor(calendar: AndroidCalendar, id: Long, baseInfo: ContentValues?) : super(calendar, id, baseInfo) {
|
||||
if (baseInfo != null) {
|
||||
fileName = baseInfo.getAsString(Events._SYNC_ID)
|
||||
eTag = baseInfo.getAsString(COLUMN_ETAG)
|
||||
}
|
||||
protected constructor(calendar: AndroidCalendar<*>, baseInfo: ContentValues) : super(calendar, baseInfo) {
|
||||
fileName = baseInfo.getAsString(Events._SYNC_ID)
|
||||
eTag = baseInfo.getAsString(COLUMN_ETAG)
|
||||
}
|
||||
|
||||
/* process LocalEvent-specific fields */
|
||||
|
||||
override fun populateEvent(values: ContentValues) {
|
||||
super.populateEvent(values)
|
||||
fileName = values.getAsString(Events._SYNC_ID)
|
||||
eTag = values.getAsString(COLUMN_ETAG)
|
||||
event.uid = values.getAsString(COLUMN_UID)
|
||||
override fun populateEvent(row: ContentValues) {
|
||||
super.populateEvent(row)
|
||||
fileName = row.getAsString(Events._SYNC_ID)
|
||||
eTag = row.getAsString(COLUMN_ETAG)
|
||||
event?.uid = row.getAsString(COLUMN_UID)
|
||||
|
||||
event.sequence = values.getAsInteger(COLUMN_SEQUENCE)
|
||||
if (Build.VERSION.SDK_INT >= 17) {
|
||||
val isOrganizer = values.getAsInteger(Events.IS_ORGANIZER)
|
||||
weAreOrganizer = isOrganizer != null && isOrganizer != 0
|
||||
} else {
|
||||
val organizer = values.getAsString(Events.ORGANIZER)
|
||||
weAreOrganizer = organizer == null || organizer == calendar.account.name
|
||||
}
|
||||
event.sequence = row.getAsInteger(COLUMN_SEQUENCE)
|
||||
val isOrganizer = row.getAsInteger(Events.IS_ORGANIZER)
|
||||
weAreOrganizer = isOrganizer != null && isOrganizer != 0
|
||||
}
|
||||
|
||||
override fun buildEvent(recurrence: Event?, builder: ContentProviderOperation.Builder) {
|
||||
@ -126,77 +116,42 @@ class LocalEvent : AndroidEvent, LocalResource {
|
||||
|
||||
/* custom queries */
|
||||
|
||||
@Throws(CalendarStorageException::class)
|
||||
override fun prepareForUpload() {
|
||||
try {
|
||||
var uid: String? = null
|
||||
val c = calendar.provider.query(eventSyncURI(), arrayOf(COLUMN_UID), null, null, null)
|
||||
if (c.moveToNext())
|
||||
uid = c.getString(0)
|
||||
if (uid == null)
|
||||
uid = UUID.randomUUID().toString()
|
||||
var uid: String? = null
|
||||
val c = calendar.provider.query(eventSyncURI(), arrayOf(COLUMN_UID), null, null, null)
|
||||
if (c.moveToNext())
|
||||
uid = c.getString(0)
|
||||
if (uid == null)
|
||||
uid = UUID.randomUUID().toString()
|
||||
|
||||
c.close()
|
||||
val newFileName = uid
|
||||
c.close()
|
||||
val newFileName = uid
|
||||
|
||||
val values = ContentValues(2)
|
||||
values.put(Events._SYNC_ID, newFileName)
|
||||
values.put(COLUMN_UID, uid)
|
||||
calendar.provider.update(eventSyncURI(), values, null, null)
|
||||
val values = ContentValues(2)
|
||||
values.put(Events._SYNC_ID, newFileName)
|
||||
values.put(COLUMN_UID, uid)
|
||||
calendar.provider.update(eventSyncURI(), values, null, null)
|
||||
|
||||
fileName = newFileName
|
||||
if (event != null)
|
||||
event.uid = uid
|
||||
|
||||
} catch (e: RemoteException) {
|
||||
throw CalendarStorageException("Couldn't update UID", e)
|
||||
}
|
||||
fileName = newFileName
|
||||
|
||||
val event = this.event
|
||||
if (event != null)
|
||||
event.uid = uid
|
||||
}
|
||||
|
||||
@Throws(CalendarStorageException::class)
|
||||
override fun clearDirty(eTag: String) {
|
||||
try {
|
||||
val values = ContentValues(2)
|
||||
values.put(CalendarContract.Events.DIRTY, 0)
|
||||
values.put(COLUMN_ETAG, eTag)
|
||||
if (event != null)
|
||||
values.put(COLUMN_SEQUENCE, event.sequence)
|
||||
calendar.provider.update(eventSyncURI(), values, null, null)
|
||||
|
||||
this.eTag = eTag
|
||||
} catch (e: RemoteException) {
|
||||
throw CalendarStorageException("Couldn't update UID", e)
|
||||
}
|
||||
val values = ContentValues(2)
|
||||
values.put(CalendarContract.Events.DIRTY, 0)
|
||||
values.put(COLUMN_ETAG, eTag)
|
||||
if (event != null)
|
||||
values.put(COLUMN_SEQUENCE, event?.sequence)
|
||||
calendar.provider.update(eventSyncURI(), values, null, null)
|
||||
|
||||
this.eTag = eTag
|
||||
}
|
||||
|
||||
internal class Factory : AndroidEventFactory {
|
||||
|
||||
override fun newInstance(calendar: AndroidCalendar, id: Long, baseInfo: ContentValues): AndroidEvent {
|
||||
return LocalEvent(calendar, id, baseInfo)
|
||||
}
|
||||
|
||||
override fun newInstance(calendar: AndroidCalendar, event: Event): AndroidEvent {
|
||||
return LocalEvent(calendar, event, null, null)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<AndroidEvent?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val INSTANCE = Factory()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
init {
|
||||
Event.prodId = ProdId(Constants.PRODID_BASE + " ical4j/2.x")
|
||||
}
|
||||
|
||||
internal val COLUMN_ETAG = CalendarContract.Events.SYNC_DATA1
|
||||
internal val COLUMN_UID = if (Build.VERSION.SDK_INT >= 17) Events.UID_2445 else Events.SYNC_DATA2
|
||||
internal val COLUMN_SEQUENCE = CalendarContract.Events.SYNC_DATA3
|
||||
object Factory: AndroidEventFactory<LocalEvent> {
|
||||
override fun fromProvider(calendar: AndroidCalendar<AndroidEvent>, values: ContentValues): LocalEvent =
|
||||
LocalEvent(calendar, values)
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import android.content.ContentProviderOperation
|
||||
import android.content.ContentUris
|
||||
import android.content.ContentValues
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Parcel
|
||||
import android.os.RemoteException
|
||||
@ -21,6 +22,7 @@ import android.provider.ContactsContract.Groups
|
||||
import android.provider.ContactsContract.RawContacts
|
||||
import android.provider.ContactsContract.RawContacts.Data
|
||||
import android.text.TextUtils
|
||||
import at.bitfire.vcard4android.*
|
||||
|
||||
import com.etesync.syncadapter.App
|
||||
|
||||
@ -34,27 +36,88 @@ import java.util.LinkedList
|
||||
import java.util.UUID
|
||||
import java.util.logging.Level
|
||||
|
||||
import at.bitfire.vcard4android.AndroidAddressBook
|
||||
import at.bitfire.vcard4android.AndroidGroup
|
||||
import at.bitfire.vcard4android.AndroidGroupFactory
|
||||
import at.bitfire.vcard4android.BatchOperation
|
||||
import at.bitfire.vcard4android.CachedGroupMembership
|
||||
import at.bitfire.vcard4android.Contact
|
||||
import at.bitfire.vcard4android.ContactsStorageException
|
||||
import ezvcard.VCardVersion
|
||||
|
||||
import at.bitfire.vcard4android.GroupMethod.GROUP_VCARDS
|
||||
|
||||
class LocalGroup : AndroidGroup, LocalResource {
|
||||
class LocalGroup : AndroidGroup, LocalAddress {
|
||||
companion object {
|
||||
/** marshalled list of member UIDs, as sent by server */
|
||||
val COLUMN_PENDING_MEMBERS = Groups.SYNC3
|
||||
|
||||
override val uuid: String
|
||||
get() = getFileName()
|
||||
/**
|
||||
* Processes all groups with non-null {@link #COLUMN_PENDING_MEMBERS}: the pending memberships
|
||||
* are (if possible) applied, keeping cached memberships in sync.
|
||||
* @param addressBook address book to take groups from
|
||||
*/
|
||||
fun applyPendingMemberships(addressBook: LocalAddressBook) {
|
||||
addressBook.provider!!.query(
|
||||
addressBook.groupsSyncUri(),
|
||||
arrayOf(Groups._ID, COLUMN_PENDING_MEMBERS),
|
||||
"$COLUMN_PENDING_MEMBERS IS NOT NULL", null,
|
||||
null
|
||||
)?.use { cursor ->
|
||||
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<Long>()
|
||||
|
||||
// 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<String>()
|
||||
val raw = cursor.getBlob(1)
|
||||
val parcel = Parcel.obtain()
|
||||
try {
|
||||
parcel.unmarshall(raw, 0, raw.size)
|
||||
parcel.setDataPosition(0)
|
||||
parcel.readStringList(members)
|
||||
} finally {
|
||||
parcel.recycle()
|
||||
}
|
||||
|
||||
// insert memberships
|
||||
for (uid in members) {
|
||||
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")
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
|
||||
// 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)
|
||||
))
|
||||
|
||||
batch.commit()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val uuid: String?
|
||||
get() = fileName
|
||||
|
||||
override val content: String
|
||||
@Throws(IOException::class, ContactsStorageException::class)
|
||||
get() {
|
||||
val contact: Contact
|
||||
contact = getContact()
|
||||
contact = this.contact!!
|
||||
|
||||
App.log.log(Level.FINE, "Preparing upload of VCard $uuid", contact)
|
||||
|
||||
@ -65,42 +128,29 @@ class LocalGroup : AndroidGroup, LocalResource {
|
||||
}
|
||||
|
||||
override val isLocalOnly: Boolean
|
||||
get() = TextUtils.isEmpty(getETag())
|
||||
get() = TextUtils.isEmpty(eTag)
|
||||
|
||||
/**
|
||||
* Lists all members of this group.
|
||||
* @return list of all members' raw contact IDs
|
||||
* @throws ContactsStorageException on contact provider errors
|
||||
*/
|
||||
val members: LongArray
|
||||
@Throws(ContactsStorageException::class)
|
||||
get() {
|
||||
assertID()
|
||||
val members = LinkedList<Long>()
|
||||
try {
|
||||
val cursor = addressBook.provider.query(
|
||||
addressBook.syncAdapterURI(ContactsContract.Data.CONTENT_URI),
|
||||
arrayOf(Data.RAW_CONTACT_ID),
|
||||
GroupMembership.MIMETYPE + "=? AND " + GroupMembership.GROUP_ROW_ID + "=?",
|
||||
arrayOf(GroupMembership.CONTENT_ITEM_TYPE, id.toString()), null
|
||||
)
|
||||
while (cursor != null && cursor.moveToNext())
|
||||
members.add(cursor.getLong(0))
|
||||
cursor!!.close()
|
||||
} catch (e: RemoteException) {
|
||||
throw ContactsStorageException("Couldn't list group members", e)
|
||||
}
|
||||
constructor(addressBook: AndroidAddressBook<out AndroidContact, LocalGroup>, values: ContentValues)
|
||||
: super(addressBook, values) {}
|
||||
|
||||
return ArrayUtils.toPrimitive(members.toTypedArray())
|
||||
constructor(addressBook: AndroidAddressBook<out AndroidContact, LocalGroup>, contact: Contact, fileName: String?, eTag: String?, flags: Int)
|
||||
: super(addressBook, contact, fileName, eTag) {}
|
||||
|
||||
override fun contentValues(): ContentValues {
|
||||
val values = super.contentValues()
|
||||
|
||||
val members = Parcel.obtain()
|
||||
try {
|
||||
members.writeStringList(contact?.members)
|
||||
values.put(COLUMN_PENDING_MEMBERS, members.marshall())
|
||||
} finally {
|
||||
members.recycle()
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
constructor(addressBook: AndroidAddressBook, id: Long, fileName: String?, eTag: String?) : super(addressBook, id, fileName, eTag) {}
|
||||
|
||||
constructor(addressBook: AndroidAddressBook, contact: Contact, fileName: String?, eTag: String?) : super(addressBook, contact, fileName, eTag) {}
|
||||
|
||||
@Throws(ContactsStorageException::class)
|
||||
override fun clearDirty(eTag: String) {
|
||||
assertID()
|
||||
val id = requireNotNull(id)
|
||||
|
||||
val values = ContentValues(2)
|
||||
values.put(Groups.DIRTY, 0)
|
||||
@ -109,7 +159,7 @@ class LocalGroup : AndroidGroup, LocalResource {
|
||||
update(values)
|
||||
|
||||
// update cached group memberships
|
||||
val batch = BatchOperation(addressBook.provider)
|
||||
val batch = BatchOperation(addressBook.provider!!)
|
||||
|
||||
// delete cached group memberships
|
||||
batch.enqueue(BatchOperation.Operation(
|
||||
@ -121,7 +171,7 @@ class LocalGroup : AndroidGroup, LocalResource {
|
||||
))
|
||||
|
||||
// insert updated cached group memberships
|
||||
for (member in members)
|
||||
for (member in getMembers())
|
||||
batch.enqueue(BatchOperation.Operation(
|
||||
ContentProviderOperation.newInsert(addressBook.syncAdapterURI(ContactsContract.Data.CONTENT_URI))
|
||||
.withValue(CachedGroupMembership.MIMETYPE, CachedGroupMembership.CONTENT_ITEM_TYPE)
|
||||
@ -133,7 +183,6 @@ class LocalGroup : AndroidGroup, LocalResource {
|
||||
batch.commit()
|
||||
}
|
||||
|
||||
@Throws(ContactsStorageException::class)
|
||||
override fun prepareForUpload() {
|
||||
val uid = UUID.randomUUID().toString()
|
||||
|
||||
@ -145,27 +194,14 @@ class LocalGroup : AndroidGroup, LocalResource {
|
||||
fileName = uid
|
||||
}
|
||||
|
||||
override fun contentValues(): ContentValues {
|
||||
val values = super.contentValues()
|
||||
|
||||
val members = Parcel.obtain()
|
||||
members.writeStringList(contact.members)
|
||||
values.put(COLUMN_PENDING_MEMBERS, members.marshall())
|
||||
|
||||
members.recycle()
|
||||
return values
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Marks all members of the current group as dirty.
|
||||
*/
|
||||
@Throws(ContactsStorageException::class)
|
||||
fun markMembersDirty() {
|
||||
assertID()
|
||||
val batch = BatchOperation(addressBook.provider)
|
||||
] val batch = BatchOperation(addressBook.provider!!)
|
||||
|
||||
for (member in members)
|
||||
for (member in getMembers())
|
||||
batch.enqueue(BatchOperation.Operation(
|
||||
ContentProviderOperation.newUpdate(addressBook.syncAdapterURI(ContentUris.withAppendedId(RawContacts.CONTENT_URI, member)))
|
||||
.withValue(RawContacts.DIRTY, 1)
|
||||
@ -175,117 +211,46 @@ class LocalGroup : AndroidGroup, LocalResource {
|
||||
batch.commit()
|
||||
}
|
||||
|
||||
override fun resetDeleted() {
|
||||
val values = ContentValues(1)
|
||||
values.put(Groups.DELETED, 0)
|
||||
addressBook.provider!!.update(groupSyncUri(), values, null, null)
|
||||
}
|
||||
|
||||
|
||||
// helpers
|
||||
|
||||
private fun assertID() {
|
||||
if (id == null)
|
||||
throw IllegalStateException("Group has not been saved yet")
|
||||
private fun groupSyncUri(): Uri {
|
||||
val id = requireNotNull(id)
|
||||
return ContentUris.withAppendedId(addressBook.groupsSyncUri(), id)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "LocalGroup(super=" + super.toString() + ", uuid=" + this.uuid + ")"
|
||||
/**
|
||||
* Lists all members of this group.
|
||||
* @return list of all members' raw contact IDs
|
||||
* @throws RemoteException on contact provider errors
|
||||
*/
|
||||
internal fun getMembers(): List<Long> {
|
||||
val id = requireNotNull(id)
|
||||
val members = LinkedList<Long>()
|
||||
addressBook.provider!!.query(
|
||||
addressBook.syncAdapterURI(ContactsContract.Data.CONTENT_URI),
|
||||
arrayOf(Data.RAW_CONTACT_ID),
|
||||
"${GroupMembership.MIMETYPE}=? AND ${GroupMembership.GROUP_ROW_ID}=?",
|
||||
arrayOf(GroupMembership.CONTENT_ITEM_TYPE, id.toString()),
|
||||
null
|
||||
)?.use { cursor ->
|
||||
while (cursor.moveToNext())
|
||||
members += cursor.getLong(0)
|
||||
}
|
||||
return members
|
||||
}
|
||||
|
||||
|
||||
// factory
|
||||
|
||||
internal class Factory : AndroidGroupFactory() {
|
||||
|
||||
override fun newInstance(addressBook: AndroidAddressBook, id: Long, fileName: String, eTag: String): LocalGroup {
|
||||
return LocalGroup(addressBook, id, fileName, eTag)
|
||||
}
|
||||
|
||||
override fun newInstance(addressBook: AndroidAddressBook, contact: Contact, fileName: String, eTag: String): LocalGroup {
|
||||
return LocalGroup(addressBook, contact, fileName, eTag)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<LocalGroup> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val INSTANCE = Factory()
|
||||
}
|
||||
|
||||
object Factory: AndroidGroupFactory<LocalGroup> {
|
||||
override fun fromProvider(addressBook: AndroidAddressBook<out AndroidContact, LocalGroup>, values: ContentValues) =
|
||||
LocalGroup(addressBook, values)
|
||||
}
|
||||
|
||||
companion object {
|
||||
/** marshalled list of member UIDs, as sent by server */
|
||||
val COLUMN_PENDING_MEMBERS = Groups.SYNC3
|
||||
|
||||
/**
|
||||
* Processes all groups with non-null [.COLUMN_PENDING_MEMBERS]: the pending memberships
|
||||
* are (if possible) applied, keeping cached memberships in sync.
|
||||
* @param addressBook address book to take groups from
|
||||
* @throws ContactsStorageException on contact provider errors
|
||||
*/
|
||||
@Throws(ContactsStorageException::class)
|
||||
fun applyPendingMemberships(addressBook: LocalAddressBook) {
|
||||
try {
|
||||
val cursor = addressBook.provider.query(
|
||||
addressBook.syncAdapterURI(Groups.CONTENT_URI),
|
||||
arrayOf(Groups._ID, COLUMN_PENDING_MEMBERS),
|
||||
"$COLUMN_PENDING_MEMBERS IS NOT NULL", arrayOf(), null
|
||||
)
|
||||
|
||||
val batch = BatchOperation(addressBook.provider)
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
val id = cursor.getLong(0)
|
||||
App.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<Long>()
|
||||
|
||||
// delete all memberships and cached memberships for this group
|
||||
for (contact in addressBook.getByGroupMembership(id)) {
|
||||
contact.removeGroupMemberships(batch)
|
||||
changeContactIDs.add(contact.id)
|
||||
}
|
||||
|
||||
// extract list of member UIDs
|
||||
val members = LinkedList<String>()
|
||||
val raw = cursor.getBlob(1)
|
||||
val parcel = Parcel.obtain()
|
||||
parcel.unmarshall(raw, 0, raw.size)
|
||||
parcel.setDataPosition(0)
|
||||
parcel.readStringList(members)
|
||||
parcel.recycle()
|
||||
|
||||
// insert memberships
|
||||
for (uid in members) {
|
||||
App.log.fine("Assigning member: $uid")
|
||||
try {
|
||||
val member = addressBook.findContactByUID(uid)
|
||||
member.addToGroup(batch, id)
|
||||
changeContactIDs.add(member.id)
|
||||
} catch (e: FileNotFoundException) {
|
||||
App.log.log(Level.WARNING, "Group member not found: $uid", e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
// workaround for Android 7 which sets DIRTY flag when only meta-data is changed
|
||||
for (contactID in changeContactIDs) {
|
||||
val contact = LocalContact(addressBook, contactID, null, null)
|
||||
contact.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)
|
||||
))
|
||||
|
||||
batch.commit()
|
||||
}
|
||||
cursor!!.close()
|
||||
} catch (e: RemoteException) {
|
||||
throw ContactsStorageException("Couldn't get pending memberships", e)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,12 +8,10 @@
|
||||
|
||||
package com.etesync.syncadapter.resource
|
||||
|
||||
import java.io.IOException
|
||||
|
||||
import at.bitfire.ical4android.CalendarStorageException
|
||||
import at.bitfire.vcard4android.ContactsStorageException
|
||||
|
||||
interface LocalResource {
|
||||
interface LocalResource<in TData: Any> {
|
||||
val uuid: String?
|
||||
|
||||
/** True if doesn't exist on server yet, false otherwise. */
|
||||
@ -22,13 +20,9 @@ interface LocalResource {
|
||||
/** Returns a string of how this should be represented for example: vCard. */
|
||||
val content: String
|
||||
|
||||
@Throws(CalendarStorageException::class, ContactsStorageException::class)
|
||||
fun delete(): Int
|
||||
|
||||
@Throws(CalendarStorageException::class, ContactsStorageException::class)
|
||||
fun prepareForUpload()
|
||||
|
||||
@Throws(CalendarStorageException::class, ContactsStorageException::class)
|
||||
fun clearDirty(eTag: String)
|
||||
|
||||
}
|
||||
|
@ -10,70 +10,62 @@ package com.etesync.syncadapter.resource
|
||||
|
||||
import android.content.ContentProviderOperation
|
||||
import android.content.ContentValues
|
||||
import android.os.RemoteException
|
||||
import android.provider.CalendarContract.Events
|
||||
|
||||
import com.etesync.syncadapter.Constants
|
||||
|
||||
import net.fortuna.ical4j.model.property.ProdId
|
||||
|
||||
import org.dmfs.provider.tasks.TaskContract.Tasks
|
||||
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import java.text.ParseException
|
||||
import java.util.UUID
|
||||
|
||||
import android.text.TextUtils
|
||||
import at.bitfire.ical4android.AndroidTask
|
||||
import at.bitfire.ical4android.AndroidTaskFactory
|
||||
import at.bitfire.ical4android.AndroidTaskList
|
||||
import at.bitfire.ical4android.CalendarStorageException
|
||||
import at.bitfire.ical4android.Task
|
||||
import at.bitfire.vcard4android.ContactsStorageException
|
||||
import com.etesync.syncadapter.App
|
||||
import org.dmfs.tasks.contract.TaskContract
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.util.*
|
||||
import java.util.logging.Level
|
||||
|
||||
class LocalTask : AndroidTask, LocalResource<Task> {
|
||||
companion object {
|
||||
internal const val COLUMN_ETAG = TaskContract.Tasks.SYNC1
|
||||
internal const val COLUMN_UID = TaskContract.Tasks.SYNC2
|
||||
internal const val COLUMN_SEQUENCE = TaskContract.Tasks.SYNC3
|
||||
}
|
||||
|
||||
class LocalTask : AndroidTask, LocalResource {
|
||||
private var fileName: String? = null
|
||||
var eTag: String? = null
|
||||
|
||||
override val content: String
|
||||
@Throws(IOException::class, ContactsStorageException::class)
|
||||
get() = ""
|
||||
get() {
|
||||
App.log.log(Level.FINE, "Preparing upload of task " + fileName!!, task)
|
||||
|
||||
val os = ByteArrayOutputStream()
|
||||
task?.write(os)
|
||||
|
||||
return os.toString()
|
||||
}
|
||||
|
||||
override val isLocalOnly: Boolean
|
||||
get() = false
|
||||
get() = TextUtils.isEmpty(eTag)
|
||||
|
||||
override// Now the same
|
||||
val uuid: String?
|
||||
get() = fileName
|
||||
|
||||
constructor(taskList: AndroidTaskList, task: Task, fileName: String?, eTag: String?) : super(taskList, task) {
|
||||
constructor(taskList: AndroidTaskList<*>, task: Task, fileName: String?, eTag: String?, flags: Int)
|
||||
: super(taskList, task) {
|
||||
this.fileName = fileName
|
||||
this.eTag = eTag
|
||||
}
|
||||
|
||||
protected constructor(taskList: AndroidTaskList, id: Long, baseInfo: ContentValues?) : super(taskList, id) {
|
||||
if (baseInfo != null) {
|
||||
fileName = baseInfo.getAsString(Events._SYNC_ID)
|
||||
eTag = baseInfo.getAsString(COLUMN_ETAG)
|
||||
}
|
||||
private constructor(taskList: AndroidTaskList<*>, values: ContentValues): super(taskList) {
|
||||
id = values.getAsLong(TaskContract.Tasks._ID)
|
||||
fileName = values.getAsString(TaskContract.Tasks._SYNC_ID)
|
||||
eTag = values.getAsString(COLUMN_ETAG)
|
||||
}
|
||||
|
||||
|
||||
/* process LocalTask-specific fields */
|
||||
|
||||
@Throws(FileNotFoundException::class, RemoteException::class, ParseException::class)
|
||||
override fun populateTask(values: ContentValues) {
|
||||
super.populateTask(values)
|
||||
|
||||
fileName = values.getAsString(Events._SYNC_ID)
|
||||
eTag = values.getAsString(COLUMN_ETAG)
|
||||
task.uid = values.getAsString(COLUMN_UID)
|
||||
|
||||
task.sequence = values.getAsInteger(COLUMN_SEQUENCE)
|
||||
}
|
||||
|
||||
override fun buildTask(builder: ContentProviderOperation.Builder, update: Boolean) {
|
||||
super.buildTask(builder, update)
|
||||
builder.withValue(Tasks._SYNC_ID, fileName)
|
||||
builder.withValue(TaskContract.Tasks._SYNC_ID, fileName)
|
||||
.withValue(COLUMN_UID, task.uid)
|
||||
.withValue(COLUMN_SEQUENCE, task.sequence)
|
||||
.withValue(COLUMN_ETAG, eTag)
|
||||
@ -82,71 +74,34 @@ class LocalTask : AndroidTask, LocalResource {
|
||||
|
||||
/* custom queries */
|
||||
|
||||
@Throws(CalendarStorageException::class)
|
||||
override fun prepareForUpload() {
|
||||
try {
|
||||
val uid = UUID.randomUUID().toString()
|
||||
val newFileName = "$uid.ics"
|
||||
val uid = UUID.randomUUID().toString()
|
||||
|
||||
val values = ContentValues(2)
|
||||
values.put(Tasks._SYNC_ID, newFileName)
|
||||
values.put(COLUMN_UID, uid)
|
||||
taskList.provider.client.update(taskSyncURI(), values, null, null)
|
||||
|
||||
fileName = newFileName
|
||||
if (task != null)
|
||||
task.uid = uid
|
||||
|
||||
} catch (e: RemoteException) {
|
||||
throw CalendarStorageException("Couldn't update UID", e)
|
||||
}
|
||||
val values = ContentValues(2)
|
||||
values.put(TaskContract.Tasks._SYNC_ID, uid)
|
||||
values.put(COLUMN_UID, uid)
|
||||
taskList.provider.client.update(taskSyncURI(), values, null, null)
|
||||
|
||||
fileName = uid
|
||||
val task = this.task
|
||||
if (task != null)
|
||||
task.uid = uid
|
||||
}
|
||||
|
||||
@Throws(CalendarStorageException::class)
|
||||
override fun clearDirty(eTag: String) {
|
||||
try {
|
||||
val values = ContentValues(2)
|
||||
values.put(Tasks._DIRTY, 0)
|
||||
values.put(COLUMN_ETAG, eTag)
|
||||
if (task != null)
|
||||
values.put(COLUMN_SEQUENCE, task.sequence)
|
||||
taskList.provider.client.update(taskSyncURI(), values, null, null)
|
||||
|
||||
this.eTag = eTag
|
||||
} catch (e: RemoteException) {
|
||||
throw CalendarStorageException("Couldn't update _DIRTY/ETag/SEQUENCE", e)
|
||||
}
|
||||
val values = ContentValues(2)
|
||||
values.put(TaskContract.Tasks._DIRTY, 0)
|
||||
values.put(COLUMN_ETAG, eTag)
|
||||
if (task != null)
|
||||
values.put(COLUMN_SEQUENCE, task?.sequence)
|
||||
taskList.provider.client.update(taskSyncURI(), values, null, null)
|
||||
|
||||
this.eTag = eTag
|
||||
}
|
||||
|
||||
|
||||
internal class Factory : AndroidTaskFactory {
|
||||
|
||||
override fun newInstance(taskList: AndroidTaskList, id: Long, baseInfo: ContentValues): LocalTask {
|
||||
return LocalTask(taskList, id, baseInfo)
|
||||
}
|
||||
|
||||
override fun newInstance(taskList: AndroidTaskList, task: Task): LocalTask {
|
||||
return LocalTask(taskList, task, null, null)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<LocalTask?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val INSTANCE = Factory()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
init {
|
||||
Task.prodId = ProdId(Constants.PRODID_BASE + " ical4j/2.x")
|
||||
}
|
||||
|
||||
internal val COLUMN_ETAG = Tasks.SYNC1
|
||||
internal val COLUMN_UID = Tasks.SYNC2
|
||||
internal val COLUMN_SEQUENCE = Tasks.SYNC3
|
||||
object Factory: AndroidTaskFactory<LocalTask> {
|
||||
override fun fromProvider(taskList: AndroidTaskList<*>, values: ContentValues) =
|
||||
LocalTask(taskList, values)
|
||||
}
|
||||
}
|
||||
|
@ -149,7 +149,7 @@ class DebugInfoActivity : BaseActivity(), LoaderManager.LoaderCallbacks<String>
|
||||
|
||||
report.append("CONFIGURATION\n")
|
||||
// power saving
|
||||
val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
|
||||
val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager?
|
||||
if (powerManager != null && Build.VERSION.SDK_INT >= 23)
|
||||
report.append("Power saving disabled: ")
|
||||
.append(if (powerManager.isIgnoringBatteryOptimizations(BuildConfig.APPLICATION_ID)) "yes" else "no")
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 268473341cb761a0676f1746ff4467e48973f972
|
||||
Subproject commit fef93f94bbc1265e53e55c95fe86e8c33e2e4f0f
|
@ -1 +1 @@
|
||||
Subproject commit 3974799d7790f47987f7ae95fe444ab4442e7786
|
||||
Subproject commit 42d5cc3f8b16c628fa13a5a3b0f211e6660fb084
|
Loading…
Reference in New Issue
Block a user