Remove Astrid XML import functionality

pull/1297/head
Alex Baker 3 years ago
parent 8e2c8e8e89
commit aa3d3ec5b6

@ -1,276 +0,0 @@
/*
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.backup
import android.app.Activity
import android.app.ProgressDialog
import android.content.DialogInterface
import android.net.Uri
import android.os.Handler
import android.text.TextUtils
import com.todoroo.andlib.utility.DialogUtilities
import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.service.TaskMover
import org.tasks.LocalBroadcastManager
import org.tasks.R
import org.tasks.analytics.Firebase
import org.tasks.backup.XmlReader
import org.tasks.data.*
import org.tasks.data.Place.Companion.newPlace
import org.tasks.dialogs.DialogBuilder
import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserException
import org.xmlpull.v1.XmlPullParserFactory
import timber.log.Timber
import java.io.IOException
import java.io.InputStreamReader
import javax.inject.Inject
class TasksXmlImporter @Inject constructor(
private val tagDataDao: TagDataDao,
private val userActivityDao: UserActivityDao,
private val dialogBuilder: DialogBuilder,
private val taskDao: TaskDao,
private val locationDao: LocationDao,
private val localBroadcastManager: LocalBroadcastManager,
private val alarmDao: AlarmDao,
private val tagDao: TagDao,
private val googleTaskDao: GoogleTaskDao,
private val taskMover: TaskMover,
private val firebase: Firebase) {
private var activity: Activity? = null
private var handler: Handler? = null
private var taskCount = 0
private var importCount = 0
private var skipCount = 0
private var errorCount = 0
private var progressDialog: ProgressDialog? = null
private var input: Uri? = null
private fun setProgressMessage(message: String) {
handler!!.post { progressDialog!!.setMessage(message) }
}
suspend fun importTasks(activity: Activity?, input: Uri?, progressDialog: ProgressDialog?) {
this.activity = activity
this.input = input
this.progressDialog = progressDialog
try {
performImport()
taskMover.migrateLocalTasks()
firebase.logEvent(R.string.event_xml_import)
} catch (e: IOException) {
firebase.reportException(e)
} catch (e: XmlPullParserException) {
firebase.reportException(e)
}
}
// --- importers
// =============================================================== FORMAT2
@Throws(IOException::class, XmlPullParserException::class)
private suspend fun performImport() {
val factory = XmlPullParserFactory.newInstance()
val xpp = factory.newPullParser()
val inputStream = activity!!.contentResolver.openInputStream(input!!)
val reader = InputStreamReader(inputStream)
xpp.setInput(reader)
try {
while (xpp.next() != XmlPullParser.END_DOCUMENT) {
val tag = xpp.name
if (xpp.eventType == XmlPullParser.END_TAG) {
// Ignore end tags
continue
}
if (tag != null) {
// Process <astrid ... >
if (tag == BackupConstants.ASTRID_TAG) {
val format = xpp.getAttributeValue(null, BackupConstants.ASTRID_ATTR_FORMAT)
when {
TextUtils.equals(format, FORMAT2) -> Format2TaskImporter(xpp).process()
TextUtils.equals(format, FORMAT3) -> Format3TaskImporter(xpp).process()
else -> throw UnsupportedOperationException(
"Did not know how to import tasks with xml format '$format'")
}
}
}
}
} finally {
reader.close()
inputStream!!.close()
localBroadcastManager.broadcastRefresh()
handler!!.post {
if (progressDialog!!.isShowing) {
DialogUtilities.dismissDialog(activity, progressDialog)
showSummary()
}
}
}
}
private fun showSummary() {
val r = activity!!.resources
dialogBuilder
.newDialog(R.string.import_summary_title)
.setMessage(
activity!!.getString(
R.string.import_summary_message,
"",
r.getQuantityString(R.plurals.Ntasks, taskCount, taskCount),
r.getQuantityString(R.plurals.Ntasks, importCount, importCount),
r.getQuantityString(R.plurals.Ntasks, skipCount, skipCount),
r.getQuantityString(R.plurals.Ntasks, errorCount, errorCount)))
.setPositiveButton(R.string.ok) { dialog: DialogInterface, id: Int -> dialog.dismiss() }
.show()
}
// =============================================================== FORMAT3
private open inner class Format2TaskImporter {
var xpp: XmlPullParser? = null
var currentTask: Task? = null
internal constructor()
internal constructor(xpp: XmlPullParser) {
this.xpp = xpp
}
open suspend fun process() {
while (xpp?.next() != XmlPullParser.END_DOCUMENT) {
val tag = xpp?.name
if (tag == null || xpp?.eventType == XmlPullParser.END_TAG) {
continue
}
try {
when (tag) {
BackupConstants.TASK_TAG -> parseTask()
BackupConstants.COMMENT_TAG -> parseComment()
BackupConstants.METADATA_TAG -> parseMetadata(2)
}
} catch (e: Exception) {
errorCount++
Timber.e(e)
}
}
}
suspend fun parseTask() {
taskCount++
setProgressMessage(activity!!.getString(R.string.import_progress_read, taskCount))
currentTask = Task(XmlReader(xpp))
val existingTask = taskDao.fetch(currentTask!!.uuid)
if (existingTask == null) {
taskDao.createNew(currentTask!!)
importCount++
} else {
skipCount++
}
}
/** Imports a comment from the XML we're reading. taken from EditNoteActivity.addComment() */
suspend fun parseComment() {
if (!currentTask!!.isSaved) {
return
}
val userActivity = UserActivity(XmlReader(xpp))
userActivityDao.createNew(userActivity)
}
suspend fun parseMetadata(format: Int) {
if (!currentTask!!.isSaved) {
return
}
val xml = XmlReader(xpp)
val key = xml.readString("key")
if ("alarm" == key) {
val alarm = Alarm()
alarm.task = currentTask!!.id
alarm.time = xml.readLong("value")
alarmDao.insert(alarm)
} else if ("geofence" == key) {
val place = newPlace()
place.name = xml.readString("value")
place.latitude = xml.readDouble("value2")
place.longitude = xml.readDouble("value3")
locationDao.insert(place)
val geofence = Geofence()
geofence.task = currentTask!!.id
geofence.place = place.uid
geofence.radius = xml.readInteger("value4")
geofence.isArrival = true
locationDao.insert(geofence)
} else if ("tags-tag" == key) {
val name = xml.readString("value")
val tagUid = xml.readString("value2")
if (tagDao.getTagByTaskAndTagUid(currentTask!!.id, tagUid) == null) {
tagDao.insert(Tag(currentTask!!, name, tagUid))
}
// Construct the TagData from Metadata
// Fix for failed backup, Version before 4.6.10
if (format == 2) {
var tagData = tagDataDao.getByUuid(tagUid)
if (tagData == null) {
tagData = TagData()
tagData.remoteId = tagUid
tagData.name = name
tagDataDao.createNew(tagData)
}
}
} else if ("gtasks" == key) {
val googleTask = GoogleTask()
googleTask.task = currentTask!!.id
googleTask.remoteId = xml.readString("value")
googleTask.listId = xml.readString("value2")
googleTask.parent = xml.readLong("value3")
googleTask.order = xml.readLong("value5")
googleTask.remoteOrder = xml.readLong("value6")
googleTask.lastSync = xml.readLong("value7")
googleTask.deleted = xml.readLong("deleted")
googleTaskDao.insert(googleTask)
}
}
}
private inner class Format3TaskImporter internal constructor(xpp: XmlPullParser) : Format2TaskImporter() {
private suspend fun parseTagdata() {
val tagData = TagData(XmlReader(xpp))
if (tagDataDao.getByUuid(tagData.remoteId!!) == null) {
tagDataDao.createNew(tagData)
}
}
init {
this.xpp = xpp
}
override suspend fun process() {
while (xpp?.next() != XmlPullParser.END_DOCUMENT) {
val tag = xpp?.name
if (tag == null || xpp?.eventType == XmlPullParser.END_TAG) {
continue
}
try {
when (tag) {
BackupConstants.TASK_TAG -> parseTask()
BackupConstants.METADATA_TAG -> parseMetadata(3)
BackupConstants.COMMENT_TAG -> parseComment()
BackupConstants.TAGDATA_TAG -> parseTagdata()
}
} catch (e: Exception) {
errorCount++
Timber.e(e)
}
}
}
}
companion object {
private const val FORMAT2 = "2" // $NON-NLS-1$
private const val FORMAT3 = "3" // $NON-NLS-1$
}
}

