You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
etesync-android/app/src/main/java/com/etesync/syncadapter/syncadapter/CalendarSyncManager.kt

207 lines
7.7 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* Copyright © 2013 2015 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.syncadapter
import android.accounts.Account
import android.content.Context
import android.content.SyncResult
import android.os.Build
import android.os.Bundle
import at.bitfire.ical4android.CalendarStorageException
import at.bitfire.ical4android.Event
import at.bitfire.ical4android.InvalidCalendarException
import at.bitfire.vcard4android.ContactsStorageException
import com.etebase.client.Item
import com.etesync.syncadapter.AccountSettings
import com.etesync.syncadapter.Constants
import com.etesync.syncadapter.R
import com.etesync.journalmanager.Exceptions
import com.etesync.journalmanager.JournalEntryManager
import com.etesync.syncadapter.log.Logger
import com.etesync.syncadapter.model.CollectionInfo
import com.etesync.journalmanager.model.SyncEntry
import com.etesync.syncadapter.resource.LocalCalendar
import com.etesync.syncadapter.resource.LocalEvent
import com.etesync.syncadapter.utils.EventEmailInvitation
import okhttp3.HttpUrl
import java.io.IOException
import java.io.StringReader
/**
*
* Synchronization manager for CardDAV collections; handles contacts and groups.
*/
class CalendarSyncManager @Throws(Exceptions.IntegrityException::class, Exceptions.GenericCryptoException::class)
constructor(context: Context, account: Account, settings: AccountSettings, extras: Bundle, authority: String, result: SyncResult, calendar: LocalCalendar, private val remote: HttpUrl) : SyncManager<LocalEvent>(context, account, settings, extras, authority, result, calendar.name!!, CollectionInfo.Type.CALENDAR, account.name) {
override val syncErrorTitle: String
get() = context.getString(R.string.sync_error_calendar, account.name)
override val syncSuccessfullyTitle: String
get() = context.getString(R.string.sync_successfully_calendar, localCalendar().displayName,
account.name)
init {
localCollection = calendar
}
override fun notificationId(): Int {
return Constants.NOTIFICATION_CALENDAR_SYNC
}
@Throws(ContactsStorageException::class, CalendarStorageException::class)
override fun prepare(): Boolean {
if (!super.prepare())
return false
if (isLegacy) {
journal = JournalEntryManager(httpClient.okHttpClient, remote, localCalendar().name!!)
}
return true
}
@Throws(CalendarStorageException::class, ContactsStorageException::class)
override fun prepareDirty() {
super.prepareDirty()
localCalendar().processDirtyExceptions()
}
// helpers
private fun localCalendar(): LocalCalendar {
return localCollection as LocalCalendar
}
override fun processItem(item: Item) {
val local = localCollection!!.findByFilename(item.uid)
if (!item.isDeleted) {
val inputReader = StringReader(String(item.content))
val events = Event.eventsFromReader(inputReader)
if (events.size == 0) {
Logger.log.warning("Received VCard without data, ignoring")
return
} else if (events.size > 1) {
Logger.log.warning("Received multiple VCALs, using first one")
}
val event = events[0]
processEvent(item, event, local)
} else {
if (local != null) {
Logger.log.info("Removing local record #" + local.id + " which has been deleted on the server")
local.delete()
} else {
Logger.log.warning("Tried deleting a non-existent record: " + item.uid)
}
}
}
@Throws(IOException::class, ContactsStorageException::class, CalendarStorageException::class, InvalidCalendarException::class)
override fun processSyncEntryImpl(cEntry: SyncEntry) {
val inputReader = StringReader(cEntry.content)
val events = Event.eventsFromReader(inputReader)
if (events.size == 0) {
Logger.log.warning("Received VCard without data, ignoring")
return
} else if (events.size > 1) {
Logger.log.warning("Received multiple VCALs, using first one")
}
val event = events[0]
val local = localCollection!!.findByUid(event.uid!!)
if (cEntry.isAction(SyncEntry.Actions.ADD) || cEntry.isAction(SyncEntry.Actions.CHANGE)) {
legacyProcessEvent(event, local)
} else {
if (local != null) {
Logger.log.info("Removing local record #" + local.id + " which has been deleted on the server")
local.delete()
} else {
Logger.log.warning("Tried deleting a non-existent record: " + event.uid)
}
}
}
@Throws(CalendarStorageException::class, ContactsStorageException::class, IOException::class)
override fun prepareLocal() {
super.prepareLocal()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
createInviteAttendeesNotification()
}
}
@Throws(CalendarStorageException::class, ContactsStorageException::class, IOException::class)
private fun createInviteAttendeesNotification() {
for (local in localDirty) {
val event = local.event
if (event?.attendees?.isEmpty()!!) {
return
}
createInviteAttendeesNotification(event, local.content)
}
}
private fun createInviteAttendeesNotification(event: Event, icsContent: String) {
val intent = EventEmailInvitation(context, account).createIntent(event, icsContent)
if (intent != null) {
val notificationHelper = SyncNotification(context, icsContent, event.hashCode())
notificationHelper.notify(
context.getString(
R.string.sync_calendar_attendees_notification_title, event.summary),
context.getString(R.string.sync_calendar_attendees_notification_content), null,
intent,
R.drawable.ic_email_black)
}
}
private fun processEvent(item: Item, newData: Event, _localEvent: LocalEvent?): LocalEvent {
var localEvent = _localEvent
// delete local event, if it exists
if (localEvent != null) {
Logger.log.info("Updating " + newData.uid + " in local calendar")
localEvent.eTag = item.etag
localEvent.update(newData)
syncResult.stats.numUpdates++
} else {
Logger.log.info("Adding " + newData.uid + " to local calendar")
localEvent = LocalEvent(localCalendar(), newData, item.uid, item.etag)
localEvent.add()
syncResult.stats.numInserts++
}
return localEvent
}
@Throws(IOException::class, ContactsStorageException::class, CalendarStorageException::class)
private fun legacyProcessEvent(newData: Event, _localEvent: LocalEvent?): LocalEvent {
var localEvent = _localEvent
// delete local event, if it exists
if (localEvent != null) {
Logger.log.info("Updating " + newData.uid + " in local calendar")
localEvent.eTag = newData.uid
localEvent.update(newData)
syncResult.stats.numUpdates++
} else {
Logger.log.info("Adding " + newData.uid + " to local calendar")
localEvent = LocalEvent(localCalendar(), newData, newData.uid, newData.uid)
localEvent.add()
syncResult.stats.numInserts++
}
return localEvent
}
}