Add opentask-provider accounts automatically

pull/1074/head
Alex Baker 4 years ago
parent 9eaa7a7408
commit 4a21421237

@ -13,6 +13,7 @@ import com.todoroo.astrid.service.Upgrader.Companion.getAndroidColor
import org.tasks.LocalBroadcastManager
import org.tasks.R
import org.tasks.data.*
import org.tasks.data.CaldavAccount.Companion.TYPE_LOCAL
import org.tasks.data.Place.Companion.newPlace
import org.tasks.preferences.Preferences
import timber.log.Timber
@ -65,10 +66,11 @@ class TasksJsonImporter @Inject constructor(
val version = input["version"].asInt
val backupContainer = gson.fromJson(data, BackupContainer::class.java)
backupContainer.tags?.forEach { tagData ->
tagData.setColor(themeToColor(context, version, tagData.getColor()!!))
if (tagDataDao.getByUuid(tagData.remoteId!!) == null) {
tagDataDao.createNew(tagData)
findTagData(tagData)?.let {
return@forEach
}
tagData.setColor(themeToColor(context, version, tagData.getColor()!!))
tagDataDao.createNew(tagData)
}
backupContainer.googleTaskAccounts?.forEach { googleTaskAccount ->
if (googleTaskListDao.getAccount(googleTaskAccount.account!!) == null) {
@ -98,10 +100,17 @@ class TasksJsonImporter @Inject constructor(
}
}
backupContainer.caldavCalendars?.forEach { calendar ->
calendar.color = themeToColor(context, version, calendar.color)
if (caldavDao.getCalendarByUuid(calendar.uuid!!) == null) {
caldavDao.insert(calendar)
val account = caldavDao.getAccountByUuid(calendar.account!!)!!
when (account.accountType) {
TYPE_LOCAL -> if (caldavDao.getCalendarByUuid(calendar.uuid!!) != null) {
return@forEach
}
else -> if (caldavDao.getCalendarByUrl(calendar.account!!, calendar.url!!) != null) {
return@forEach
}
}
calendar.color = themeToColor(context, version, calendar.color)
caldavDao.insert(calendar)
}
backupContainer.taskListMetadata?.forEach { tlm ->
val id = tlm.filter.takeIf { it?.isNotBlank() == true } ?: tlm.tagUuid!!
@ -116,10 +125,19 @@ class TasksJsonImporter @Inject constructor(
progressDialog,
context.getString(R.string.import_progress_read, result.taskCount))
val task = backup.task
if (taskDao.fetch(task.uuid) != null) {
result.skipCount++
return@forEach
}
taskDao.fetch(task.uuid)
?.let {
result.skipCount++
return@forEach
}
backup.caldavTasks
?.filter { it.deleted == 0L }
?.any { caldavDao.getCalendar(it.calendar!!) == null }
?.takeIf { it }
?.let {
result.skipCount++
return@forEach
}
task.suppressRefresh()
task.suppressSync()
taskDao.createNew(task)
@ -158,7 +176,9 @@ class TasksJsonImporter @Inject constructor(
locationDao.insert(geofence)
}
for (tag in backup.tags) {
val tagData = findTagData(tag) ?: continue
tag.task = taskId
tag.tagUid = tagData.remoteId
tag.setTaskUid(taskUuid)
tagDao.insert(tag)
}
@ -214,6 +234,14 @@ class TasksJsonImporter @Inject constructor(
return result
}
private suspend fun findTagData(tagData: TagData) =
findTagData(tagData.remoteId!!, tagData.name!!)
private suspend fun findTagData(tag: Tag) = findTagData(tag.tagUid!!, tag.name!!)
private suspend fun findTagData(uid: String, name: String): TagData? =
tagDataDao.getByUuid(uid) ?: tagDataDao.getTagByName(name)
private fun themeToColor(context: Context, version: Int, color: Int) =
if (version < Upgrader.V8_2) getAndroidColor(context, color) else color

@ -162,6 +162,9 @@ SELECT EXISTS(SELECT 1
@Query("SELECT * FROM caldav_lists ORDER BY cdl_name COLLATE NOCASE")
abstract suspend fun getCalendars(): List<CaldavCalendar>
@Query("SELECT EXISTS(SELECT 1 FROM caldav_lists WHERE cdl_url IN (:urls))")
abstract suspend fun anyExist(urls: List<String>): Boolean
@Query("SELECT * FROM caldav_lists WHERE cdl_uuid = :uuid LIMIT 1")
abstract suspend fun getCalendar(uuid: String): CaldavCalendar?

@ -19,14 +19,22 @@ import org.tasks.caldav.iCalendar.Companion.APPLE_SORT_ORDER
import timber.log.Timber
import javax.inject.Inject
class OpenTaskDao @Inject constructor(@ApplicationContext context: Context) {
class OpenTaskDao @Inject constructor(
@ApplicationContext context: Context,
private val caldavDao: CaldavDao
) {
private val cr = context.contentResolver
val authority = context.getString(R.string.opentasks_authority)
private val tasks = Tasks.getContentUri(authority)
private val properties = Properties.getContentUri(authority)
suspend fun accounts(): List<String> = getLists().map { it.account!! }.distinct()
suspend fun newAccounts(): List<String> =
getListsByAccount()
.newAccounts(caldavDao)
.map { it.key }
suspend fun getListsByAccount(): Map<String, List<CaldavCalendar>> =
getLists().groupBy { it.account!! }
suspend fun getLists(): List<CaldavCalendar> = withContext(Dispatchers.IO) {
val calendars = ArrayList<CaldavCalendar>()
@ -238,6 +246,9 @@ class OpenTaskDao @Inject constructor(@ApplicationContext context: Context) {
const val ACCOUNT_TYPE_DAVx5 = "bitfire.at.davdroid"
const val ACCOUNT_TYPE_ETESYNC = "com.etesync.syncadapter"
suspend fun Map<String, List<CaldavCalendar>>.newAccounts(caldavDao: CaldavDao) =
filterNot { (_, lists) -> caldavDao.anyExist(lists.map { it.url!! }) }
fun Cursor.getString(columnName: String): String? =
getString(getColumnIndex(columnName))

@ -12,6 +12,7 @@ import org.tasks.BuildConfig
import org.tasks.R
import org.tasks.data.CaldavDao
import org.tasks.data.GoogleTaskListDao
import org.tasks.data.OpenTaskDao
import org.tasks.db.Migrations
import org.tasks.jobs.WorkManager
import org.tasks.jobs.WorkManagerImpl
@ -41,7 +42,9 @@ internal class ProductionModule {
@ApplicationContext context: Context,
preferences: Preferences,
googleTaskListDao: GoogleTaskListDao,
caldavDao: CaldavDao): WorkManager {
return WorkManagerImpl(context, preferences, googleTaskListDao, caldavDao)
caldavDao: CaldavDao,
openTaskDao: OpenTaskDao
): WorkManager {
return WorkManagerImpl(context, preferences, googleTaskListDao, caldavDao, openTaskDao)
}
}

@ -9,6 +9,7 @@ import org.tasks.R
import org.tasks.analytics.Firebase
import org.tasks.data.CaldavAccount.Companion.TYPE_OPENTASKS
import org.tasks.data.CaldavDao
import org.tasks.data.OpenTaskDao
import org.tasks.opentasks.OpenTasksSynchronizer
import org.tasks.preferences.Preferences
@ -19,12 +20,14 @@ class SyncOpenTasksWork @WorkerInject constructor(
localBroadcastManager: LocalBroadcastManager,
preferences: Preferences,
private val openTasksSynchronizer: OpenTasksSynchronizer,
private val caldavDao: CaldavDao
private val caldavDao: CaldavDao,
private val openTaskDao: OpenTaskDao
) : SyncWork(context, workerParams, firebase, localBroadcastManager, preferences) {
override val syncStatus = R.string.p_sync_ongoing_opentasks
override suspend fun enabled() =
caldavDao.getAccounts(TYPE_OPENTASKS).isNotEmpty()
|| openTaskDao.newAccounts().isNotEmpty()
override suspend fun doSync() {
openTasksSynchronizer.sync()

@ -17,6 +17,7 @@ import org.tasks.data.CaldavAccount.Companion.TYPE_ETESYNC
import org.tasks.data.CaldavAccount.Companion.TYPE_OPENTASKS
import org.tasks.data.CaldavDao
import org.tasks.data.GoogleTaskListDao
import org.tasks.data.OpenTaskDao
import org.tasks.data.Place
import org.tasks.date.DateTimeUtils.midnight
import org.tasks.date.DateTimeUtils.newDateTime
@ -46,7 +47,8 @@ class WorkManagerImpl constructor(
private val context: Context,
private val preferences: Preferences,
private val googleTaskListDao: GoogleTaskListDao,
private val caldavDao: CaldavDao
private val caldavDao: CaldavDao,
private val openTaskDao: OpenTaskDao
): WorkManager {
private val throttle = Throttle(200, 60000, "WORK")
private val alarmManager: AlarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
@ -150,7 +152,8 @@ class WorkManagerImpl constructor(
scheduleBackgroundSync(
TAG_BACKGROUND_SYNC_OPENTASKS,
SyncOpenTasksWork::class.java,
enabled && caldavDao.getAccounts(TYPE_OPENTASKS).isNotEmpty())
caldavDao.getAccounts(TYPE_OPENTASKS).isNotEmpty()
|| openTaskDao.newAccounts().isNotEmpty())
}
}

@ -30,6 +30,7 @@ import org.tasks.data.OpenTaskDao.Companion.ACCOUNT_TYPE_ETESYNC
import org.tasks.data.OpenTaskDao.Companion.getInt
import org.tasks.data.OpenTaskDao.Companion.getLong
import org.tasks.data.OpenTaskDao.Companion.getString
import org.tasks.data.OpenTaskDao.Companion.newAccounts
import org.tasks.date.DateTimeUtils.newDateTime
import org.tasks.time.DateTime
import org.tasks.time.DateTimeUtils.currentTimeMillis
@ -57,7 +58,14 @@ class OpenTasksSynchronizer @Inject constructor(
private val cr = context.contentResolver
suspend fun sync() {
val lists = getLists()
val lists = openTaskDao.getListsByAccount()
lists.newAccounts(caldavDao).forEach { (account, _) ->
caldavDao.insert(CaldavAccount().apply {
name = account.split(":")[1]
uuid = account
accountType = CaldavAccount.TYPE_OPENTASKS
})
}
caldavDao.getAccounts(CaldavAccount.TYPE_OPENTASKS).forEach { account ->
if (!lists.containsKey(account.uuid)) {
setError(account, context.getString(R.string.account_not_found))
@ -69,9 +77,6 @@ class OpenTasksSynchronizer @Inject constructor(
}
}
private suspend fun getLists(): Map<String, List<CaldavCalendar>> =
openTaskDao.getLists().groupBy { it.account!! }
private suspend fun sync(account: CaldavAccount, lists: List<CaldavCalendar>) {
caldavDao
.findDeletedCalendars(account.uuid!!, lists.mapNotNull { it.url })

@ -21,7 +21,6 @@ import org.tasks.data.CaldavAccount.Companion.TYPE_LOCAL
import org.tasks.data.CaldavDao
import org.tasks.data.GoogleTaskAccount
import org.tasks.data.GoogleTaskListDao
import org.tasks.data.OpenTaskDao
import org.tasks.data.OpenTaskDao.Companion.ACCOUNT_TYPE_DAVx5
import org.tasks.data.OpenTaskDao.Companion.ACCOUNT_TYPE_ETESYNC
import org.tasks.etesync.EteSyncAccountSettingsActivity
@ -47,7 +46,6 @@ class Synchronization : InjectingPreferenceFragment() {
@Inject lateinit var googleTaskListDao: GoogleTaskListDao
@Inject lateinit var taskDeleter: TaskDeleter
@Inject lateinit var syncAdapters: SyncAdapters
@Inject lateinit var openTaskDao: OpenTaskDao
override fun getPreferenceXml() = R.xml.preferences_synchronization
@ -85,13 +83,8 @@ class Synchronization : InjectingPreferenceFragment() {
findPreference(R.string.add_account)
.setOnPreferenceClickListener {
lifecycleScope.launch {
val accounts = openTaskDao.accounts().filter {
caldavDao.getAccountByUuid(it) == null
}
newAccountDialog(this@Synchronization, REQUEST_ADD_ACCOUNT, accounts)
.show(parentFragmentManager, FRAG_TAG_ADD_ACCOUNT)
}
newAccountDialog(this@Synchronization, REQUEST_ADD_ACCOUNT)
.show(parentFragmentManager, FRAG_TAG_ADD_ACCOUNT)
false
}
}

@ -1,8 +1,8 @@
package org.tasks.sync
import android.app.Activity.RESULT_OK
import android.app.Dialog
import android.content.Intent
import android.content.Intent.ACTION_VIEW
import android.net.Uri
import android.os.Bundle
import android.view.View
@ -12,19 +12,12 @@ import android.widget.ImageView
import android.widget.TextView
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.todoroo.astrid.gtasks.auth.GtasksLoginActivity
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import org.tasks.R
import org.tasks.caldav.CaldavAccountSettingsActivity
import org.tasks.data.CaldavAccount
import org.tasks.data.CaldavDao
import org.tasks.data.OpenTaskDao.Companion.ACCOUNT_TYPE_DAVx5
import org.tasks.data.OpenTaskDao.Companion.ACCOUNT_TYPE_ETESYNC
import org.tasks.dialogs.DialogBuilder
import org.tasks.etesync.EteSyncAccountSettingsActivity
import org.tasks.jobs.WorkManager
import org.tasks.preferences.fragments.REQUEST_CALDAV_SETTINGS
import org.tasks.preferences.fragments.REQUEST_GOOGLE_TASKS
import org.tasks.themes.DrawableUtil
@ -34,36 +27,16 @@ import javax.inject.Inject
class AddAccountDialog : DialogFragment() {
@Inject lateinit var dialogBuilder: DialogBuilder
@Inject lateinit var caldavDao: CaldavDao
@Inject lateinit var syncAdapters: SyncAdapters
@Inject lateinit var workManager: WorkManager
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val services = requireActivity().resources.getStringArray(R.array.synchronization_services).toMutableList()
val descriptions = requireActivity().resources.getStringArray(R.array.synchronization_services_description).toMutableList()
val icons = arrayListOf(
R.drawable.ic_google,
R.drawable.ic_webdav_logo,
R.drawable.ic_etesync
)
val types = arrayListOf("", "", "")
requireArguments().getStringArrayList(EXTRA_ACCOUNTS)?.forEach { account ->
val (type, name) = account.split(":")
when (type) {
ACCOUNT_TYPE_DAVx5 -> {
services.add(name)
descriptions.add(getString(R.string.davx5))
types.add(ACCOUNT_TYPE_DAVx5)
icons.add(R.drawable.ic_davx5_icon_green_bg)
}
ACCOUNT_TYPE_ETESYNC -> {
services.add(name)
descriptions.add(getString(R.string.etesync))
types.add(ACCOUNT_TYPE_ETESYNC)
icons.add(R.drawable.ic_etesync)
}
}
val services = requireActivity().resources.getStringArray(R.array.synchronization_services)
val descriptions = requireActivity().resources.getStringArray(R.array.synchronization_services_description)
val typedArray = requireActivity().resources.obtainTypedArray(R.array.synchronization_services_icons)
val icons = IntArray(typedArray.length())
for (i in icons.indices) {
icons[i] = typedArray.getResourceId(i, 0)
}
typedArray.recycle()
val adapter: ArrayAdapter<String> = object : ArrayAdapter<String>(
requireActivity(), R.layout.simple_list_item_2_themed, R.id.text1, services) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
@ -83,38 +56,19 @@ class AddAccountDialog : DialogFragment() {
.setTitle(R.string.choose_synchronization_service)
.setSingleChoiceItems(adapter, -1) { dialog, which ->
when (which) {
0 -> {
activity?.startActivityForResult(
Intent(activity, GtasksLoginActivity::class.java),
REQUEST_GOOGLE_TASKS)
dialog.dismiss()
}
1 -> {
activity?.startActivityForResult(
Intent(activity, CaldavAccountSettingsActivity::class.java),
REQUEST_CALDAV_SETTINGS)
dialog.dismiss()
}
2 -> {
activity?.startActivityForResult(
Intent(activity, EteSyncAccountSettingsActivity::class.java),
REQUEST_CALDAV_SETTINGS)
dialog.dismiss()
}
else -> {
lifecycleScope.launch {
caldavDao.insert(CaldavAccount().apply {
name = services[which]
uuid = "${types[which]}:${name}"
accountType = CaldavAccount.TYPE_OPENTASKS
})
syncAdapters.sync(true)
workManager.updateBackgroundSync()
dialog.dismiss()
targetFragment?.onActivityResult(targetRequestCode, RESULT_OK, null)
}
}
0 -> activity?.startActivityForResult(
Intent(activity, GtasksLoginActivity::class.java),
REQUEST_GOOGLE_TASKS)
1 -> activity?.startActivityForResult(
Intent(activity, CaldavAccountSettingsActivity::class.java),
REQUEST_CALDAV_SETTINGS)
2 -> activity?.startActivityForResult(
Intent(activity, EteSyncAccountSettingsActivity::class.java),
REQUEST_CALDAV_SETTINGS)
3 -> activity?.startActivity(
Intent(ACTION_VIEW, Uri.parse("https://tasks.org/davx5")))
}
dialog.dismiss()
}
.setNeutralButton(R.string.help) { _, _ ->
activity?.startActivity(Intent(
@ -126,15 +80,8 @@ class AddAccountDialog : DialogFragment() {
}
companion object {
private const val EXTRA_ACCOUNTS = "extra_accounts"
fun newAccountDialog(
targetFragment: Fragment, rc: Int, openTaskAccounts: List<String>
): AddAccountDialog {
fun newAccountDialog(targetFragment: Fragment, rc: Int): AddAccountDialog {
val dialog = AddAccountDialog()
dialog.arguments = Bundle().apply {
putStringArrayList(EXTRA_ACCOUNTS, ArrayList(openTaskAccounts))
}
dialog.setTargetFragment(targetFragment, rc)
return dialog
}

@ -9,6 +9,7 @@ import org.tasks.data.CaldavAccount.Companion.TYPE_OPENTASKS
import org.tasks.data.CaldavDao
import org.tasks.data.GoogleTaskDao
import org.tasks.data.GoogleTaskListDao
import org.tasks.data.OpenTaskDao
import org.tasks.jobs.WorkManager
import org.tasks.jobs.WorkManager.Companion.TAG_SYNC_CALDAV
import org.tasks.jobs.WorkManager.Companion.TAG_SYNC_ETESYNC
@ -23,7 +24,8 @@ class SyncAdapters @Inject constructor(
workManager: WorkManager,
private val caldavDao: CaldavDao,
private val googleTaskDao: GoogleTaskDao,
private val googleTaskListDao: GoogleTaskListDao) {
private val googleTaskListDao: GoogleTaskListDao,
private val openTaskDao: OpenTaskDao) {
private val scope = CoroutineScope(newSingleThreadExecutor().asCoroutineDispatcher() + SupervisorJob())
private val googleTasks = Debouncer(TAG_SYNC_GOOGLE_TASKS) { workManager.googleTaskSync(it) }
private val caldav = Debouncer(TAG_SYNC_CALDAV) { workManager.caldavSync(it) }
@ -88,5 +90,7 @@ class SyncAdapters @Inject constructor(
private suspend fun isEteSyncEnabled() = caldavDao.getAccounts(TYPE_ETESYNC).isNotEmpty()
private suspend fun isOpenTaskSyncEnabled() = caldavDao.getAccounts(TYPE_OPENTASKS).isNotEmpty()
private suspend fun isOpenTaskSyncEnabled() =
caldavDao.getAccounts(TYPE_OPENTASKS).isNotEmpty()
|| openTaskDao.newAccounts().isNotEmpty()
}

@ -177,18 +177,21 @@
<item>@string/gtasks_GPr_header</item>
<item>@string/caldav</item>
<item>@string/etesync</item>
<item>@string/davx5</item>
</string-array>
<string-array name="synchronization_services_description">
<item>@string/google_tasks_selection_description</item>
<item>@string/caldav_selection_description</item>
<item>@string/etesync_selection_description</item>
<item>@string/davx5_selection_description</item>
</string-array>
<array name="synchronization_services_icons">
<item>@drawable/ic_google</item>
<item>@drawable/ic_webdav_logo</item>
<item>@drawable/ic_etesync</item>
<item>@drawable/ic_davx5_icon_green_bg</item>
</array>
<string-array name="chip_styles">

@ -546,6 +546,7 @@ File %1$s contained %2$s.\n\n
<string name="google_tasks_selection_description">Basic service that synchronizes with your Google account</string>
<string name="caldav_selection_description">Synchronization based on open internet standards</string>
<string name="etesync_selection_description">Open source, end-to-end encrypted synchronization</string>
<string name="davx5_selection_description">Synchronize your tasks with the DAVx⁵ app</string>
<string name="show_advanced_settings">Show advanced settings</string>
<string name="caldav_account_description">Requires an account with a CalDAV service provider or a self-hosted server. Find a service provider by visiting tasks.org/caldav</string>
<string name="etesync_account_description">Requires an account with EteSync.com or a self-hosted server</string>

Loading…
Cancel
Save