Import backup file in two passes

Import non-task data before loading tasks
pull/3565/head
Alex Baker 7 months ago
parent 93674075cb
commit 4c01ab2e66

@ -15,6 +15,8 @@ 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 kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import org.tasks.LocalBroadcastManager
@ -85,9 +87,37 @@ class TasksJsonImporter @Inject constructor(
handler.post { progressDialog.setMessage(message) }
}
suspend fun importTasks(context: Context, backupFile: Uri?, progressDialog: ProgressDialog?): ImportResult {
suspend fun importTasks(
context: Context,
backupFile: Uri?,
progressDialog: ProgressDialog?
): ImportResult = withContext(Dispatchers.IO) {
Timber.d("Importing backup file $backupFile")
val handler = Handler(context.mainLooper)
try {
val version = importMetadata(context, backupFile)
importTasks(context, backupFile, progressDialog, version)
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()
}
Timber.d("Updating parents")
caldavDao.updateParents()
} catch (e: IOException) {
Timber.e(e)
}
localBroadcastManager.broadcastRefresh()
result
}
private suspend fun importMetadata(
context: Context,
backupFile: Uri?,
): Int {
val `is`: InputStream? = try {
context.contentResolver.openInputStream(backupFile!!)
} catch (e: FileNotFoundException) {
@ -97,167 +127,192 @@ class TasksJsonImporter @Inject constructor(
val reader = JsonReader(bufferedReader)
reader.isLenient = true
val ignoreKeys = ignorePrefs.map { context.getString(it) }
try {
reader.beginObject()
var version = 0
while (reader.hasNext()) {
when (val name = reader.nextName()) {
"version" -> version = reader.nextInt().also { Timber.d("Backup version: $it") }
"timestamp" -> reader.nextLong().let { Timber.d("Backup timestamp: $it") }
"data" -> {
reader.beginObject()
while (reader.hasNext()) {
when (val element = reader.nextName()) {
"tasks" -> {
reader.forEach<TaskBackup> { backup ->
result.taskCount++
setProgressMessage(
handler,
progressDialog,
context.getString(R.string.import_progress_read, result.taskCount))
importTask(backup, version)
}
reader.beginObject()
var version = 0
while (reader.hasNext()) {
when (val name = reader.nextName()) {
"version" -> version = reader.nextInt().also { Timber.d("Backup version: $it") }
"timestamp" -> reader.nextLong().let { Timber.d("Backup timestamp: $it") }
"data" -> {
reader.beginObject()
while (reader.hasNext()) {
when (val element = reader.nextName()) {
"places" -> reader.forEach<Place> { place ->
if (locationDao.getByUid(place.uid!!) == null) {
locationDao.insert(
place.copy(icon = place.icon.migrateLegacyIcon())
)
}
"places" -> reader.forEach<Place> { place ->
if (locationDao.getByUid(place.uid!!) == null) {
locationDao.insert(
place.copy(icon = place.icon.migrateLegacyIcon())
)
}
}
"tags" -> reader.forEach<TagData> { tagData ->
findTagData(tagData)?.let {
return@forEach
}
"tags" -> reader.forEach<TagData> { tagData ->
findTagData(tagData)?.let {
return@forEach
}
tagDataDao.insert(
tagData.copy(
color = themeToColor(context, version, tagData.color ?: 0),
icon = tagData.icon.migrateLegacyIcon(),
)
tagDataDao.insert(
tagData.copy(
color = themeToColor(context, version, tagData.color ?: 0),
icon = tagData.icon.migrateLegacyIcon(),
)
}
"filters" -> reader.forEach<Filter> {
it
.let {
if (version < Upgrade_13_2.VERSION)
filterCriteriaProvider.rebuildFilter(it)
else
it
}
.let { filter ->
if (filterDao.getByName(filter.title!!) == null) {
filterDao.insert(
filter.copy(
color = themeToColor(context, version, filter.color ?: 0),
icon = filter.icon.migrateLegacyIcon(),
)
)
}
"filters" -> reader.forEach<Filter> {
it
.let {
if (version < Upgrade_13_2.VERSION)
filterCriteriaProvider.rebuildFilter(it)
else
it
}
.let { filter ->
if (filterDao.getByName(filter.title!!) == null) {
filterDao.insert(
filter.copy(
color = themeToColor(context, version, filter.color ?: 0),
icon = filter.icon.migrateLegacyIcon(),
)
}
)
}
}
"caldavAccounts" -> reader.forEach<CaldavAccount> { account ->
if (caldavDao.getAccountByUuid(account.uuid!!) == null) {
caldavDao.insert(account)
}
}
"caldavAccounts" -> reader.forEach<CaldavAccount> { account ->
if (caldavDao.getAccountByUuid(account.uuid!!) == null) {
caldavDao.insert(account)
}
"caldavCalendars" -> reader.forEach<CaldavCalendar> { calendar ->
if (caldavDao.getCalendarByUuid(calendar.uuid!!) == null) {
caldavDao.insert(
calendar.copy(
color = themeToColor(context, version, calendar.color),
icon = calendar.icon.migrateLegacyIcon(),
)
}
"caldavCalendars" -> reader.forEach<CaldavCalendar> { calendar ->
if (caldavDao.getCalendarByUuid(calendar.uuid!!) == null) {
caldavDao.insert(
calendar.copy(
color = themeToColor(context, version, calendar.color),
icon = calendar.icon.migrateLegacyIcon(),
)
}
)
}
"taskListMetadata" -> reader.forEach<TaskListMetadata> { tlm ->
val id = tlm.filter.takeIf { it?.isNotBlank() == true } ?: tlm.tagUuid!!
if (taskListMetadataDao.fetchByTagOrFilter(id) == null) {
taskListMetadataDao.insert(tlm)
}
}
"taskListMetadata" -> reader.forEach<TaskListMetadata> { tlm ->
val id = tlm.filter.takeIf { it?.isNotBlank() == true } ?: tlm.tagUuid!!
if (taskListMetadataDao.fetchByTagOrFilter(id) == null) {
taskListMetadataDao.insert(tlm)
}
"taskAttachments" -> reader.forEach<TaskAttachment> { attachment ->
if (taskAttachmentDao.getAttachment(attachment.remoteId) == null) {
taskAttachmentDao.insert(attachment)
}
}
"taskAttachments" -> reader.forEach<TaskAttachment> { attachment ->
if (taskAttachmentDao.getAttachment(attachment.remoteId) == null) {
taskAttachmentDao.insert(attachment)
}
"intPrefs" ->
Json.decodeFromString<Map<String, Integer>>(reader.jsonString())
.filterNot { (key, _) -> ignoreKeys.contains(key) }
.forEach { (k, v) -> preferences.setInt(k, v as Int) }
"longPrefs" ->
Json.decodeFromString<Map<String, java.lang.Long>>(reader.jsonString())
.filterNot { (key, _) -> ignoreKeys.contains(key) }
.forEach { (k, v) -> preferences.setLong(k, v as Long)}
"stringPrefs" ->
Json.decodeFromString<Map<String, String>>(reader.jsonString())
.filterNot { (k, _) -> ignoreKeys.contains(k) }
.forEach { (k, v) -> preferences.setString(k, v)}
"boolPrefs" ->
Json.decodeFromString<Map<String, java.lang.Boolean>>(reader.jsonString())
.filterNot { (k, _) -> ignoreKeys.contains(k) }
.forEach { (k, v) -> preferences.setBoolean(k, v as Boolean) }
"setPrefs" ->
Json.decodeFromString<Map<String, Set<String>>>(reader.jsonString())
.filterNot { (k, _) -> ignoreKeys.contains(k) }
.forEach { (k, v) -> preferences.setStringSet(k, v as HashSet<String>)}
"googleTaskAccounts" -> reader.forEach<GoogleTaskAccount> { googleTaskAccount ->
if (caldavDao.getAccount(TYPE_GOOGLE_TASKS, googleTaskAccount.account!!) == null) {
caldavDao.insert(
CaldavAccount(
accountType = TYPE_GOOGLE_TASKS,
uuid = googleTaskAccount.account,
name = googleTaskAccount.account,
username = googleTaskAccount.account,
)
}
"intPrefs" ->
Json.decodeFromString<Map<String, Integer>>(reader.jsonString())
.filterNot { (key, _) -> ignoreKeys.contains(key) }
.forEach { (k, v) -> preferences.setInt(k, v as Int) }
"longPrefs" ->
Json.decodeFromString<Map<String, java.lang.Long>>(reader.jsonString())
.filterNot { (key, _) -> ignoreKeys.contains(key) }
.forEach { (k, v) -> preferences.setLong(k, v as Long)}
"stringPrefs" ->
Json.decodeFromString<Map<String, String>>(reader.jsonString())
.filterNot { (k, _) -> ignoreKeys.contains(k) }
.forEach { (k, v) -> preferences.setString(k, v)}
"boolPrefs" ->
Json.decodeFromString<Map<String, java.lang.Boolean>>(reader.jsonString())
.filterNot { (k, _) -> ignoreKeys.contains(k) }
.forEach { (k, v) -> preferences.setBoolean(k, v as Boolean) }
"setPrefs" ->
Json.decodeFromString<Map<String, Set<String>>>(reader.jsonString())
.filterNot { (k, _) -> ignoreKeys.contains(k) }
.forEach { (k, v) -> preferences.setStringSet(k, v as HashSet<String>)}
"googleTaskAccounts" -> reader.forEach<GoogleTaskAccount> { googleTaskAccount ->
if (caldavDao.getAccount(TYPE_GOOGLE_TASKS, googleTaskAccount.account!!) == null) {
caldavDao.insert(
CaldavAccount(
accountType = TYPE_GOOGLE_TASKS,
uuid = googleTaskAccount.account,
name = googleTaskAccount.account,
username = googleTaskAccount.account,
)
}
)
}
"googleTaskLists" -> reader.forEach<GoogleTaskList> { googleTaskList ->
if (caldavDao.getCalendar(googleTaskList.remoteId!!) == null) {
caldavDao.insert(
CaldavCalendar(
account = googleTaskList.account,
uuid = googleTaskList.remoteId,
color = themeToColor(context, version, googleTaskList.color ?: 0),
icon = googleTaskList.icon?.toString().migrateLegacyIcon(),
)
}
"googleTaskLists" -> reader.forEach<GoogleTaskList> { googleTaskList ->
if (caldavDao.getCalendar(googleTaskList.remoteId!!) == null) {
caldavDao.insert(
CaldavCalendar(
account = googleTaskList.account,
uuid = googleTaskList.remoteId,
color = themeToColor(context, version, googleTaskList.color ?: 0),
icon = googleTaskList.icon?.toString().migrateLegacyIcon(),
)
}
}
else -> {
Timber.w("Skipping $element")
reader.skipValue()
)
}
}
else -> {
Timber.w("Skipping $element")
reader.skipValue()
}
}
reader.endObject()
}
else -> {
Timber.w("Skipping $name")
reader.skipValue()
}
reader.endObject()
}
else -> {
Timber.w("Skipping $name")
reader.skipValue()
}
}
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()
bufferedReader.close()
`is`.close()
return version
}
private suspend fun importTasks(
context: Context,
backupFile: Uri?,
progressDialog: ProgressDialog?,
version: Int,
) {
val handler = Handler(context.mainLooper)
val `is`: InputStream? = try {
context.contentResolver.openInputStream(backupFile!!)
} catch (e: FileNotFoundException) {
throw IllegalStateException(e)
}
val bufferedReader = `is`!!.bufferedReader()
val reader = JsonReader(bufferedReader)
reader.isLenient = true
reader.beginObject()
while (reader.hasNext()) {
when (val name = reader.nextName()) {
"data" -> {
reader.beginObject()
while (reader.hasNext()) {
when (val element = reader.nextName()) {
"tasks" -> {
reader.forEach<TaskBackup> { backup ->
result.taskCount++
setProgressMessage(
handler,
progressDialog,
context.getString(R.string.import_progress_read, result.taskCount))
importTask(backup, version)
}
}
else -> {
Timber.w("Skipping $element")
reader.skipValue()
}
}
}
reader.endObject()
}
else -> {
Timber.w("Skipping $name")
reader.skipValue()
}
}
Timber.d("Updating parents")
caldavDao.updateParents()
reader.close()
bufferedReader.close()
`is`!!.close()
} catch (e: IOException) {
Timber.e(e)
}
localBroadcastManager.broadcastRefresh()
return result
reader.close()
bufferedReader.close()
`is`.close()
}
private suspend fun importTask(backup: TaskBackup, version: Int) {

Loading…
Cancel
Save