mirror of
https://github.com/etesync/android
synced 2024-12-23 07:08:16 +00:00
commit
dc3db43d70
@ -110,6 +110,19 @@
|
||||
android:resource="@xml/sync_tasks"/>
|
||||
</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 -->
|
||||
<service
|
||||
|
@ -24,6 +24,7 @@ import android.provider.ContactsContract
|
||||
import androidx.core.content.ContextCompat
|
||||
import at.bitfire.ical4android.AndroidCalendar
|
||||
import at.bitfire.ical4android.CalendarStorageException
|
||||
import at.bitfire.ical4android.TaskProvider.Companion.OPENTASK_PROVIDERS
|
||||
import at.bitfire.vcard4android.ContactsStorageException
|
||||
import com.etesync.syncadapter.log.Logger
|
||||
import com.etesync.syncadapter.model.*
|
||||
@ -33,6 +34,7 @@ import com.etesync.syncadapter.ui.AccountsActivity
|
||||
import com.etesync.syncadapter.utils.HintManager
|
||||
import com.etesync.syncadapter.utils.LanguageUtils
|
||||
import com.etesync.syncadapter.utils.NotificationUtils
|
||||
import com.etesync.syncadapter.utils.TaskProviderHandling
|
||||
import io.requery.Persistable
|
||||
import io.requery.android.sqlite.DatabaseSource
|
||||
import io.requery.meta.EntityModel
|
||||
@ -85,8 +87,10 @@ class App : Application() {
|
||||
tasksFilter.addDataScheme("package")
|
||||
registerReceiver(PackageChangedReceiver(), tasksFilter)
|
||||
|
||||
// check whether a tasks app is currently installed
|
||||
PackageChangedReceiver.updateTaskSync(this@App)
|
||||
OPENTASK_PROVIDERS.forEach {
|
||||
// check whether a tasks app is currently installed
|
||||
TaskProviderHandling.updateTaskSync(this@App, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -274,6 +278,7 @@ class App : Application() {
|
||||
val OVERRIDE_PROXY = "overrideProxy"
|
||||
val OVERRIDE_PROXY_HOST = "overrideProxyHost"
|
||||
val OVERRIDE_PROXY_PORT = "overrideProxyPort"
|
||||
val PREFER_TASKSORG = "preferTasksOrg"
|
||||
val FORCE_LANGUAGE = "forceLanguage"
|
||||
val CHANGE_NOTIFICATION = "show_change_notification"
|
||||
|
||||
|
@ -8,47 +8,21 @@
|
||||
|
||||
package com.etesync.syncadapter
|
||||
|
||||
import android.accounts.AccountManager
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.provider.CalendarContract
|
||||
import at.bitfire.ical4android.TaskProvider
|
||||
import com.etesync.syncadapter.log.Logger
|
||||
import com.etesync.syncadapter.resource.LocalTaskList
|
||||
import com.etesync.syncadapter.utils.TaskProviderHandling.Companion.updateTaskSync
|
||||
|
||||
class PackageChangedReceiver : BroadcastReceiver() {
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
if (Intent.ACTION_PACKAGE_ADDED == intent.action || Intent.ACTION_PACKAGE_FULLY_REMOVED == intent.action)
|
||||
updateTaskSync(context)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
internal fun updateTaskSync(context: Context) {
|
||||
val tasksInstalled = LocalTaskList.tasksProviderAvailable(context)
|
||||
Logger.log.info("Package (un)installed; OpenTasks provider now available = $tasksInstalled")
|
||||
|
||||
for (account in AccountManager.get(context).getAccountsByType(App.accountType)) {
|
||||
val settings = AccountSettings(context, account)
|
||||
val calendarSyncInterval = settings.getSyncInterval(CalendarContract.AUTHORITY)
|
||||
|
||||
if (tasksInstalled) {
|
||||
if (calendarSyncInterval == null) {
|
||||
// do nothing atm
|
||||
} else if (ContentResolver.getIsSyncable(account, TaskProvider.ProviderName.OpenTasks.authority) <= 0) {
|
||||
ContentResolver.setIsSyncable(account, TaskProvider.ProviderName.OpenTasks.authority, 1)
|
||||
settings.setSyncInterval(TaskProvider.ProviderName.OpenTasks.authority, calendarSyncInterval)
|
||||
}
|
||||
} else {
|
||||
ContentResolver.setIsSyncable(account, TaskProvider.ProviderName.OpenTasks.authority, 0)
|
||||
}
|
||||
if (Intent.ACTION_PACKAGE_ADDED == intent.action || Intent.ACTION_PACKAGE_FULLY_REMOVED == intent.action) {
|
||||
TaskProvider.OPENTASK_PROVIDERS.forEach {
|
||||
updateTaskSync(context, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ import at.bitfire.ical4android.AndroidTaskList
|
||||
import at.bitfire.ical4android.AndroidTaskListFactory
|
||||
import at.bitfire.ical4android.CalendarStorageException
|
||||
import at.bitfire.ical4android.TaskProvider
|
||||
import at.bitfire.ical4android.TaskProvider.ProviderName
|
||||
import com.etesync.syncadapter.model.JournalEntity
|
||||
import org.dmfs.tasks.contract.TaskContract.TaskLists
|
||||
import org.dmfs.tasks.contract.TaskContract.Tasks
|
||||
@ -30,12 +31,12 @@ class LocalTaskList private constructor(
|
||||
companion object {
|
||||
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)
|
||||
return context.packageManager.resolveContentProvider(TaskProvider.ProviderName.OpenTasks.authority, 0) != null
|
||||
return context.packageManager.resolveContentProvider(provider.authority, 0) != null
|
||||
else {
|
||||
try {
|
||||
TaskProvider.acquire(context, TaskProvider.ProviderName.OpenTasks)?.use {
|
||||
TaskProvider.acquire(context, provider)?.use {
|
||||
return true
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
@ -2,16 +2,21 @@ package com.etesync.syncadapter.syncadapter
|
||||
|
||||
import android.accounts.Account
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.provider.CalendarContract
|
||||
import at.bitfire.ical4android.TaskProvider
|
||||
import com.etesync.syncadapter.App
|
||||
import com.etesync.syncadapter.utils.TaskProviderHandling
|
||||
|
||||
|
||||
fun requestSync(account: Account?) {
|
||||
val authorities = arrayOf(App.addressBooksAuthority, CalendarContract.AUTHORITY, TaskProvider.ProviderName.OpenTasks.authority)
|
||||
fun requestSync(context: Context, account: Account?) {
|
||||
val authorities = arrayOf(
|
||||
App.addressBooksAuthority,
|
||||
CalendarContract.AUTHORITY,
|
||||
TaskProviderHandling.getWantedTaskSyncProvider(context)?.authority
|
||||
)
|
||||
|
||||
for (authority in authorities) {
|
||||
for (authority in authorities.filterNotNull()) {
|
||||
val extras = Bundle()
|
||||
extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true) // manual sync
|
||||
extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true) // run immediately (don't queue)
|
||||
|
@ -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)
|
||||
|
||||
}
|
@ -17,6 +17,7 @@ import android.os.Build
|
||||
import android.os.Bundle
|
||||
import at.bitfire.ical4android.AndroidTaskList
|
||||
import at.bitfire.ical4android.TaskProvider
|
||||
import at.bitfire.ical4android.TaskProvider.ProviderName
|
||||
import com.etesync.syncadapter.AccountSettings
|
||||
import com.etesync.syncadapter.App
|
||||
import com.etesync.syncadapter.Constants
|
||||
@ -35,18 +36,18 @@ import java.util.*
|
||||
*/
|
||||
class TasksSyncAdapterService: SyncAdapterService() {
|
||||
|
||||
override fun syncAdapter() = TasksSyncAdapter(this)
|
||||
|
||||
override fun syncAdapter() = TasksSyncAdapter(this, ProviderName.OpenTasks)
|
||||
|
||||
class TasksSyncAdapter(
|
||||
context: Context
|
||||
context: Context,
|
||||
private val name: ProviderName
|
||||
): SyncAdapter(context) {
|
||||
override val syncErrorTitle = R.string.sync_error_tasks
|
||||
override val notificationManager = SyncNotification(context, "journals-tasks", Constants.NOTIFICATION_TASK_SYNC)
|
||||
|
||||
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
|
||||
if (Build.VERSION.SDK_INT >= 26)
|
||||
|
@ -26,6 +26,7 @@ import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.content.ContextCompat
|
||||
import at.bitfire.ical4android.TaskProvider
|
||||
import at.bitfire.ical4android.TaskProvider.Companion.OPENTASK_PROVIDERS
|
||||
import at.bitfire.vcard4android.ContactsStorageException
|
||||
import com.etesync.syncadapter.*
|
||||
import com.etesync.journalmanager.Crypto
|
||||
@ -285,7 +286,7 @@ class AccountActivity : BaseActivity(), Toolbar.OnMenuItemClickListener, PopupMe
|
||||
listTaskDAV!!.adapter = adapter
|
||||
listTaskDAV!!.onItemClickListener = onItemClickListener
|
||||
|
||||
if (!packageInstalled(this, openTasksPackage)) {
|
||||
if (!packageInstalled(this, tasksOrgPackage) && !packageInstalled(this, openTasksPackage)) {
|
||||
val opentasksWarning = findViewById<View>(R.id.taskdav_opentasks_warning)
|
||||
opentasksWarning.visibility = View.VISIBLE
|
||||
}
|
||||
@ -378,7 +379,9 @@ class AccountActivity : BaseActivity(), Toolbar.OnMenuItemClickListener, PopupMe
|
||||
info.taskdav = AccountInfo.ServiceInfo()
|
||||
info.taskdav!!.id = 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)
|
||||
}
|
||||
}
|
||||
@ -470,7 +473,7 @@ class AccountActivity : BaseActivity(), Toolbar.OnMenuItemClickListener, PopupMe
|
||||
|
||||
|
||||
private fun requestSync() {
|
||||
requestSync(account)
|
||||
requestSync(applicationContext, account)
|
||||
Snackbar.make(findViewById(R.id.parent), R.string.account_synchronizing_now, Snackbar.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@ import androidx.loader.content.AsyncTaskLoader
|
||||
import androidx.loader.content.Loader
|
||||
import androidx.preference.*
|
||||
import at.bitfire.ical4android.TaskProvider
|
||||
import at.bitfire.ical4android.TaskProvider.Companion.OPENTASK_PROVIDERS
|
||||
import com.etesync.syncadapter.*
|
||||
import com.etesync.syncadapter.Constants.KEY_ACCOUNT
|
||||
import com.etesync.syncadapter.R
|
||||
@ -121,7 +122,9 @@ class AccountSettingsActivity : BaseActivity() {
|
||||
val newInterval = java.lang.Long.parseLong(newValue as String)
|
||||
settings.setSyncInterval(App.addressBooksAuthority, 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)
|
||||
false
|
||||
}
|
||||
|
@ -43,6 +43,8 @@ class AppSettingsActivity : BaseActivity() {
|
||||
internal lateinit var dbHelper: ServiceDB.OpenHelper
|
||||
internal lateinit var settings: Settings
|
||||
|
||||
internal lateinit var prefPreferTasksOrg: SwitchPreferenceCompat
|
||||
|
||||
internal lateinit var prefResetHints: Preference
|
||||
internal lateinit var prefOverrideProxy: SwitchPreferenceCompat
|
||||
internal lateinit var prefDistrustSystemCerts: SwitchPreferenceCompat
|
||||
@ -86,6 +88,14 @@ class AppSettingsActivity : BaseActivity() {
|
||||
true
|
||||
}
|
||||
|
||||
prefPreferTasksOrg = findPreference("prefer_tasksorg") as SwitchPreferenceCompat
|
||||
prefPreferTasksOrg.isChecked = context!!.defaultSharedPreferences.getBoolean(App.PREFER_TASKSORG, false)
|
||||
prefPreferTasksOrg.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||
context!!.defaultSharedPreferences.edit().putBoolean(App.PREFER_TASKSORG, newValue as Boolean).apply()
|
||||
Snackbar.make(view!!, getString(R.string.app_settings_prefer_tasksorg_snack), Snackbar.LENGTH_LONG).show()
|
||||
true
|
||||
}
|
||||
|
||||
prefProxyHost = findPreference("proxy_host") as EditTextPreference
|
||||
val proxyHost = settings.getString(App.OVERRIDE_PROXY_HOST, App.OVERRIDE_PROXY_HOST_DEFAULT)
|
||||
prefProxyHost.text = proxyHost
|
||||
|
@ -134,7 +134,7 @@ open class ChangeEncryptionPasswordActivity : BaseActivity() {
|
||||
this@ChangeEncryptionPasswordActivity.finish()
|
||||
}.show()
|
||||
|
||||
requestSync(account)
|
||||
requestSync(applicationContext, account)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
uiThread {
|
||||
|
@ -19,7 +19,6 @@ import androidx.fragment.app.DialogFragment
|
||||
import androidx.loader.app.LoaderManager
|
||||
import androidx.loader.content.AsyncTaskLoader
|
||||
import androidx.loader.content.Loader
|
||||
import at.bitfire.ical4android.TaskProvider
|
||||
import com.etesync.syncadapter.*
|
||||
import com.etesync.journalmanager.Crypto
|
||||
import com.etesync.journalmanager.Exceptions
|
||||
@ -27,6 +26,7 @@ import com.etesync.journalmanager.JournalManager
|
||||
import com.etesync.syncadapter.model.CollectionInfo
|
||||
import com.etesync.syncadapter.model.JournalEntity
|
||||
import com.etesync.syncadapter.model.JournalModel
|
||||
import com.etesync.syncadapter.utils.TaskProviderHandling
|
||||
import okhttp3.HttpUrl
|
||||
|
||||
class CreateCollectionFragment : DialogFragment(), LoaderManager.LoaderCallbacks<Exception> {
|
||||
@ -84,15 +84,16 @@ class CreateCollectionFragment : DialogFragment(), LoaderManager.LoaderCallbacks
|
||||
|
||||
override fun loadInBackground(): Exception? {
|
||||
try {
|
||||
var authority: String = ""
|
||||
|
||||
val data = (context.applicationContext as App).data
|
||||
val context = context.applicationContext
|
||||
val data = (context as App).data
|
||||
|
||||
// 1. find service ID
|
||||
when (info.enumType){
|
||||
CollectionInfo.Type.ADDRESS_BOOK -> authority = App.addressBooksAuthority
|
||||
CollectionInfo.Type.CALENDAR -> authority = CalendarContract.AUTHORITY
|
||||
CollectionInfo.Type.TASKS -> authority = TaskProvider.ProviderName.OpenTasks.authority
|
||||
val authority = when (info.enumType){
|
||||
CollectionInfo.Type.ADDRESS_BOOK -> App.addressBooksAuthority
|
||||
CollectionInfo.Type.CALENDAR -> CalendarContract.AUTHORITY
|
||||
CollectionInfo.Type.TASKS ->
|
||||
TaskProviderHandling.getWantedTaskSyncProvider(context)?.authority
|
||||
else -> null
|
||||
}
|
||||
|
||||
val serviceEntity = JournalModel.Service.fetchOrCreate(data, account.name, info.enumType)
|
||||
@ -127,7 +128,7 @@ class CreateCollectionFragment : DialogFragment(), LoaderManager.LoaderCallbacks
|
||||
journalManager.update(journal)
|
||||
}
|
||||
|
||||
requestSync(authority)
|
||||
authority?.let { requestSync(it) }
|
||||
} catch (e: IllegalStateException) {
|
||||
return e
|
||||
} catch (e: Exceptions.HttpException) {
|
||||
|
@ -25,6 +25,7 @@ import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import at.bitfire.ical4android.TaskProvider.ProviderName
|
||||
import at.bitfire.vcard4android.ContactsStorageException
|
||||
import com.etesync.syncadapter.*
|
||||
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("\n")
|
||||
// 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: ")
|
||||
.append(if (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED) "granted" else "denied")
|
||||
.append("\n")
|
||||
@ -169,7 +170,7 @@ class DebugInfoActivity : BaseActivity(), LoaderManager.LoaderCallbacks<String>
|
||||
for (acct in accountManager.getAccountsByType(context.getString(R.string.account_type)))
|
||||
try {
|
||||
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)
|
||||
report.append(", SSID: ").append(settings.syncWifiOnlySSID)
|
||||
report.append("\n [CardDAV] Contact group method: ").append(settings.groupMethod)
|
||||
|
@ -29,6 +29,7 @@ import com.etesync.journalmanager.model.SyncEntry
|
||||
import com.etesync.syncadapter.resource.*
|
||||
import com.etesync.syncadapter.ui.journalviewer.ListEntriesFragment.Companion.setJournalEntryView
|
||||
import com.etesync.syncadapter.utils.EventEmailInvitation
|
||||
import com.etesync.syncadapter.utils.TaskProviderHandling
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import ezvcard.util.PartialDate
|
||||
import org.jetbrains.anko.doAsync
|
||||
@ -116,15 +117,17 @@ class JournalItemActivity : BaseActivity(), Refreshable {
|
||||
}
|
||||
}
|
||||
CollectionInfo.Type.TASKS -> {
|
||||
val provider = TaskProvider.acquire(this, TaskProvider.ProviderName.OpenTasks)!!
|
||||
val localTaskList = LocalTaskList.findByName(account, provider, LocalTaskList.Factory, info.uid!!)!!
|
||||
val task = Task.tasksFromReader(StringReader(syncEntry.content))[0]
|
||||
var localTask = localTaskList.findByUid(task.uid!!)
|
||||
if (localTask != null) {
|
||||
localTask.updateAsDirty(task)
|
||||
} else {
|
||||
localTask = LocalTask(localTaskList, task, task.uid, null)
|
||||
localTask.addAsDirty()
|
||||
TaskProviderHandling.getWantedTaskSyncProvider(applicationContext)?.let {
|
||||
val provider = TaskProvider.acquire(this, it)!!
|
||||
val localTaskList = LocalTaskList.findByName(account, provider, LocalTaskList.Factory, info.uid!!)!!
|
||||
val task = Task.tasksFromReader(StringReader(syncEntry.content))[0]
|
||||
var localTask = localTaskList.findByUid(task.uid!!)
|
||||
if (localTask != null) {
|
||||
localTask.updateAsDirty(task)
|
||||
} else {
|
||||
localTask = LocalTask(localTaskList, task, task.uid, null)
|
||||
localTask.addAsDirty()
|
||||
}
|
||||
}
|
||||
}
|
||||
CollectionInfo.Type.ADDRESS_BOOK -> {
|
||||
|
@ -13,8 +13,11 @@ import android.app.Activity
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.core.app.ActivityCompat
|
||||
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.R
|
||||
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
|
||||
findViewById<View>(R.id.contacts_permissions).visibility = if (noContactsPermissions) View.VISIBLE else View.GONE
|
||||
|
||||
val noTaskPermissions: Boolean
|
||||
if (LocalTaskList.tasksProviderAvailable(this)) {
|
||||
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
|
||||
}
|
||||
val needOpenTaskPermissions = setupPermissions(ProviderName.OpenTasks, R.id.opentasks_permissions)
|
||||
val needTasksOrgPermissions = setupPermissions(ProviderName.TasksOrg, R.id.tasksorg_permissions)
|
||||
|
||||
if (!noCalendarPermissions && !noContactsPermissions && !noTaskPermissions) {
|
||||
if (!noCalendarPermissions && !noContactsPermissions && !(needOpenTaskPermissions || needTasksOrgPermissions)) {
|
||||
val nm = NotificationManagerCompat.from(this)
|
||||
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) {
|
||||
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_CALENDAR, Manifest.permission.WRITE_CALENDAR), 0)
|
||||
}
|
||||
@ -64,7 +70,11 @@ class PermissionsActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -75,11 +85,8 @@ class PermissionsActivity : BaseActivity() {
|
||||
companion object {
|
||||
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) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ import com.etesync.syncadapter.ui.importlocal.ImportActivity
|
||||
import com.etesync.syncadapter.ui.journalviewer.ListEntriesFragment
|
||||
import com.etesync.syncadapter.utils.HintManager
|
||||
import com.etesync.syncadapter.utils.ShowcaseBuilder
|
||||
import com.etesync.syncadapter.utils.TaskProviderHandling
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import tourguide.tourguide.ToolTip
|
||||
import java.io.FileNotFoundException
|
||||
@ -212,7 +213,9 @@ class ViewCollectionActivity : BaseActivity(), Refreshable {
|
||||
}
|
||||
CollectionInfo.Type.TASKS -> {
|
||||
try {
|
||||
val providerClient = TaskProvider.acquire(this@ViewCollectionActivity, TaskProvider.ProviderName.OpenTasks)
|
||||
val providerClient = TaskProviderHandling.getWantedTaskSyncProvider(this@ViewCollectionActivity)?.let {
|
||||
TaskProvider.acquire(this@ViewCollectionActivity, it)
|
||||
}
|
||||
if (providerClient == null) {
|
||||
return null
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import android.provider.CalendarContract
|
||||
import android.provider.ContactsContract
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import at.bitfire.ical4android.*
|
||||
import at.bitfire.ical4android.TaskProvider.Companion.OPENTASK_PROVIDERS
|
||||
import at.bitfire.vcard4android.BatchOperation
|
||||
import at.bitfire.vcard4android.Contact
|
||||
import at.bitfire.vcard4android.ContactsStorageException
|
||||
@ -27,6 +28,7 @@ import com.etesync.syncadapter.resource.*
|
||||
import com.etesync.syncadapter.syncadapter.ContactsSyncManager
|
||||
import com.etesync.syncadapter.ui.Refreshable
|
||||
import com.etesync.syncadapter.ui.importlocal.ResultFragment.ImportResult
|
||||
import com.etesync.syncadapter.utils.TaskProviderHandling
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
@ -282,40 +284,46 @@ class ImportFragment : DialogFragment() {
|
||||
|
||||
finishParsingFile(tasks.size)
|
||||
|
||||
val provider = TaskProvider.acquire(context, TaskProvider.ProviderName.OpenTasks)
|
||||
if (provider == null) {
|
||||
result.e = Exception("Failed to acquire tasks content provider.")
|
||||
return result
|
||||
}
|
||||
|
||||
val localTaskList: LocalTaskList?
|
||||
try {
|
||||
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 {
|
||||
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++
|
||||
val provider = TaskProviderHandling.getWantedTaskSyncProvider(requireContext())
|
||||
.let {
|
||||
if (it == null) {
|
||||
result.e = Exception("Failed to acquire tasks content provider.")
|
||||
null
|
||||
} else {
|
||||
TaskProvider.acquire(context, it)
|
||||
}
|
||||
}
|
||||
} catch (e: CalendarStorageException) {
|
||||
e.printStackTrace()
|
||||
|
||||
provider?.let {
|
||||
val localTaskList: LocalTaskList?
|
||||
try {
|
||||
localTaskList = LocalTaskList.findByName(account, it, 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
|
||||
}
|
||||
|
||||
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) {
|
||||
val uidToLocalId = HashMap<String?, Long>()
|
||||
|
@ -21,6 +21,7 @@ import android.provider.CalendarContract
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import at.bitfire.ical4android.TaskProvider
|
||||
import at.bitfire.ical4android.TaskProvider.Companion.OPENTASK_PROVIDERS
|
||||
import com.etesync.syncadapter.*
|
||||
import com.etesync.journalmanager.Crypto
|
||||
import com.etesync.journalmanager.Exceptions
|
||||
@ -32,6 +33,7 @@ import com.etesync.syncadapter.model.ServiceEntity
|
||||
import com.etesync.syncadapter.resource.LocalTaskList
|
||||
import com.etesync.syncadapter.ui.setup.BaseConfigurationFinder.Configuration
|
||||
import com.etesync.syncadapter.utils.AndroidCompat
|
||||
import com.etesync.syncadapter.utils.TaskProviderHandling
|
||||
import okhttp3.HttpUrl
|
||||
import java.util.logging.Level
|
||||
|
||||
@ -153,11 +155,10 @@ class SetupEncryptionFragment : DialogFragment() {
|
||||
// calendar sync is automatically enabled by isAlwaysSyncable="true" in res/xml/sync_contacts.xml
|
||||
settings.setSyncInterval(CalendarContract.AUTHORITY, Constants.DEFAULT_SYNC_INTERVAL.toLong())
|
||||
|
||||
// enable task sync if OpenTasks is installed
|
||||
// further changes will be handled by PackageChangedReceiver
|
||||
if (LocalTaskList.tasksProviderAvailable(context!!)) {
|
||||
ContentResolver.setIsSyncable(account, TaskProvider.ProviderName.OpenTasks.authority, 1)
|
||||
settings.setSyncInterval(TaskProvider.ProviderName.OpenTasks.authority, Constants.DEFAULT_SYNC_INTERVAL.toLong())
|
||||
OPENTASK_PROVIDERS.forEach {
|
||||
// enable task sync if OpenTasks is installed
|
||||
// further changes will be handled by PackageChangedReceiver
|
||||
TaskProviderHandling.updateTaskSync(context!!, it)
|
||||
}
|
||||
} else {
|
||||
ContentResolver.setIsSyncable(account, CalendarContract.AUTHORITY, 0)
|
||||
|
@ -0,0 +1,56 @@
|
||||
package com.etesync.syncadapter.utils
|
||||
|
||||
import android.accounts.AccountManager
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.provider.CalendarContract
|
||||
import at.bitfire.ical4android.TaskProvider
|
||||
import com.etesync.syncadapter.AccountSettings
|
||||
import com.etesync.syncadapter.App
|
||||
import com.etesync.syncadapter.log.Logger
|
||||
import com.etesync.syncadapter.resource.LocalTaskList
|
||||
import org.jetbrains.anko.defaultSharedPreferences
|
||||
|
||||
class TaskProviderHandling {
|
||||
companion object {
|
||||
fun getWantedTaskSyncProvider(context: Context): TaskProvider.ProviderName? {
|
||||
val openTasksAvailable = LocalTaskList.tasksProviderAvailable(context, TaskProvider.ProviderName.OpenTasks)
|
||||
val tasksOrgAvailable = LocalTaskList.tasksProviderAvailable(context, TaskProvider.ProviderName.TasksOrg)
|
||||
|
||||
if (openTasksAvailable && tasksOrgAvailable) {
|
||||
if (context.defaultSharedPreferences.getBoolean(App.PREFER_TASKSORG, false))
|
||||
return TaskProvider.ProviderName.TasksOrg
|
||||
else
|
||||
return TaskProvider.ProviderName.OpenTasks
|
||||
} else {
|
||||
if (openTasksAvailable)
|
||||
return TaskProvider.ProviderName.OpenTasks
|
||||
else if (tasksOrgAvailable)
|
||||
return TaskProvider.ProviderName.TasksOrg
|
||||
else
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
fun updateTaskSync(context: Context, provider: TaskProvider.ProviderName) {
|
||||
for (account in AccountManager.get(context).getAccountsByType(App.accountType)) {
|
||||
val settings = AccountSettings(context, account)
|
||||
val calendarSyncInterval = settings.getSyncInterval(CalendarContract.AUTHORITY)
|
||||
val wantedProvider = getWantedTaskSyncProvider(context)
|
||||
val shouldSync = wantedProvider == provider
|
||||
|
||||
Logger.log.info("Package (un)installed; Syncing (${shouldSync}) for ${provider.name}")
|
||||
if (shouldSync) {
|
||||
if (calendarSyncInterval == null) {
|
||||
// do nothing atm
|
||||
} else if (ContentResolver.getIsSyncable(account, provider.authority) <= 0) {
|
||||
ContentResolver.setIsSyncable(account, provider.authority, 1)
|
||||
settings.setSyncInterval(provider.authority, calendarSyncInterval)
|
||||
}
|
||||
} else {
|
||||
ContentResolver.setIsSyncable(account, provider.authority, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -77,6 +77,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="16dp"
|
||||
tools:ignore="UselessParent">
|
||||
|
||||
<TextView
|
||||
@ -98,6 +99,31 @@
|
||||
|
||||
</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>
|
||||
|
||||
</ScrollView>
|
@ -8,6 +8,9 @@
|
||||
-->
|
||||
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@+id/create_tasklist"
|
||||
android:title="@string/create_tasklist"/>
|
||||
|
||||
<item android:id="@+id/install_tasksorg"
|
||||
android:visible="false"
|
||||
android:title="@string/install_tasksorg"/>
|
||||
@ -15,8 +18,4 @@
|
||||
<item android:id="@+id/install_opentasks"
|
||||
android:visible="false"
|
||||
android:title="@string/install_opentasks"/>
|
||||
|
||||
<item android:id="@+id/create_tasklist"
|
||||
android:title="@string/create_tasklist"/>
|
||||
|
||||
</menu>
|
@ -93,6 +93,11 @@
|
||||
<string name="app_settings_reset_hints">Reset hints</string>
|
||||
<string name="app_settings_reset_hints_summary">Re-enables hints which have been dismissed previously</string>
|
||||
<string name="app_settings_reset_hints_success">All hints will be shown again</string>
|
||||
<string name="app_settings_sync">Sync</string>
|
||||
<string name="app_settings_prefer_tasksorg">Prefer Tasks.org tasks provider</string>
|
||||
<string name="app_settings_prefer_tasksorg_on">Will sync tasks with Tasks.org if available</string>
|
||||
<string name="app_settings_prefer_tasksorg_off">Will sync tasks with OpenTasks if available</string>
|
||||
<string name="app_settings_prefer_tasksorg_snack">You may need to remove and re-add your account for these changes to take effect.</string>
|
||||
<string name="app_settings_connection">Connection</string>
|
||||
<string name="app_settings_override_proxy">Override proxy settings</string>
|
||||
<string name="app_settings_override_proxy_on">Use custom proxy settings</string>
|
||||
@ -130,13 +135,13 @@
|
||||
<string name="account_delete_collection_last_text">Deleting the last collection is not allowed, please create a new one if you\'d like to delete this one.</string>
|
||||
<string name="account_showcase_view_collection">You can click on an item to view the collection. From there you can view the journal, import, and much more...</string>
|
||||
<string name="account_click_install_tasks">Click here to install OpenTasks!</string>
|
||||
<string name="account_tasks_not_showing">* Only OpenTasks lists are shown below</string>
|
||||
<string name="account_tasks_not_showing">* Not syncing tasks (install a provider)</string>
|
||||
|
||||
<string name="show_fingperprint_title">My Fingerprint</string>
|
||||
|
||||
<!-- ViewCollection -->
|
||||
<string name="change_journal_title">Change Journal</string>
|
||||
<string name="tasks_not_showing">* Log is only shown if OpenTasks is installed. We are working on a fix.</string>
|
||||
<string name="tasks_not_showing">* Log is only shown if a task provider is installed.</string>
|
||||
<string name="account_showcase_import">In order to import contacts and calendars into EteSync, you need to click on the menu, and choose \"Import\".</string>
|
||||
<string name="account_owner">Owner: %s</string>
|
||||
<string name="members_owner_only">Only the owner of this collection (%s) is allowed to view its members.</string>
|
||||
@ -196,6 +201,9 @@
|
||||
<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_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 -->
|
||||
<string name="login_title">Add account</string>
|
||||
|
@ -26,6 +26,16 @@
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/app_settings_sync">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="prefer_tasksorg"
|
||||
android:title="@string/app_settings_prefer_tasksorg"
|
||||
android:summaryOn="@string/app_settings_prefer_tasksorg_on"
|
||||
android:summaryOff="@string/app_settings_prefer_tasksorg_off"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/app_settings_connection">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
|
12
app/src/main/res/xml/sync_tasks_org.xml
Normal file
12
app/src/main/res/xml/sync_tasks_org.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.tasks.opentasks"
|
||||
android:supportsUploading="true" />
|
@ -1 +1 @@
|
||||
Subproject commit c0459f905571ab2e08e8af9ac40a96200d8cb862
|
||||
Subproject commit 0a070c7866792756d90be429522127ef9108eff8
|
Loading…
Reference in New Issue
Block a user