1
0
mirror of https://github.com/etesync/android synced 2024-11-22 16:08:13 +00:00

Add Tasks.org support

This commit is contained in:
Alex Baker 2020-08-04 07:58:48 -05:00
parent a81973f816
commit e01f54c687
20 changed files with 199 additions and 87 deletions

View File

@ -110,6 +110,19 @@
android:resource="@xml/sync_tasks"/> android:resource="@xml/sync_tasks"/>
</service> </service>
<service
android:name=".syncadapter.TasksOrgSyncAdapterService"
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_org"/>
</service>
<!-- Address book account --> <!-- Address book account -->
<service <service

View File

@ -24,6 +24,7 @@ import android.provider.ContactsContract
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import at.bitfire.ical4android.AndroidCalendar import at.bitfire.ical4android.AndroidCalendar
import at.bitfire.ical4android.CalendarStorageException import at.bitfire.ical4android.CalendarStorageException
import at.bitfire.ical4android.TaskProvider.Companion.OPENTASK_PROVIDERS
import at.bitfire.vcard4android.ContactsStorageException import at.bitfire.vcard4android.ContactsStorageException
import com.etesync.syncadapter.log.Logger import com.etesync.syncadapter.log.Logger
import com.etesync.syncadapter.model.* import com.etesync.syncadapter.model.*
@ -85,8 +86,10 @@ class App : Application() {
tasksFilter.addDataScheme("package") tasksFilter.addDataScheme("package")
registerReceiver(PackageChangedReceiver(), tasksFilter) registerReceiver(PackageChangedReceiver(), tasksFilter)
// check whether a tasks app is currently installed OPENTASK_PROVIDERS.forEach {
PackageChangedReceiver.updateTaskSync(this@App) // check whether a tasks app is currently installed
PackageChangedReceiver.updateTaskSync(this@App, it)
}
} }
} }

View File

