mirror of https://github.com/tasks/tasks
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
325 lines
14 KiB
Kotlin
325 lines
14 KiB
Kotlin
package org.tasks.backup
|
|
|
|
import android.app.ProgressDialog
|
|
import android.content.Context
|
|
import android.net.Uri
|
|
import android.os.Handler
|
|
import com.google.gson.Gson
|
|
import com.google.gson.JsonObject
|
|
import com.todoroo.astrid.dao.TaskDao
|
|
import com.todoroo.astrid.data.Task
|
|
import com.todoroo.astrid.service.TaskCreator.Companion.getDefaultAlarms
|
|
import com.todoroo.astrid.service.TaskMover
|
|
import com.todoroo.astrid.service.Upgrader
|
|
import com.todoroo.astrid.service.Upgrader.Companion.V12_4
|
|
import com.todoroo.astrid.service.Upgrader.Companion.V12_8
|
|
import com.todoroo.astrid.service.Upgrader.Companion.V6_4
|
|
import com.todoroo.astrid.service.Upgrader.Companion.getAndroidColor
|
|
import org.tasks.LocalBroadcastManager
|
|
import org.tasks.R
|
|
import org.tasks.caldav.VtodoCache
|
|
import org.tasks.data.*
|
|
import org.tasks.data.CaldavAccount.Companion.TYPE_GOOGLE_TASKS
|
|
import org.tasks.data.Place.Companion.newPlace
|
|
import org.tasks.db.Migrations.repeatFrom
|
|
import org.tasks.db.Migrations.withoutFrom
|
|
import org.tasks.preferences.Preferences
|
|
import timber.log.Timber
|
|
import java.io.FileNotFoundException
|
|
import java.io.IOException
|
|
import java.io.InputStream
|
|
import java.io.InputStreamReader
|
|
import javax.inject.Inject
|
|
|
|
class TasksJsonImporter @Inject constructor(
|
|
private val tagDataDao: TagDataDao,
|
|
private val userActivityDao: UserActivityDao,
|
|
private val taskDao: TaskDao,
|
|
private val locationDao: LocationDao,
|
|
private val localBroadcastManager: LocalBroadcastManager,
|
|
private val alarmDao: AlarmDao,
|
|
private val tagDao: TagDao,
|
|
private val filterDao: FilterDao,
|
|
private val taskAttachmentDao: TaskAttachmentDao,
|
|
private val caldavDao: CaldavDao,
|
|
private val preferences: Preferences,
|
|
private val taskMover: TaskMover,
|
|
private val taskListMetadataDao: TaskListMetadataDao,
|
|
private val vtodoCache: VtodoCache,
|
|
) {
|
|
|
|
private val result = ImportResult()
|
|
|
|
private fun setProgressMessage(
|
|
handler: Handler, progressDialog: ProgressDialog?, message: String) {
|
|
if (progressDialog == null) {
|
|
return
|
|
}
|
|
handler.post { progressDialog.setMessage(message) }
|
|
}
|
|
|
|
suspend fun importTasks(context: Context, backupFile: Uri?, progressDialog: ProgressDialog?): ImportResult {
|
|
val handler = Handler(context.mainLooper)
|
|
val gson = Gson()
|
|
val `is`: InputStream? = try {
|
|
context.contentResolver.openInputStream(backupFile!!)
|
|
} catch (e: FileNotFoundException) {
|
|
throw IllegalStateException(e)
|
|
}
|
|
val reader = InputStreamReader(`is`, TasksJsonExporter.UTF_8)
|
|
val input = gson.fromJson(reader, JsonObject::class.java)
|
|
try {
|
|
val data = input["data"]
|
|
val version = input["version"].asInt
|
|
val backupContainer = gson.fromJson(data, BackupContainer::class.java)
|
|
backupContainer.tags?.forEach { tagData ->
|
|
findTagData(tagData)?.let {
|
|
return@forEach
|
|
}
|
|
tagData.setColor(themeToColor(context, version, tagData.getColor()!!))
|
|
tagDataDao.createNew(tagData)
|
|
}
|
|
backupContainer.googleTaskAccounts?.forEach { googleTaskAccount ->
|
|
if (caldavDao.getAccount(TYPE_GOOGLE_TASKS, googleTaskAccount.account!!) == null) {
|
|
caldavDao.insert(
|
|
CaldavAccount().apply {
|
|
accountType = TYPE_GOOGLE_TASKS
|
|
uuid = googleTaskAccount.account
|
|
name = googleTaskAccount.account
|
|
username = googleTaskAccount.account
|
|
}
|
|
)
|
|
}
|
|
}
|
|
backupContainer.places?.forEach { place ->
|
|
if (locationDao.getByUid(place.uid!!) == null) {
|
|
locationDao.insert(place)
|
|
}
|
|
}
|
|
backupContainer.googleTaskLists?.forEach { googleTaskList ->
|
|
if (caldavDao.getCalendar(googleTaskList.remoteId!!) == null) {
|
|
caldavDao.insert(
|
|
CaldavCalendar(
|
|
account = googleTaskList.account,
|
|
uuid = googleTaskList.remoteId,
|
|
color = themeToColor(context, version, googleTaskList.color ?: 0),
|
|
|
|
)
|
|
)
|
|
}
|
|
}
|
|
backupContainer.filters?.forEach { filter ->
|
|
filter.setColor(themeToColor(context, version, filter.getColor()!!))
|
|
if (filterDao.getByName(filter.title!!) == null) {
|
|
filterDao.insert(filter)
|
|
}
|
|
}
|
|
backupContainer.caldavAccounts?.forEach { account ->
|
|
if (caldavDao.getAccountByUuid(account.uuid!!) == null) {
|
|
caldavDao.insert(account)
|
|
}
|
|
}
|
|
backupContainer.caldavCalendars?.forEach { calendar ->
|
|
if (caldavDao.getCalendarByUuid(calendar.uuid!!) == null) {
|
|
caldavDao.insert(
|
|
calendar.copy(color = themeToColor(context, version, calendar.color))
|
|
)
|
|
}
|
|
}
|
|
backupContainer.taskListMetadata?.forEach { tlm ->
|
|
val id = tlm.filter.takeIf { it?.isNotBlank() == true } ?: tlm.tagUuid!!
|
|
if (taskListMetadataDao.fetchByTagOrFilter(id) == null) {
|
|
taskListMetadataDao.insert(tlm)
|
|
}
|
|
}
|
|
backupContainer.taskAttachments?.forEach { attachment ->
|
|
if (taskAttachmentDao.getAttachment(attachment.remoteId) == null) {
|
|
taskAttachmentDao.insert(attachment)
|
|
}
|
|
}
|
|
backupContainer.tasks?.forEach { backup ->
|
|
result.taskCount++
|
|
setProgressMessage(
|
|
handler,
|
|
progressDialog,
|
|
context.getString(R.string.import_progress_read, result.taskCount))
|
|
val task = backup.task
|
|
taskDao.fetch(task.uuid)
|
|
?.let {
|
|
result.skipCount++
|
|
return@forEach
|
|
}
|
|
if (true == backup.caldavTasks
|
|
?.filter { it.deleted == 0L }
|
|
?.any { caldavDao.getTask(it.calendar!!, it.`object`!!) != null }) {
|
|
result.skipCount++
|
|
return@forEach
|
|
}
|
|
task.suppressRefresh()
|
|
task.suppressSync()
|
|
taskDao.createNew(task)
|
|
val taskId = task.id
|
|
val taskUuid = task.uuid
|
|
for (alarm in backup.alarms) {
|
|
alarm.task = taskId
|
|
alarmDao.insert(alarm)
|
|
}
|
|
if (version < V12_4) {
|
|
task.defaultReminders(task.ringFlags)
|
|
alarmDao.insert(task.getDefaultAlarms())
|
|
task.ringFlags = when {
|
|
task.isNotifyModeFive -> Task.NOTIFY_MODE_FIVE
|
|
task.isNotifyModeNonstop -> Task.NOTIFY_MODE_NONSTOP
|
|
else -> 0
|
|
}
|
|
taskDao.save(task)
|
|
}
|
|
if (version < V12_8) {
|
|
task.repeatFrom = task.recurrence.repeatFrom()
|
|
task.recurrence = task.recurrence.withoutFrom()
|
|
}
|
|
for (comment in backup.comments) {
|
|
comment.targetId = taskUuid
|
|
if (version < V6_4) {
|
|
comment.convertPictureUri()
|
|
}
|
|
userActivityDao.createNew(comment)
|
|
}
|
|
for (googleTask in backup.google) {
|
|
caldavDao.insert(
|
|
CaldavTask(
|
|
task = taskId,
|
|
calendar = googleTask.listId,
|
|
remoteId = googleTask.remoteId,
|
|
).apply {
|
|
remoteOrder = googleTask.remoteOrder
|
|
remoteParent = googleTask.remoteParent
|
|
lastSync = googleTask.lastSync
|
|
}
|
|
)
|
|
}
|
|
for (location in backup.locations) {
|
|
val place = newPlace()
|
|
place.longitude = location.longitude
|
|
place.latitude = location.latitude
|
|
place.name = location.name
|
|
place.address = location.address
|
|
place.url = location.url
|
|
place.phone = location.phone
|
|
locationDao.insert(place)
|
|
val geofence = Geofence()
|
|
geofence.task = taskId
|
|
geofence.place = place.uid
|
|
geofence.isArrival = location.arrival
|
|
geofence.isDeparture = location.departure
|
|
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)
|
|
}
|
|
backup.geofences?.forEach { geofence ->
|
|
geofence.task = taskId
|
|
locationDao.insert(geofence)
|
|
}
|
|
backup.attachments
|
|
?.mapNotNull { taskAttachmentDao.getAttachment(it.attachmentUid) }
|
|
?.map {
|
|
Attachment(
|
|
task = taskId,
|
|
fileId = it.id!!,
|
|
attachmentUid = it.remoteId,
|
|
)
|
|
}
|
|
?.let { taskAttachmentDao.insert(it) }
|
|
backup.caldavTasks?.forEach { caldavTask ->
|
|
caldavTask.task = taskId
|
|
caldavDao.insert(caldavTask)
|
|
}
|
|
backup.vtodo?.let {
|
|
val caldavTask =
|
|
backup.caldavTasks?.firstOrNull { t -> !t.isDeleted() } ?: return@let
|
|
val caldavCalendar = caldavDao.getCalendar(caldavTask.calendar!!) ?: return@let
|
|
vtodoCache.putVtodo(caldavCalendar, caldavTask, it)
|
|
}
|
|
result.importCount++
|
|
}
|
|
caldavDao.updateParents()
|
|
val ignoreKeys = ignorePrefs.map { context.getString(it) }
|
|
backupContainer
|
|
.intPrefs
|
|
?.filterNot { (key, _) -> ignoreKeys.contains(key) }
|
|
?.forEach { (key, value) -> preferences.setInt(key, value as Int) }
|
|
backupContainer
|
|
.longPrefs
|
|
?.filterNot { (key, _) -> ignoreKeys.contains(key) }
|
|
?.forEach { (key, value) -> preferences.setLong(key, value as Long) }
|
|
backupContainer
|
|
.stringPrefs
|
|
?.filterNot { (key, _) -> ignoreKeys.contains(key) }
|
|
?.forEach { (key, value) -> preferences.setString(key, value) }
|
|
backupContainer
|
|
.boolPrefs
|
|
?.filterNot { (key, _) -> ignoreKeys.contains(key) }
|
|
?.forEach { (key, value) -> preferences.setBoolean(key, value as Boolean) }
|
|
backupContainer
|
|
.setPrefs
|
|
?.filterNot { (key, _) -> ignoreKeys.contains(key) }
|
|
?.forEach { (key, value) -> preferences.setStringSet(key, value as HashSet<String>)}
|
|
if (version < Upgrader.V8_2) {
|
|
val themeIndex = preferences.getInt(R.string.p_theme_color, 7)
|
|
preferences.setInt(
|
|
R.string.p_theme_color,
|
|
getAndroidColor(context, themeIndex))
|
|
}
|
|
if (version < Upgrader.V9_6) {
|
|
taskMover.migrateLocalTasks()
|
|
}
|
|
reader.close()
|
|
`is`!!.close()
|
|
} catch (e: IOException) {
|
|
Timber.e(e)
|
|
}
|
|
localBroadcastManager.broadcastRefresh()
|
|
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
|
|
|
|
class ImportResult {
|
|
var taskCount = 0
|
|
var importCount = 0
|
|
var skipCount = 0
|
|
}
|
|
|
|
class LegacyLocation {
|
|
var name: String? = null
|
|
var address: String? = null
|
|
var phone: String? = null
|
|
var url: String? = null
|
|
var latitude = 0.0
|
|
var longitude = 0.0
|
|
var radius = 0
|
|
var arrival = false
|
|
var departure = false
|
|
}
|
|
|
|
companion object {
|
|
private val ignorePrefs = intArrayOf(
|
|
R.string.p_current_version,
|
|
R.string.p_backups_android_backup_last
|
|
)
|
|
}
|
|
} |