mirror of
https://github.com/etesync/android
synced 2024-11-15 20:38:58 +00:00
Implement Tasks support via OpenTasks
This adds support for tasks via OpenTasks. https://github.com/dmfs/opentasks Need the OpenTasks client for it to be used. Currently you can't create new task lists. You can only have the default one, but that's just a UI thing. Fixes #7
This commit is contained in:
parent
c15f894a71
commit
7f2ab44bca
@ -106,6 +106,19 @@
|
||||
android:name="android.content.SyncAdapter"
|
||||
android:resource="@xml/sync_calendars" />
|
||||
</service>
|
||||
<service
|
||||
android:name=".syncadapter.TasksSyncAdapterService"
|
||||
android:exported="true"
|
||||
tools:ignore="ExportedService">
|
||||
<intent-filter>
|
||||
<action android:name="android.content.SyncAdapter"/>
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.content.SyncAdapter"
|
||||
android:resource="@xml/sync_tasks"/>
|
||||
</service>
|
||||
|
||||
|
||||
<!-- Address book account -->
|
||||
<service
|
||||
|
@ -51,7 +51,8 @@ class CollectionInfo : Serializable {
|
||||
|
||||
enum class Type {
|
||||
ADDRESS_BOOK,
|
||||
CALENDAR
|
||||
CALENDAR,
|
||||
TASKS,
|
||||
}
|
||||
|
||||
init {
|
||||
|
@ -146,7 +146,15 @@ public class JournalModel {
|
||||
CollectionInfo.Type type;
|
||||
|
||||
public static ServiceEntity fetch(EntityDataStore<Persistable> data, String account, CollectionInfo.Type type) {
|
||||
return data.select(ServiceEntity.class).where(ServiceEntity.ACCOUNT.eq(account).and(ServiceEntity.TYPE.eq(type))).limit(1).get().firstOrNull();
|
||||
ServiceEntity service = data.select(ServiceEntity.class).where(ServiceEntity.ACCOUNT.eq(account).and(ServiceEntity.TYPE.eq(type))).limit(1).get().firstOrNull();
|
||||
if (service == null) {
|
||||
// If our first time, create service and a journal
|
||||
ServiceEntity serviceEntity = new ServiceEntity();
|
||||
serviceEntity.account = account;
|
||||
serviceEntity.type = CollectionInfo.Type.TASKS;
|
||||
service = data.insert(serviceEntity);
|
||||
}
|
||||
return service;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,7 +151,7 @@ class LocalAddressBook(
|
||||
_mainAccount = newMainAccount
|
||||
}
|
||||
|
||||
var url: String
|
||||
override var url: String
|
||||
get() = AccountManager.get(context).getUserData(account, USER_DATA_URL)
|
||||
?: throw IllegalStateException("Address book has no URL")
|
||||
set(url) = AccountManager.get(context).setUserData(account, USER_DATA_URL, url)
|
||||
|
@ -88,6 +88,9 @@ class LocalCalendar private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override val url: String?
|
||||
get() = name
|
||||
|
||||
fun update(journalEntity: JournalEntity, updateColor: Boolean) =
|
||||
update(valuesFromCollectionInfo(journalEntity, updateColor))
|
||||
|
||||
|
@ -9,6 +9,8 @@
|
||||
package com.etesync.syncadapter.resource
|
||||
|
||||
interface LocalCollection<out T: LocalResource<*>> {
|
||||
val url: String?
|
||||
|
||||
fun findDeleted(): List<T>
|
||||
fun findDirty(): List<T>
|
||||
fun findWithoutFileName(): List<T>
|
||||
|
@ -48,7 +48,7 @@ class LocalTask : AndroidTask, LocalResource<Task> {
|
||||
val uuid: String?
|
||||
get() = fileName
|
||||
|
||||
constructor(taskList: AndroidTaskList<*>, task: Task, fileName: String?, eTag: String?, flags: Int)
|
||||
constructor(taskList: AndroidTaskList<*>, task: Task, fileName: String?, eTag: String?)
|
||||
: super(taskList, task) {
|
||||
this.fileName = fileName
|
||||
this.eTag = eTag
|
||||
@ -63,6 +63,15 @@ class LocalTask : AndroidTask, LocalResource<Task> {
|
||||
|
||||
/* process LocalTask-specific fields */
|
||||
|
||||
override fun populateTask(values: ContentValues) {
|
||||
super.populateTask(values)
|
||||
fileName = values.getAsString(TaskContract.Tasks._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(TaskContract.Tasks._SYNC_ID, fileName)
|
||||
@ -75,7 +84,14 @@ class LocalTask : AndroidTask, LocalResource<Task> {
|
||||
/* custom queries */
|
||||
|
||||
override fun prepareForUpload() {
|
||||
val uid = UUID.randomUUID().toString()
|
||||
var uid: String? = null
|
||||
val c = taskList.provider.client.query(taskSyncURI(), arrayOf(COLUMN_UID), null, null, null)
|
||||
if (c.moveToNext())
|
||||
uid = c.getString(0)
|
||||
if (uid == null)
|
||||
uid = UUID.randomUUID().toString()
|
||||
|
||||
c.close()
|
||||
|
||||
val values = ContentValues(2)
|
||||
values.put(TaskContract.Tasks._SYNC_ID, uid)
|
||||
|
@ -62,6 +62,9 @@ class LocalTaskList private constructor(
|
||||
|
||||
}
|
||||
|
||||
override val url: String?
|
||||
get() = syncId
|
||||
|
||||
fun update(journalEntity: JournalEntity, updateColor: Boolean) =
|
||||
update(valuesFromCollectionInfo(journalEntity, updateColor))
|
||||
|
||||
|
@ -10,9 +10,7 @@ package com.etesync.syncadapter.syncadapter
|
||||
import android.accounts.Account
|
||||
import android.annotation.TargetApi
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SyncResult
|
||||
import android.content.res.Resources
|
||||
import android.os.Bundle
|
||||
|
||||
import com.etesync.syncadapter.AccountSettings
|
||||
@ -40,7 +38,6 @@ import com.etesync.syncadapter.ui.ViewCollectionActivity
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import java.util.ArrayList
|
||||
import java.util.Arrays
|
||||
import java.util.LinkedList
|
||||
import java.util.Locale
|
||||
import java.util.logging.Level
|
||||
|
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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.syncadapter
|
||||
|
||||
import android.accounts.Account
|
||||
import android.accounts.AccountManager
|
||||
import android.content.ContentProviderClient
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.content.SyncResult
|
||||
import android.database.sqlite.SQLiteException
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import at.bitfire.ical4android.AndroidTaskList
|
||||
import at.bitfire.ical4android.TaskProvider
|
||||
import com.etesync.syncadapter.*
|
||||
import com.etesync.syncadapter.journalmanager.Exceptions
|
||||
import com.etesync.syncadapter.model.CollectionInfo
|
||||
import com.etesync.syncadapter.model.JournalEntity
|
||||
import com.etesync.syncadapter.model.JournalModel
|
||||
import com.etesync.syncadapter.model.ServiceEntity
|
||||
import com.etesync.syncadapter.resource.LocalTaskList
|
||||
import com.etesync.syncadapter.ui.DebugInfoActivity
|
||||
import okhttp3.HttpUrl
|
||||
import org.dmfs.tasks.contract.TaskContract
|
||||
import java.util.*
|
||||
import java.util.logging.Level
|
||||
|
||||
/**
|
||||
* Synchronization manager for CalDAV collections; handles tasks ({@code VTODO}).
|
||||
*/
|
||||
class TasksSyncAdapterService: SyncAdapterService() {
|
||||
|
||||
override fun syncAdapter() = TasksSyncAdapter(this)
|
||||
|
||||
|
||||
class TasksSyncAdapter(
|
||||
context: Context
|
||||
): SyncAdapter(context) {
|
||||
|
||||
override fun onPerformSync(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) {
|
||||
super.onPerformSync(account, extras, authority, provider, syncResult)
|
||||
|
||||
val notificationManager = NotificationHelper(context, "journals-tasks", Constants.NOTIFICATION_TASK_SYNC)
|
||||
notificationManager.cancel()
|
||||
|
||||
try {
|
||||
val taskProvider = TaskProvider.fromProviderClient(context, provider)
|
||||
|
||||
// make sure account can be seen by OpenTasks
|
||||
if (Build.VERSION.SDK_INT >= 26)
|
||||
AccountManager.get(context).setAccountVisibility(account, taskProvider.name.packageName, AccountManager.VISIBILITY_VISIBLE)
|
||||
|
||||
val accountSettings = AccountSettings(context, account)
|
||||
/* don't run sync if
|
||||
- sync conditions (e.g. "sync only in WiFi") are not met AND
|
||||
- this is is an automatic sync (i.e. manual syncs are run regardless of sync conditions)
|
||||
*/
|
||||
if (!extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL) && !checkSyncConditions(accountSettings))
|
||||
return
|
||||
|
||||
RefreshCollections(account, CollectionInfo.Type.TASKS).run()
|
||||
|
||||
updateLocalTaskLists(taskProvider, account, accountSettings)
|
||||
|
||||
val principal = HttpUrl.get(accountSettings.uri!!)!!
|
||||
|
||||
for (taskList in AndroidTaskList.find(account, taskProvider, LocalTaskList.Factory, "${TaskContract.TaskLists.SYNC_ENABLED}!=0", null)) {
|
||||
App.log.info("Synchronizing task list #${taskList.id} [${taskList.syncId}]")
|
||||
val tasksSyncManager = TasksSyncManager(context, account, accountSettings, extras, authority, syncResult, taskList, principal);
|
||||
tasksSyncManager.performSync()
|
||||
}
|
||||
} catch (e: Exceptions.ServiceUnavailableException) {
|
||||
syncResult.stats.numIoExceptions++
|
||||
syncResult.delayUntil = if (e.retryAfter > 0) e.retryAfter else Constants.DEFAULT_RETRY_DELAY
|
||||
} catch (e: Exception) {
|
||||
if (e is SQLiteException) {
|
||||
App.log.log(Level.SEVERE, "Couldn't prepare local task list", e)
|
||||
syncResult.databaseError = true
|
||||
}
|
||||
|
||||
val syncPhase = R.string.sync_phase_journals
|
||||
val title = context.getString(R.string.sync_error_tasks, account.name)
|
||||
|
||||
notificationManager.setThrowable(e)
|
||||
|
||||
val detailsIntent = notificationManager.detailsIntent
|
||||
detailsIntent.putExtra(Constants.KEY_ACCOUNT, account)
|
||||
if (e !is Exceptions.UnauthorizedException) {
|
||||
detailsIntent.putExtra(DebugInfoActivity.KEY_AUTHORITY, authority)
|
||||
detailsIntent.putExtra(DebugInfoActivity.KEY_PHASE, syncPhase)
|
||||
}
|
||||
|
||||
notificationManager.notify(title, context.getString(syncPhase))
|
||||
} catch (e: OutOfMemoryError) {
|
||||
val syncPhase = R.string.sync_phase_journals
|
||||
val title = context.getString(R.string.sync_error_tasks, account.name)
|
||||
notificationManager.setThrowable(e)
|
||||
val detailsIntent = notificationManager.detailsIntent
|
||||
detailsIntent.putExtra(Constants.KEY_ACCOUNT, account)
|
||||
notificationManager.notify(title, context.getString(syncPhase))
|
||||
}
|
||||
|
||||
App.log.info("Task sync complete")
|
||||
}
|
||||
|
||||
private fun updateLocalTaskLists(provider: TaskProvider, account: Account, settings: AccountSettings) {
|
||||
val data = (context.applicationContext as App).data
|
||||
var service = JournalModel.Service.fetch(data, account.name, CollectionInfo.Type.TASKS)
|
||||
|
||||
val remote = HashMap<String, JournalEntity>()
|
||||
val remoteJournals = JournalEntity.getJournals(data, service)
|
||||
for (journalEntity in remoteJournals) {
|
||||
remote[journalEntity.uid] = journalEntity
|
||||
}
|
||||
|
||||
val local = AndroidTaskList.find(account, provider, LocalTaskList.Factory, null, null)
|
||||
|
||||
val updateColors = settings.manageCalendarColors
|
||||
|
||||
// delete obsolete local TaskList
|
||||
for (taskList in local) {
|
||||
val url = taskList.url
|
||||
val journalEntity = remote[url]
|
||||
if (journalEntity == null) {
|
||||
App.log.fine("Deleting obsolete local task list $url")
|
||||
taskList.delete()
|
||||
} else {
|
||||
// remote CollectionInfo found for this local collection, update data
|
||||
App.log.fine("Updating local task list $url with $journalEntity")
|
||||
taskList.update(journalEntity, updateColors)
|
||||
// we already have a local tasks for this remote collection, don't take into consideration anymore
|
||||
remote.remove(url)
|
||||
}
|
||||
}
|
||||
|
||||
// create new local taskss
|
||||
for (url in remote.keys) {
|
||||
val journalEntity = remote[url]!!
|
||||
App.log.info("Adding local task list $journalEntity")
|
||||
LocalTaskList.create(account, provider, journalEntity)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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.syncadapter
|
||||
|
||||
import android.accounts.Account
|
||||
import android.content.Context
|
||||
import android.content.SyncResult
|
||||
import android.os.Bundle
|
||||
import at.bitfire.ical4android.Event
|
||||
import at.bitfire.ical4android.InvalidCalendarException
|
||||
import at.bitfire.ical4android.Task
|
||||
import com.etesync.syncadapter.AccountSettings
|
||||
import com.etesync.syncadapter.App
|
||||
import com.etesync.syncadapter.Constants
|
||||
import com.etesync.syncadapter.R
|
||||
import com.etesync.syncadapter.journalmanager.JournalEntryManager
|
||||
import com.etesync.syncadapter.model.CollectionInfo
|
||||
import com.etesync.syncadapter.model.SyncEntry
|
||||
import com.etesync.syncadapter.resource.LocalEvent
|
||||
import com.etesync.syncadapter.resource.LocalTask
|
||||
import com.etesync.syncadapter.resource.LocalTaskList
|
||||
import okhttp3.HttpUrl
|
||||
import java.io.Reader
|
||||
import java.io.StringReader
|
||||
import java.util.logging.Level
|
||||
|
||||
/**
|
||||
* Synchronization manager for CalDAV collections; handles tasks (VTODO)
|
||||
*/
|
||||
class TasksSyncManager(
|
||||
context: Context,
|
||||
account: Account,
|
||||
accountSettings: AccountSettings,
|
||||
extras: Bundle,
|
||||
authority: String,
|
||||
syncResult: SyncResult,
|
||||
taskList: LocalTaskList,
|
||||
private val remote: HttpUrl
|
||||
): SyncManager<LocalTask>(context, account, accountSettings, extras, authority, syncResult, taskList.url!!, CollectionInfo.Type.TASKS, account.name) {
|
||||
|
||||
override val syncErrorTitle: String
|
||||
get() = context.getString(R.string.sync_error_tasks, account.name)
|
||||
|
||||
override val syncSuccessfullyTitle: String
|
||||
get() = context.getString(R.string.sync_successfully_tasks, info.displayName,
|
||||
account.name)
|
||||
|
||||
init {
|
||||
localCollection = taskList
|
||||
}
|
||||
|
||||
override fun notificationId(): Int {
|
||||
return Constants.NOTIFICATION_TASK_SYNC
|
||||
}
|
||||
|
||||
override fun prepare(): Boolean {
|
||||
if (!super.prepare())
|
||||
return false
|
||||
|
||||
journal = JournalEntryManager(httpClient, remote, localTaskList().url!!)
|
||||
return true
|
||||
}
|
||||
|
||||
// helpers
|
||||
|
||||
private fun localTaskList(): LocalTaskList {
|
||||
return localCollection as LocalTaskList
|
||||
}
|
||||
|
||||
override fun processSyncEntry(cEntry: SyncEntry) {
|
||||
val inputReader = StringReader(cEntry.content)
|
||||
|
||||
val tasks = Task.fromReader(inputReader)
|
||||
if (tasks.size == 0) {
|
||||
App.log.warning("Received VCard without data, ignoring")
|
||||
return
|
||||
} else if (tasks.size > 1) {
|
||||
App.log.warning("Received multiple VCALs, using first one")
|
||||
}
|
||||
|
||||
val event = tasks[0]
|
||||
val local = localCollection!!.findByUid(event.uid!!)
|
||||
|
||||
if (cEntry.isAction(SyncEntry.Actions.ADD) || cEntry.isAction(SyncEntry.Actions.CHANGE)) {
|
||||
processTask(event, local)
|
||||
} else {
|
||||
if (local != null) {
|
||||
App.log.info("Removing local record #" + local.id + " which has been deleted on the server")
|
||||
local.delete()
|
||||
} else {
|
||||
App.log.warning("Tried deleting a non-existent record: " + event.uid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun processTask(newData: Task, localTask: LocalTask?): LocalTask {
|
||||
var localTask = localTask
|
||||
// delete local Task, if it exists
|
||||
if (localTask != null) {
|
||||
App.log.info("Updating " + newData.uid + " in local calendar")
|
||||
localTask.eTag = newData.uid
|
||||
localTask.update(newData)
|
||||
syncResult.stats.numUpdates++
|
||||
} else {
|
||||
App.log.info("Adding " + newData.uid + " to local calendar")
|
||||
localTask = LocalTask(localTaskList(), newData, newData.uid, newData.uid)
|
||||
localTask.add()
|
||||
syncResult.stats.numInserts++
|
||||
}
|
||||
|
||||
return localTask
|
||||
}
|
||||
}
|
@ -278,29 +278,32 @@ class AccountActivity : BaseActivity(), Toolbar.OnMenuItemClickListener, PopupMe
|
||||
for (serviceEntity in data.select(ServiceEntity::class.java).where(ServiceEntity.ACCOUNT.eq(account.name)).get()) {
|
||||
val id = serviceEntity.id.toLong()
|
||||
val service = serviceEntity.type
|
||||
if (service == CollectionInfo.Type.ADDRESS_BOOK) {
|
||||
info.carddav = AccountInfo.ServiceInfo()
|
||||
info.carddav!!.id = id
|
||||
info.carddav!!.refreshing = davService != null && davService!!.isRefreshing(id) || ContentResolver.isSyncActive(account, App.addressBooksAuthority)
|
||||
info.carddav!!.journals = JournalEntity.getJournals(data, serviceEntity)
|
||||
when (service) {
|
||||
CollectionInfo.Type.ADDRESS_BOOK -> {
|
||||
info.carddav = AccountInfo.ServiceInfo()
|
||||
info.carddav!!.id = id
|
||||
info.carddav!!.refreshing = davService != null && davService!!.isRefreshing(id) || ContentResolver.isSyncActive(account, App.addressBooksAuthority)
|
||||
info.carddav!!.journals = JournalEntity.getJournals(data, serviceEntity)
|
||||
|
||||
val accountManager = AccountManager.get(context)
|
||||
for (addrBookAccount in accountManager.getAccountsByType(App.addressBookAccountType)) {
|
||||
val addressBook = LocalAddressBook(context, addrBookAccount, null)
|
||||
try {
|
||||
if (account == addressBook.mainAccount)
|
||||
info.carddav!!.refreshing = info.carddav!!.refreshing or ContentResolver.isSyncActive(addrBookAccount, ContactsContract.AUTHORITY)
|
||||
} catch (e: ContactsStorageException) {
|
||||
}
|
||||
|
||||
val accountManager = AccountManager.get(context)
|
||||
for (addrBookAccount in accountManager.getAccountsByType(App.addressBookAccountType)) {
|
||||
val addressBook = LocalAddressBook(context, addrBookAccount, null)
|
||||
try {
|
||||
if (account == addressBook.mainAccount)
|
||||
info.carddav!!.refreshing = info.carddav!!.refreshing or ContentResolver.isSyncActive(addrBookAccount, ContactsContract.AUTHORITY)
|
||||
} catch (e: ContactsStorageException) {
|
||||
}
|
||||
|
||||
}
|
||||
} else if (service == CollectionInfo.Type.CALENDAR) {
|
||||
info.caldav = AccountInfo.ServiceInfo()
|
||||
info.caldav!!.id = id
|
||||
info.caldav!!.refreshing = davService != null && davService!!.isRefreshing(id) ||
|
||||
ContentResolver.isSyncActive(account, CalendarContract.AUTHORITY) ||
|
||||
ContentResolver.isSyncActive(account, TaskProvider.ProviderName.OpenTasks.authority)
|
||||
info.caldav!!.journals = JournalEntity.getJournals(data, serviceEntity)
|
||||
CollectionInfo.Type.CALENDAR -> {
|
||||
info.caldav = AccountInfo.ServiceInfo()
|
||||
info.caldav!!.id = id
|
||||
info.caldav!!.refreshing = davService != null && davService!!.isRefreshing(id) ||
|
||||
ContentResolver.isSyncActive(account, CalendarContract.AUTHORITY) ||
|
||||
ContentResolver.isSyncActive(account, TaskProvider.ProviderName.OpenTasks.authority)
|
||||
info.caldav!!.journals = JournalEntity.getJournals(data, serviceEntity)
|
||||
}
|
||||
}
|
||||
}
|
||||
return info
|
||||
|
@ -43,26 +43,29 @@ open class CreateCollectionActivity : BaseActivity() {
|
||||
setContentView(R.layout.activity_create_collection)
|
||||
|
||||
val displayName = findViewById<View>(R.id.display_name) as EditText
|
||||
if (info!!.type == CollectionInfo.Type.CALENDAR) {
|
||||
setTitle(R.string.create_calendar)
|
||||
displayName.setHint(R.string.create_calendar_display_name_hint)
|
||||
when (info.type) {
|
||||
CollectionInfo.Type.CALENDAR, CollectionInfo.Type.TASKS -> {
|
||||
setTitle(R.string.create_calendar)
|
||||
displayName.setHint(R.string.create_calendar_display_name_hint)
|
||||
|
||||
val colorSquare = findViewById<View>(R.id.color)
|
||||
colorSquare.setOnClickListener {
|
||||
AmbilWarnaDialog(this@CreateCollectionActivity, (colorSquare.background as ColorDrawable).color, true, object : AmbilWarnaDialog.OnAmbilWarnaListener {
|
||||
override fun onCancel(dialog: AmbilWarnaDialog) {}
|
||||
val colorSquare = findViewById<View>(R.id.color)
|
||||
colorSquare.setOnClickListener {
|
||||
AmbilWarnaDialog(this@CreateCollectionActivity, (colorSquare.background as ColorDrawable).color, true, object : AmbilWarnaDialog.OnAmbilWarnaListener {
|
||||
override fun onCancel(dialog: AmbilWarnaDialog) {}
|
||||
|
||||
override fun onOk(dialog: AmbilWarnaDialog, color: Int) {
|
||||
colorSquare.setBackgroundColor(color)
|
||||
}
|
||||
}).show()
|
||||
override fun onOk(dialog: AmbilWarnaDialog, color: Int) {
|
||||
colorSquare.setBackgroundColor(color)
|
||||
}
|
||||
}).show()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setTitle(R.string.create_addressbook)
|
||||
displayName.setHint(R.string.create_addressbook_display_name_hint)
|
||||
CollectionInfo.Type.ADDRESS_BOOK -> {
|
||||
setTitle(R.string.create_addressbook)
|
||||
displayName.setHint(R.string.create_addressbook_display_name_hint)
|
||||
|
||||
val colorGroup = findViewById<View>(R.id.color_group)
|
||||
colorGroup.visibility = View.GONE
|
||||
val colorGroup = findViewById<View>(R.id.color_group)
|
||||
colorGroup.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ import android.support.v4.app.DialogFragment
|
||||
import android.support.v4.app.LoaderManager
|
||||
import android.support.v4.content.AsyncTaskLoader
|
||||
import android.support.v4.content.Loader
|
||||
import at.bitfire.ical4android.TaskProvider
|
||||
import com.etesync.syncadapter.*
|
||||
import com.etesync.syncadapter.journalmanager.Crypto
|
||||
import com.etesync.syncadapter.journalmanager.Exceptions
|
||||
@ -83,17 +84,15 @@ class CreateCollectionFragment : DialogFragment(), LoaderManager.LoaderCallbacks
|
||||
|
||||
override fun loadInBackground(): Exception? {
|
||||
try {
|
||||
var authority: String
|
||||
var authority: String = ""
|
||||
|
||||
val data = (context.applicationContext as App).data
|
||||
|
||||
// 1. find service ID
|
||||
if (info.type == CollectionInfo.Type.ADDRESS_BOOK) {
|
||||
authority = App.addressBooksAuthority
|
||||
} else if (info.type == CollectionInfo.Type.CALENDAR) {
|
||||
authority = CalendarContract.AUTHORITY
|
||||
} else {
|
||||
throw IllegalArgumentException("Collection must be an address book or calendar")
|
||||
when (info.type){
|
||||
CollectionInfo.Type.ADDRESS_BOOK -> authority = App.addressBooksAuthority
|
||||
CollectionInfo.Type.CALENDAR -> authority = CalendarContract.AUTHORITY
|
||||
CollectionInfo.Type.TASKS -> authority = TaskProvider.ProviderName.OpenTasks.authority
|
||||
}
|
||||
|
||||
val serviceEntity = JournalModel.Service.fetch(data, account.name, info.type)
|
||||
|
@ -80,7 +80,7 @@ class PermissionsActivity : BaseActivity() {
|
||||
val PERMISSION_WRITE_TASKS = "org.dmfs.permission.WRITE_TASKS"
|
||||
|
||||
fun requestAllPermissions(activity: Activity) {
|
||||
ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.READ_CALENDAR, Manifest.permission.WRITE_CALENDAR, Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS), REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS)
|
||||
ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.READ_CALENDAR, Manifest.permission.WRITE_CALENDAR, Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS, PERMISSION_READ_TASKS, PERMISSION_WRITE_TASKS), REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -306,6 +306,7 @@
|
||||
<string name="sync_error_permissions_text">Additional permissions required</string>
|
||||
<string name="sync_error_calendar">Calendar sync failed (%s)</string>
|
||||
<string name="sync_error_contacts">Contacts sync failed (%s)</string>
|
||||
<string name="sync_error_tasks">Tasks sync failed (%s)</string>
|
||||
<string name="sync_error">Error while %s</string>
|
||||
<string name="sync_error_integrity">Integrity error while %s</string>
|
||||
<string name="sync_error_http_dav">Server error while %s</string>
|
||||
@ -325,6 +326,7 @@
|
||||
<string name="sync_error_user_inactive">User is inactive</string>
|
||||
<string name="sync_successfully_calendar" formatted="false">Calendar \"%s\" modified (%s)</string>
|
||||
<string name="sync_successfully_contacts" formatted="false">Contacts modified (%s)</string>
|
||||
<string name="sync_successfully_tasks" formatted="false">Tasks \"%s\" modified (%s)</string>
|
||||
<string name="sync_successfully_modified" formatted="false">%s modified.</string>
|
||||
<string name="sync_successfully_modified_full" formatted="false">%s added.\n%s updated.\n%s deleted.</string>
|
||||
|
||||
|
12
app/src/main/res/xml/sync_tasks.xml
Normal file
12
app/src/main/res/xml/sync_tasks.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<!--
|
||||
~ 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
|
||||
-->
|
||||
|
||||
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accountType="@string/account_type"
|
||||
android:contentAuthority="org.dmfs.tasks"
|
||||
android:supportsUploading="true" />
|
Loading…
Reference in New Issue
Block a user