@ -11,7 +11,6 @@ import com.todoroo.andlib.data.Table
import com.todoroo.andlib.sql.Field
import com.todoroo.andlib.utility.DateUtilities
import org.tasks.Strings
import org.tasks.backup.XmlReader
import org.tasks.data.Tag
import org.tasks.date.DateTimeUtils
import org.tasks.date.DateTimeUtils.toDateTime
@ -122,30 +121,6 @@ class Task : Parcelable {
constructor()
@Ignore
constructor(reader: XmlReader) {
calendarURI = reader.readString("calendarUri")
completionDate = reader.readLong("completed")
creationDate = reader.readLong("created")
deletionDate = reader.readLong("deleted")
dueDate = reader.readLong("dueDate")
elapsedSeconds = reader.readInteger("elapsedSeconds")
estimatedSeconds = reader.readInteger("estimatedSeconds")
hideUntil = reader.readLong("hideUntil")
priority = reader.readInteger("importance")
modificationDate = reader.readLong("modified")
notes = reader.readString("notes")
recurrence = reader.readString("recurrence")
reminderFlags = reader.readInteger("notificationFlags")
reminderLast = reader.readLong("lastNotified")
reminderPeriod = reader.readLong("notifications")
reminderSnooze = reader.readLong("snoozeTime")
repeatUntil = reader.readLong("repeatUntil")
timerStart = reader.readLong("timerStart")
title = reader.readString("title")
remoteId = reader.readString("remoteId")
}
@Ignore
constructor(parcel: Parcel) {
calendarURI = parcel.readString()

@ -1,42 +1,11 @@
/*
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.backup
package org.tasks.backup
import androidx.documentfile.provider.DocumentFile
import com.google.api.services.drive.model.File
import org.tasks.time.DateTime
import java.util.regex.Pattern
/**
* Constants for backup XML attributes and nodes.
*
* @author Tim Su <tim></tim>@todoroo.com>
*/
object BackupConstants {
// Do NOT edit the constants in this file! You will break compatibility with old backups
// --- general xml
/** Tag containing Astrid backup data */
const val ASTRID_TAG = "astrid"
/** Attribute indicating backup file format */
const val ASTRID_ATTR_FORMAT = "format"
// --- format 2
/** Tag containing a task */
const val TASK_TAG = "task"
/** Tag containing a comment item */
const val COMMENT_TAG = "comment"
/** Tag containing a metadata item */
const val METADATA_TAG = "metadata"
/** Tag containing a tagdata item */
const val TAGDATA_TAG = "tagdata"
// --- general
const val INTERNAL_BACKUP = "backup.json"
const val EXPORT_FILE_NAME = "user.%s.json"
const val BACKUP_FILE_NAME = "auto.%s.json"

@ -6,7 +6,6 @@ import android.app.backup.FileBackupHelper
import android.content.Context
import android.net.Uri
import android.os.ParcelFileDescriptor
import com.todoroo.astrid.backup.BackupConstants
import dagger.hilt.EntryPoint
import dagger.hilt.InstallIn
import dagger.hilt.android.EntryPointAccessors

@ -11,7 +11,6 @@ import com.google.common.io.Files
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.todoroo.andlib.utility.DialogUtilities
import com.todoroo.astrid.backup.BackupConstants
import com.todoroo.astrid.data.Task
import org.tasks.BuildConfig
import org.tasks.R

@ -1,58 +0,0 @@
package org.tasks.backup;
import org.xmlpull.v1.XmlPullParser;
public class XmlReader {
private static final String XML_NULL = "null"; // $NON-NLS-1$
private final XmlPullParser xpp;
public XmlReader(XmlPullParser xpp) {
this.xpp = xpp;
}
public Long readLong(String name) {
final String value = xpp.getAttributeValue(null, name);
return value == null || XML_NULL.equals(value) ? null : Long.parseLong(value);
}
public void readLong(String name, ValueWriter<Long> writer) {
final Long value = readLong(name);
if (value != null) {
writer.write(value);
}
}
public Integer readInteger(String name) {
final String value = xpp.getAttributeValue(null, name);
return value == null || XML_NULL.equals(value) ? null : Integer.parseInt(value);
}
public void readInteger(String name, ValueWriter<Integer> writer) {
final Integer value = readInteger(name);
if (value != null) {
writer.write(value);
}
}
public String readString(String name) {
return xpp.getAttributeValue(null, name);
}
public void readString(String name, ValueWriter<String> writer) {
final String value = readString(name);
if (value != null) {
writer.write(value);
}
}
public Double readDouble(String name) {
final String value = xpp.getAttributeValue(null, name);
return value == null || XML_NULL.equals(value) ? null : Double.parseDouble(value);
}
public interface ValueWriter<T> {
void write(T value);
}
}

@ -3,7 +3,6 @@ package org.tasks.data
import androidx.room.*
import com.todoroo.andlib.data.Table
import com.todoroo.astrid.data.Task
import org.tasks.backup.XmlReader
@Entity(tableName = "tags", indices = [Index(name = "tag_task", value = ["task"])])
class Tag {
@ -42,13 +41,6 @@ class Tag {
this.tagUid = tagUid
}
@Ignore
constructor(xmlReader: XmlReader) {
xmlReader.readString("name") { name: String? -> this.name = name }
xmlReader.readString("tag_uid") { tagUid: String? -> this.tagUid = tagUid }
xmlReader.readString("task_uid") { taskUid: String -> setTaskUid(taskUid) }
}
fun getTaskUid(): String = taskUid!!
fun setTaskUid(taskUid: String) {

@ -9,7 +9,6 @@ import androidx.room.Ignore
import androidx.room.PrimaryKey
import com.todoroo.astrid.api.FilterListItem.NO_ORDER
import com.todoroo.astrid.data.Task
import org.tasks.backup.XmlReader
import org.tasks.themes.CustomIcons.LABEL
@Entity(tableName = "tagdata")
@ -44,14 +43,6 @@ class TagData : Parcelable {
constructor()
@Ignore
constructor(reader: XmlReader) {
reader.readString("remoteId") { remoteId: String? -> this.remoteId = remoteId }
reader.readString("name") { name: String? -> this.name = name }
reader.readInteger("color") { color: Int? -> setColor(color) }
reader.readString("tagOrdering") { tagOrdering: String? -> this.tagOrdering = tagOrdering }
}
@SuppressLint("ParcelClassLoader")
@Ignore
private constructor(parcel: Parcel) {

@ -12,7 +12,6 @@ import com.todoroo.astrid.data.Task
import org.json.JSONException
import org.json.JSONObject
import org.tasks.Strings
import org.tasks.backup.XmlReader
import timber.log.Timber
import java.io.File
@ -41,18 +40,6 @@ class UserActivity : Parcelable {
constructor()
@Ignore
constructor(reader: XmlReader) {
reader.readString("remoteId") { remoteId: String? -> this.remoteId = remoteId }
reader.readString("message") { message: String? -> this.message = message }
reader.readString("picture") { p: String? ->
picture = p
convertPictureUri()
}
reader.readString("target_id") { targetId: String? -> this.targetId = targetId }
reader.readLong("created_at") { created: Long -> this.created = created }
}
@Ignore
private constructor(parcel: Parcel) {
with(parcel) {

@ -7,7 +7,6 @@ import android.net.Uri
import android.os.Bundle
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import com.todoroo.astrid.backup.TasksXmlImporter
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.launch
@ -20,7 +19,6 @@ import javax.inject.Inject
@AndroidEntryPoint
class ImportTasksDialog : DialogFragment() {
@Inject lateinit var xmlImporter: TasksXmlImporter
@Inject lateinit var jsonImporter: TasksJsonImporter
@Inject lateinit var dialogBuilder: DialogBuilder
@Inject lateinit var context: Activity
@ -48,9 +46,6 @@ class ImportTasksDialog : DialogFragment() {
}
showSummary(result)
}
"xml" -> lifecycleScope.launch {
xmlImporter.importTasks(activity, data, progressDialog)
}
else -> throw RuntimeException("Invalid extension: $extension")
}
return progressDialog

@ -7,11 +7,11 @@ import com.google.api.client.http.javanet.NetHttpTransport
import com.google.api.client.json.jackson2.JacksonFactory
import com.google.api.services.drive.Drive
import com.google.api.services.drive.model.File
import com.todoroo.astrid.backup.BackupConstants
import com.todoroo.astrid.gtasks.api.HttpCredentialsAdapter
import com.todoroo.astrid.gtasks.api.HttpNotFoundException
import dagger.hilt.android.qualifiers.ApplicationContext
import org.tasks.DebugNetworkInterceptor
import org.tasks.backup.BackupConstants
import org.tasks.files.FileHelper
import org.tasks.googleapis.BaseInvoker
import org.tasks.preferences.Preferences

@ -7,9 +7,9 @@ import androidx.hilt.Assisted
import androidx.hilt.work.WorkerInject
import androidx.work.WorkerParameters
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.backup.BackupConstants
import org.tasks.R
import org.tasks.analytics.Firebase
import org.tasks.backup.BackupConstants
import org.tasks.backup.TasksJsonExporter
import org.tasks.preferences.Preferences
import timber.log.Timber

@ -8,11 +8,11 @@ import androidx.work.Data
import androidx.work.WorkerParameters
import com.google.api.client.googleapis.json.GoogleJsonResponseException
import com.google.api.services.drive.model.File
import com.todoroo.astrid.backup.BackupConstants
import org.tasks.LocalBroadcastManager
import org.tasks.R
import org.tasks.Strings.isNullOrEmpty
import org.tasks.analytics.Firebase
import org.tasks.backup.BackupConstants
import org.tasks.googleapis.InvokerFactory
import org.tasks.injection.BaseWorker
import org.tasks.preferences.Preferences

@ -8,12 +8,12 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.api.services.drive.DriveScopes
import com.todoroo.astrid.backup.BackupConstants
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.tasks.R
import org.tasks.backup.BackupConstants
import org.tasks.data.CaldavAccount
import org.tasks.data.CaldavDao
import org.tasks.date.DateTimeUtils.newDateTime

@ -442,7 +442,6 @@
<string name="param_user_no_churn">user_no_churn</string>
<string name="param_user_pro">user_pro</string>
<string name="param_click">click</string>
<string name="event_xml_import">xml_import</string>
<string name="event_todoagenda">cp_todoagenda</string>
<string name="event_astrid2taskprovider">cp_astrid2taskprovider</string>
<string name="event_sync_add_account">sync_add_account</string>

@ -1,4 +1,4 @@
package com.todoroo.astrid.backup
package org.tasks.backup
import org.junit.Assert.*
import org.junit.Test
Loading…
Cancel
Save