@ -16,6 +16,7 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.provider.CalendarContract import android.provider.CalendarContract
import at.bitfire.ical4android.TaskProvider import at.bitfire.ical4android.TaskProvider
import at.bitfire.ical4android.TaskProvider.ProviderName
import com.etesync.syncadapter.log.Logger import com.etesync.syncadapter.log.Logger
import com.etesync.syncadapter.resource.LocalTaskList import com.etesync.syncadapter.resource.LocalTaskList
@ -23,15 +24,18 @@ class PackageChangedReceiver : BroadcastReceiver() {
@SuppressLint("MissingPermission") @SuppressLint("MissingPermission")
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
if (Intent.ACTION_PACKAGE_ADDED == intent.action || Intent.ACTION_PACKAGE_FULLY_REMOVED == intent.action) if (Intent.ACTION_PACKAGE_ADDED == intent.action || Intent.ACTION_PACKAGE_FULLY_REMOVED == intent.action) {
updateTaskSync(context) TaskProvider.OPENTASK_PROVIDERS.forEach {
updateTaskSync(context, it)
}
}
} }
companion object { companion object {
internal fun updateTaskSync(context: Context) { internal fun updateTaskSync(context: Context, provider: ProviderName) {
val tasksInstalled = LocalTaskList.tasksProviderAvailable(context) val tasksInstalled = LocalTaskList.tasksProviderAvailable(context, provider)
Logger.log.info("Package (un)installed; OpenTasks provider now available = $tasksInstalled") Logger.log.info("Package (un)installed; ${provider.name} provider now available = $tasksInstalled")
for (account in AccountManager.get(context).getAccountsByType(App.accountType)) { for (account in AccountManager.get(context).getAccountsByType(App.accountType)) {
val settings = AccountSettings(context, account) val settings = AccountSettings(context, account)
@ -40,12 +44,12 @@ class PackageChangedReceiver : BroadcastReceiver() {
if (tasksInstalled) { if (tasksInstalled) {
if (calendarSyncInterval == null) { if (calendarSyncInterval == null) {
// do nothing atm // do nothing atm
} else if (ContentResolver.getIsSyncable(account, TaskProvider.ProviderName.OpenTasks.authority) <= 0) { } else if (ContentResolver.getIsSyncable(account, provider.authority) <= 0) {
ContentResolver.setIsSyncable(account, TaskProvider.ProviderName.OpenTasks.authority, 1) ContentResolver.setIsSyncable(account, provider.authority, 1)
settings.setSyncInterval(TaskProvider.ProviderName.OpenTasks.authority, calendarSyncInterval) settings.setSyncInterval(provider.authority, calendarSyncInterval)
} }
} else { } else {
ContentResolver.setIsSyncable(account, TaskProvider.ProviderName.OpenTasks.authority, 0) ContentResolver.setIsSyncable(account, provider.authority, 0)
} }
} }
} }

View File

@ -18,6 +18,7 @@ import at.bitfire.ical4android.AndroidTaskList
import at.bitfire.ical4android.AndroidTaskListFactory import at.bitfire.ical4android.AndroidTaskListFactory
import at.bitfire.ical4android.CalendarStorageException import at.bitfire.ical4android.CalendarStorageException
import at.bitfire.ical4android.TaskProvider import at.bitfire.ical4android.TaskProvider
import at.bitfire.ical4android.TaskProvider.ProviderName
import com.etesync.syncadapter.model.JournalEntity import com.etesync.syncadapter.model.JournalEntity
import org.dmfs.tasks.contract.TaskContract.TaskLists import org.dmfs.tasks.contract.TaskContract.TaskLists
import org.dmfs.tasks.contract.TaskContract.Tasks import org.dmfs.tasks.contract.TaskContract.Tasks
@ -30,12 +31,12 @@ class LocalTaskList private constructor(
companion object { companion object {
val defaultColor = -0x743cb6 // light green 500 val defaultColor = -0x743cb6 // light green 500
fun tasksProviderAvailable(context: Context): Boolean { fun tasksProviderAvailable(context: Context, provider: ProviderName): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
return context.packageManager.resolveContentProvider(TaskProvider.ProviderName.OpenTasks.authority, 0) != null return context.packageManager.resolveContentProvider(provider.authority, 0) != null
else { else {
try { try {
TaskProvider.acquire(context, TaskProvider.ProviderName.OpenTasks)?.use { TaskProvider.acquire(context, provider)?.use {
return true return true
} }
} catch (e: Exception) { } catch (e: Exception) {

View File

@ -4,12 +4,13 @@ import android.accounts.Account
import android.content.ContentResolver import android.content.ContentResolver
import android.os.Bundle import android.os.Bundle
import android.provider.CalendarContract import android.provider.CalendarContract
import at.bitfire.ical4android.TaskProvider import at.bitfire.ical4android.TaskProvider.Companion.OPENTASK_PROVIDERS
import com.etesync.syncadapter.App import com.etesync.syncadapter.App
fun requestSync(account: Account?) { fun requestSync(account: Account?) {
val authorities = arrayOf(App.addressBooksAuthority, CalendarContract.AUTHORITY, TaskProvider.ProviderName.OpenTasks.authority) val authorities = arrayOf(App.addressBooksAuthority, CalendarContract.AUTHORITY) +
OPENTASK_PROVIDERS.map { it.authority }
for (authority in authorities) { for (authority in authorities) {
val extras = Bundle() val extras = Bundle()

View File

@ -0,0 +1,20 @@
/*
* 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 at.bitfire.ical4android.TaskProvider
/**
* Synchronization manager for CalDAV collections; handles tasks ({@code VTODO}).
*/
class TasksOrgSyncAdapterService: SyncAdapterService() {
override fun syncAdapter() =
TasksSyncAdapterService.TasksSyncAdapter(this, TaskProvider.ProviderName.TasksOrg)
}

View File

@ -17,6 +17,7 @@ import android.os.Build
import android.os.Bundle import android.os.Bundle
import at.bitfire.ical4android.AndroidTaskList import at.bitfire.ical4android.AndroidTaskList
import at.bitfire.ical4android.TaskProvider import at.bitfire.ical4android.TaskProvider
import at.bitfire.ical4android.TaskProvider.ProviderName
import com.etesync.syncadapter.AccountSettings import com.etesync.syncadapter.AccountSettings
import com.etesync.syncadapter.App import com.etesync.syncadapter.App
import com.etesync.syncadapter.Constants import com.etesync.syncadapter.Constants
@ -35,18 +36,18 @@ import java.util.*
*/ */
class TasksSyncAdapterService: SyncAdapterService() { class TasksSyncAdapterService: SyncAdapterService() {
override fun syncAdapter() = TasksSyncAdapter(this) override fun syncAdapter() = TasksSyncAdapter(this, ProviderName.OpenTasks)
class TasksSyncAdapter( class TasksSyncAdapter(
context: Context context: Context,
private val name: ProviderName
): SyncAdapter(context) { ): SyncAdapter(context) {
override val syncErrorTitle = R.string.sync_error_tasks override val syncErrorTitle = R.string.sync_error_tasks
override val notificationManager = SyncNotification(context, "journals-tasks", Constants.NOTIFICATION_TASK_SYNC) override val notificationManager = SyncNotification(context, "journals-tasks", Constants.NOTIFICATION_TASK_SYNC)
override fun onPerformSyncDo(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) { override fun onPerformSyncDo(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) {
val taskProvider = TaskProvider.fromProviderClient(context, provider) val taskProvider = TaskProvider.fromProviderClient(context, provider, name)
// make sure account can be seen by OpenTasks // make sure account can be seen by OpenTasks
if (Build.VERSION.SDK_INT >= 26) if (Build.VERSION.SDK_INT >= 26)

View File

@ -26,6 +26,7 @@ import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import at.bitfire.ical4android.TaskProvider import at.bitfire.ical4android.TaskProvider
import at.bitfire.ical4android.TaskProvider.Companion.OPENTASK_PROVIDERS
import at.bitfire.vcard4android.ContactsStorageException import at.bitfire.vcard4android.ContactsStorageException
import com.etesync.syncadapter.* import com.etesync.syncadapter.*
import com.etesync.journalmanager.Crypto import com.etesync.journalmanager.Crypto
@ -378,7 +379,9 @@ class AccountActivity : BaseActivity(), Toolbar.OnMenuItemClickListener, PopupMe
info.taskdav = AccountInfo.ServiceInfo() info.taskdav = AccountInfo.ServiceInfo()
info.taskdav!!.id = id info.taskdav!!.id = id
info.taskdav!!.refreshing = davService != null && davService!!.isRefreshing(id) || info.taskdav!!.refreshing = davService != null && davService!!.isRefreshing(id) ||
ContentResolver.isSyncActive(account, TaskProvider.ProviderName.OpenTasks.authority) OPENTASK_PROVIDERS.any {
ContentResolver.isSyncActive(account, it.authority)
}
info.taskdav!!.journals = JournalEntity.getJournals(data, serviceEntity) info.taskdav!!.journals = JournalEntity.getJournals(data, serviceEntity)
} }
} }

View File

@ -23,6 +23,7 @@ import androidx.loader.content.AsyncTaskLoader
import androidx.loader.content.Loader import androidx.loader.content.Loader
import androidx.preference.* import androidx.preference.*
import at.bitfire.ical4android.TaskProvider import at.bitfire.ical4android.TaskProvider
import at.bitfire.ical4android.TaskProvider.Companion.OPENTASK_PROVIDERS
import com.etesync.syncadapter.* import com.etesync.syncadapter.*
import com.etesync.syncadapter.Constants.KEY_ACCOUNT import com.etesync.syncadapter.Constants.KEY_ACCOUNT
import com.etesync.syncadapter.R import com.etesync.syncadapter.R
@ -121,7 +122,9 @@ class AccountSettingsActivity : BaseActivity() {
val newInterval = java.lang.Long.parseLong(newValue as String) val newInterval = java.lang.Long.parseLong(newValue as String)
settings.setSyncInterval(App.addressBooksAuthority, newInterval) settings.setSyncInterval(App.addressBooksAuthority, newInterval)
settings.setSyncInterval(CalendarContract.AUTHORITY, newInterval) settings.setSyncInterval(CalendarContract.AUTHORITY, newInterval)
settings.setSyncInterval(TaskProvider.ProviderName.OpenTasks.authority, newInterval) OPENTASK_PROVIDERS.forEach {
settings.setSyncInterval(it.authority, newInterval)
}
loaderManager.restartLoader(0, arguments, this@AccountSettingsFragment) loaderManager.restartLoader(0, arguments, this@AccountSettingsFragment)
false false
} }

View File

@ -20,6 +20,7 @@ import androidx.loader.app.LoaderManager
import androidx.loader.content.AsyncTaskLoader import androidx.loader.content.AsyncTaskLoader
import androidx.loader.content.Loader import androidx.loader.content.Loader
import at.bitfire.ical4android.TaskProvider import at.bitfire.ical4android.TaskProvider
import at.bitfire.ical4android.TaskProvider.Companion.OPENTASK_PROVIDERS
import com.etesync.syncadapter.* import com.etesync.syncadapter.*
import com.etesync.journalmanager.Crypto import com.etesync.journalmanager.Crypto
import com.etesync.journalmanager.Exceptions import com.etesync.journalmanager.Exceptions
@ -84,15 +85,14 @@ class CreateCollectionFragment : DialogFragment(), LoaderManager.LoaderCallbacks
override fun loadInBackground(): Exception? { override fun loadInBackground(): Exception? {
try { try {
var authority: String = ""
val data = (context.applicationContext as App).data val data = (context.applicationContext as App).data
// 1. find service ID // 1. find service ID
when (info.enumType){ val authorities = when (info.enumType){
CollectionInfo.Type.ADDRESS_BOOK -> authority = App.addressBooksAuthority CollectionInfo.Type.ADDRESS_BOOK -> listOf(App.addressBooksAuthority)
CollectionInfo.Type.CALENDAR -> authority = CalendarContract.AUTHORITY CollectionInfo.Type.CALENDAR -> listOf(CalendarContract.AUTHORITY)
CollectionInfo.Type.TASKS -> authority = TaskProvider.ProviderName.OpenTasks.authority CollectionInfo.Type.TASKS -> OPENTASK_PROVIDERS.map { it.authority }
else -> emptyList()
} }
val serviceEntity = JournalModel.Service.fetchOrCreate(data, account.name, info.enumType) val serviceEntity = JournalModel.Service.fetchOrCreate(data, account.name, info.enumType)
@ -127,7 +127,7 @@ class CreateCollectionFragment : DialogFragment(), LoaderManager.LoaderCallbacks
journalManager.update(journal) journalManager.update(journal)
} }
requestSync(authority) authorities.forEach { requestSync(it) }
} catch (e: IllegalStateException) { } catch (e: IllegalStateException) {
return e return e
} catch (e: Exceptions.HttpException) { } catch (e: Exceptions.HttpException) {

View File

@ -25,6 +25,7 @@ import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.widget.TextView import android.widget.TextView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import at.bitfire.ical4android.TaskProvider.ProviderName
import at.bitfire.vcard4android.ContactsStorageException import at.bitfire.vcard4android.ContactsStorageException
import com.etesync.syncadapter.* import com.etesync.syncadapter.*
import com.etesync.syncadapter.Constants.KEY_ACCOUNT import com.etesync.syncadapter.Constants.KEY_ACCOUNT
@ -156,7 +157,7 @@ class DebugInfoActivity : BaseActivity(), LoaderManager.LoaderCallbacks<String>
.append(if (powerManager.isIgnoringBatteryOptimizations(BuildConfig.APPLICATION_ID)) "yes" else "no") .append(if (powerManager.isIgnoringBatteryOptimizations(BuildConfig.APPLICATION_ID)) "yes" else "no")
.append("\n") .append("\n")
// permissions // permissions
for (permission in arrayOf(Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS, Manifest.permission.READ_CALENDAR, Manifest.permission.WRITE_CALENDAR, PermissionsActivity.PERMISSION_READ_TASKS, PermissionsActivity.PERMISSION_WRITE_TASKS)) for (permission in arrayOf(Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS, Manifest.permission.READ_CALENDAR, Manifest.permission.WRITE_CALENDAR) + ProviderName.OpenTasks.permissions + ProviderName.TasksOrg.permissions)
report.append(permission).append(" permission: ") report.append(permission).append(" permission: ")
.append(if (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED) "granted" else "denied") .append(if (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED) "granted" else "denied")
.append("\n") .append("\n")
@ -169,7 +170,7 @@ class DebugInfoActivity : BaseActivity(), LoaderManager.LoaderCallbacks<String>
for (acct in accountManager.getAccountsByType(context.getString(R.string.account_type))) for (acct in accountManager.getAccountsByType(context.getString(R.string.account_type)))
try { try {
val settings = AccountSettings(context, acct) val settings = AccountSettings(context, acct)
report.append("Account: ").append(acct.name).append("\n" + " Address book sync. interval: ").append(syncStatus(settings, App.addressBooksAuthority)).append("\n" + " Calendar sync. interval: ").append(syncStatus(settings, CalendarContract.AUTHORITY)).append("\n" + " OpenTasks sync. interval: ").append(syncStatus(settings, "org.dmfs.tasks")).append("\n" + " WiFi only: ").append(settings.syncWifiOnly) report.append("Account: ").append(acct.name).append("\n" + " Address book sync. interval: ").append(syncStatus(settings, App.addressBooksAuthority)).append("\n" + " Calendar sync. interval: ").append(syncStatus(settings, CalendarContract.AUTHORITY)).append("\n" + " OpenTasks sync. interval: ").append(syncStatus(settings, ProviderName.OpenTasks.authority)).append("\n" + " Tasks.org sync. interval: ").append(syncStatus(settings, ProviderName.TasksOrg.authority)).append("\n" + " WiFi only: ").append(settings.syncWifiOnly)
if (settings.syncWifiOnlySSID != null) if (settings.syncWifiOnlySSID != null)
report.append(", SSID: ").append(settings.syncWifiOnlySSID) report.append(", SSID: ").append(settings.syncWifiOnlySSID)
report.append("\n [CardDAV] Contact group method: ").append(settings.groupMethod) report.append("\n [CardDAV] Contact group method: ").append(settings.groupMethod)

View File

@ -19,6 +19,7 @@ import at.bitfire.ical4android.Event
import at.bitfire.ical4android.InvalidCalendarException import at.bitfire.ical4android.InvalidCalendarException
import at.bitfire.ical4android.Task import at.bitfire.ical4android.Task
import at.bitfire.ical4android.TaskProvider import at.bitfire.ical4android.TaskProvider
import at.bitfire.ical4android.TaskProvider.Companion.OPENTASK_PROVIDERS
import at.bitfire.vcard4android.Contact import at.bitfire.vcard4android.Contact
import com.etesync.syncadapter.App import com.etesync.syncadapter.App
import com.etesync.syncadapter.Constants import com.etesync.syncadapter.Constants
@ -116,15 +117,17 @@ class JournalItemActivity : BaseActivity(), Refreshable {
} }
} }
CollectionInfo.Type.TASKS -> { CollectionInfo.Type.TASKS -> {
val provider = TaskProvider.acquire(this, TaskProvider.ProviderName.OpenTasks)!! OPENTASK_PROVIDERS.forEach {
val localTaskList = LocalTaskList.findByName(account, provider, LocalTaskList.Factory, info.uid!!)!! val provider = TaskProvider.acquire(this, it)!!
val task = Task.tasksFromReader(StringReader(syncEntry.content))[0] val localTaskList = LocalTaskList.findByName(account, provider, LocalTaskList.Factory, info.uid!!)!!
var localTask = localTaskList.findByUid(task.uid!!) val task = Task.tasksFromReader(StringReader(syncEntry.content))[0]
if (localTask != null) { var localTask = localTaskList.findByUid(task.uid!!)
localTask.updateAsDirty(task) if (localTask != null) {
} else { localTask.updateAsDirty(task)
localTask = LocalTask(localTaskList, task, task.uid, null) } else {
localTask.addAsDirty() localTask = LocalTask(localTaskList, task, task.uid, null)
localTask.addAsDirty()
}
} }
} }
CollectionInfo.Type.ADDRESS_BOOK -> { CollectionInfo.Type.ADDRESS_BOOK -> {

View File

@ -13,8 +13,11 @@ import android.app.Activity
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.annotation.IdRes
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import at.bitfire.ical4android.TaskProvider.Companion.OPENTASK_PROVIDERS
import at.bitfire.ical4android.TaskProvider.ProviderName
import com.etesync.syncadapter.Constants import com.etesync.syncadapter.Constants
import com.etesync.syncadapter.R import com.etesync.syncadapter.R
import com.etesync.syncadapter.resource.LocalTaskList import com.etesync.syncadapter.resource.LocalTaskList
@ -38,16 +41,10 @@ class PermissionsActivity : BaseActivity() {
val noContactsPermissions = ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_CONTACTS) != PackageManager.PERMISSION_GRANTED val noContactsPermissions = ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_CONTACTS) != PackageManager.PERMISSION_GRANTED
findViewById<View>(R.id.contacts_permissions).visibility = if (noContactsPermissions) View.VISIBLE else View.GONE findViewById<View>(R.id.contacts_permissions).visibility = if (noContactsPermissions) View.VISIBLE else View.GONE
val noTaskPermissions: Boolean val needOpenTaskPermissions = setupPermissions(ProviderName.OpenTasks, R.id.opentasks_permissions)
if (LocalTaskList.tasksProviderAvailable(this)) { val needTasksOrgPermissions = setupPermissions(ProviderName.TasksOrg, R.id.tasksorg_permissions)
noTaskPermissions = ActivityCompat.checkSelfPermission(this, PERMISSION_READ_TASKS) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(this, PERMISSION_WRITE_TASKS) != PackageManager.PERMISSION_GRANTED
findViewById<View>(R.id.opentasks_permissions).visibility = if (noTaskPermissions) View.VISIBLE else View.GONE
} else {
findViewById<View>(R.id.opentasks_permissions).visibility = View.GONE
noTaskPermissions = false
}
if (!noCalendarPermissions && !noContactsPermissions && !noTaskPermissions) { if (!noCalendarPermissions && !noContactsPermissions && !(needOpenTaskPermissions || needTasksOrgPermissions)) {
val nm = NotificationManagerCompat.from(this) val nm = NotificationManagerCompat.from(this)
nm.cancel(Constants.NOTIFICATION_PERMISSIONS) nm.cancel(Constants.NOTIFICATION_PERMISSIONS)
@ -55,6 +52,15 @@ class PermissionsActivity : BaseActivity() {
} }
} }
private fun setupPermissions(provider: ProviderName, @IdRes id: Int): Boolean {
val providerAvailable = LocalTaskList.tasksProviderAvailable(this, provider)
val hasPermissions = providerAvailable && provider.permissions.all {
ActivityCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED
}
findViewById<View>(id).visibility = if (hasPermissions) View.GONE else View.VISIBLE
return providerAvailable && !hasPermissions
}
fun requestCalendarPermissions(v: View) { fun requestCalendarPermissions(v: View) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_CALENDAR, Manifest.permission.WRITE_CALENDAR), 0) ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_CALENDAR, Manifest.permission.WRITE_CALENDAR), 0)
} }
@ -64,7 +70,11 @@ class PermissionsActivity : BaseActivity() {
} }
fun requestOpenTasksPermissions(v: View) { fun requestOpenTasksPermissions(v: View) {
ActivityCompat.requestPermissions(this, arrayOf(PERMISSION_READ_TASKS, PERMISSION_WRITE_TASKS), 0) ActivityCompat.requestPermissions(this, ProviderName.OpenTasks.permissions, 0)
}
fun requestTasksOrgPermissions(v: View) {
ActivityCompat.requestPermissions(this, ProviderName.TasksOrg.permissions, 0)
} }
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
@ -75,11 +85,8 @@ class PermissionsActivity : BaseActivity() {
companion object { companion object {
private val REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 124 private val REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 124
val PERMISSION_READ_TASKS = "org.dmfs.permission.READ_TASKS"
val PERMISSION_WRITE_TASKS = "org.dmfs.permission.WRITE_TASKS"
fun requestAllPermissions(activity: Activity) { fun requestAllPermissions(activity: Activity) {
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) ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.READ_CALENDAR, Manifest.permission.WRITE_CALENDAR, Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS) + OPENTASK_PROVIDERS.flatMap { it.permissions.toList() }, REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS)
} }
} }
} }

View File

@ -24,6 +24,7 @@ import android.widget.TextView
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import at.bitfire.ical4android.CalendarStorageException import at.bitfire.ical4android.CalendarStorageException
import at.bitfire.ical4android.TaskProvider import at.bitfire.ical4android.TaskProvider
import at.bitfire.ical4android.TaskProvider.Companion.OPENTASK_PROVIDERS
import at.bitfire.vcard4android.ContactsStorageException import at.bitfire.vcard4android.ContactsStorageException
import com.etesync.syncadapter.App import com.etesync.syncadapter.App
import com.etesync.syncadapter.Constants import com.etesync.syncadapter.Constants
@ -212,7 +213,9 @@ class ViewCollectionActivity : BaseActivity(), Refreshable {
} }
CollectionInfo.Type.TASKS -> { CollectionInfo.Type.TASKS -> {
try { try {
val providerClient = TaskProvider.acquire(this@ViewCollectionActivity, TaskProvider.ProviderName.OpenTasks) val providerClient = OPENTASK_PROVIDERS.mapNotNull {
TaskProvider.acquire(this@ViewCollectionActivity, it)
}.firstOrNull()
if (providerClient == null) { if (providerClient == null) {
return null return null
} }

View File

@ -15,6 +15,7 @@ import android.provider.CalendarContract
import android.provider.ContactsContract import android.provider.ContactsContract
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import at.bitfire.ical4android.* import at.bitfire.ical4android.*
import at.bitfire.ical4android.TaskProvider.Companion.OPENTASK_PROVIDERS
import at.bitfire.vcard4android.BatchOperation import at.bitfire.vcard4android.BatchOperation
import at.bitfire.vcard4android.Contact import at.bitfire.vcard4android.Contact
import at.bitfire.vcard4android.ContactsStorageException import at.bitfire.vcard4android.ContactsStorageException
@ -282,40 +283,44 @@ class ImportFragment : DialogFragment() {
finishParsingFile(tasks.size) finishParsingFile(tasks.size)
val provider = TaskProvider.acquire(context, TaskProvider.ProviderName.OpenTasks) val providers = OPENTASK_PROVIDERS.mapNotNull {
if (provider == null) { TaskProvider.acquire(context, it)
}
if (providers.isEmpty()) {
result.e = Exception("Failed to acquire tasks content provider.") result.e = Exception("Failed to acquire tasks content provider.")
return result return result
} }
val localTaskList: LocalTaskList? providers.forEach {
try { val localTaskList: LocalTaskList?
localTaskList = LocalTaskList.findByName(account, provider, LocalTaskList.Factory, info.uid!!)
if (localTaskList == null) {
throw FileNotFoundException("Failed to load local resource.")
}
} catch (e: FileNotFoundException) {
Logger.log.info("Fail" + e.localizedMessage)
result.e = e
return result
}
for (task in tasks) {
try { try {
var localTask = localTaskList.findByUid(task.uid!!) localTaskList = LocalTaskList.findByName(account, it, LocalTaskList.Factory, info.uid!!)
if (localTask != null) { if (localTaskList == null) {
localTask.updateAsDirty(task) throw FileNotFoundException("Failed to load local resource.")
result.updated++
} else {
localTask = LocalTask(localTaskList, task, task.uid, null)
localTask.addAsDirty()
result.added++
} }
} catch (e: CalendarStorageException) { } catch (e: FileNotFoundException) {
e.printStackTrace() Logger.log.info("Fail" + e.localizedMessage)
result.e = e
return result
} }
entryProcessed() for (task in tasks) {
try {
var localTask = localTaskList.findByUid(task.uid!!)
if (localTask != null) {
localTask.updateAsDirty(task)
result.updated++
} else {
localTask = LocalTask(localTaskList, task, task.uid, null)
localTask.addAsDirty()
result.added++
}
} catch (e: CalendarStorageException) {
e.printStackTrace()
}
entryProcessed()
}
} }
} else if (info.enumType == CollectionInfo.Type.ADDRESS_BOOK) { } else if (info.enumType == CollectionInfo.Type.ADDRESS_BOOK) {
val uidToLocalId = HashMap<String?, Long>() val uidToLocalId = HashMap<String?, Long>()

View File

@ -21,6 +21,7 @@ import android.provider.CalendarContract
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import at.bitfire.ical4android.TaskProvider import at.bitfire.ical4android.TaskProvider
import at.bitfire.ical4android.TaskProvider.Companion.OPENTASK_PROVIDERS
import com.etesync.syncadapter.* import com.etesync.syncadapter.*
import com.etesync.journalmanager.Crypto import com.etesync.journalmanager.Crypto
import com.etesync.journalmanager.Exceptions import com.etesync.journalmanager.Exceptions
@ -153,11 +154,13 @@ class SetupEncryptionFragment : DialogFragment() {
// calendar sync is automatically enabled by isAlwaysSyncable="true" in res/xml/sync_contacts.xml // calendar sync is automatically enabled by isAlwaysSyncable="true" in res/xml/sync_contacts.xml
settings.setSyncInterval(CalendarContract.AUTHORITY, Constants.DEFAULT_SYNC_INTERVAL.toLong()) settings.setSyncInterval(CalendarContract.AUTHORITY, Constants.DEFAULT_SYNC_INTERVAL.toLong())
// enable task sync if OpenTasks is installed OPENTASK_PROVIDERS.forEach {
// further changes will be handled by PackageChangedReceiver // enable task sync if OpenTasks is installed
if (LocalTaskList.tasksProviderAvailable(context!!)) { // further changes will be handled by PackageChangedReceiver
ContentResolver.setIsSyncable(account, TaskProvider.ProviderName.OpenTasks.authority, 1) if (LocalTaskList.tasksProviderAvailable(context!!, it)) {
settings.setSyncInterval(TaskProvider.ProviderName.OpenTasks.authority, Constants.DEFAULT_SYNC_INTERVAL.toLong()) ContentResolver.setIsSyncable(account, it.authority, 1)
settings.setSyncInterval(it.authority, Constants.DEFAULT_SYNC_INTERVAL.toLong())
}
} }
} else { } else {
ContentResolver.setIsSyncable(account, CalendarContract.AUTHORITY, 0) ContentResolver.setIsSyncable(account, CalendarContract.AUTHORITY, 0)

View File

@ -77,6 +77,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:layout_marginBottom="16dp"
tools:ignore="UselessParent"> tools:ignore="UselessParent">
<TextView <TextView
@ -98,6 +99,31 @@
</LinearLayout> </LinearLayout>
<LinearLayout
android:id="@+id/tasksorg_permissions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:ignore="UselessParent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/TextView.Heading"
android:text="@string/permissions_tasks_org"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/permissions_tasks_org_details"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/permissions_tasks_org_request"
android:onClick="requestTasksOrgPermissions"/>
</LinearLayout>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>

View File

@ -196,6 +196,9 @@
<string name="permissions_opentasks">OpenTasks permissions</string> <string name="permissions_opentasks">OpenTasks permissions</string>
<string name="permissions_opentasks_details">To synchronize tasks with your local task lists, EteSync needs to access OpenTasks.</string> <string name="permissions_opentasks_details">To synchronize tasks with your local task lists, EteSync needs to access OpenTasks.</string>
<string name="permissions_opentasks_request">Request OpenTasks permissions</string> <string name="permissions_opentasks_request">Request OpenTasks permissions</string>
<string name="permissions_tasks_org">Tasks.org permissions</string>
<string name="permissions_tasks_org_details">To synchronize tasks with your local task lists, EteSync needs to access Tasks.org.</string>
<string name="permissions_tasks_org_request">Request Tasks.org permissions</string>
<!-- AddAccountActivity --> <!-- AddAccountActivity -->
<string name="login_title">Add account</string> <string name="login_title">Add account</string>

View 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.tasks.opentasks"
android:supportsUploading="true" />

@ -1 +1 @@
Subproject commit c0459f905571ab2e08e8af9ac40a96200d8cb862 Subproject commit 0a070c7866792756d90be429522127ef9108eff8