diff --git a/app/src/main/java/org/tasks/backup/BackupContainer.java b/app/src/main/java/org/tasks/backup/BackupContainer.java deleted file mode 100644 index 8c8ee6f9c..000000000 --- a/app/src/main/java/org/tasks/backup/BackupContainer.java +++ /dev/null @@ -1,159 +0,0 @@ -package org.tasks.backup; - -import static java.util.Collections.emptyList; -import static java.util.Collections.emptyMap; - -import com.todoroo.astrid.data.Task; -import java.util.List; -import java.util.Map; -import org.tasks.backup.TasksJsonImporter.LegacyLocation; -import org.tasks.data.Alarm; -import org.tasks.data.CaldavAccount; -import org.tasks.data.CaldavCalendar; -import org.tasks.data.CaldavTask; -import org.tasks.data.Filter; -import org.tasks.data.Geofence; -import org.tasks.data.GoogleTask; -import org.tasks.data.GoogleTaskAccount; -import org.tasks.data.GoogleTaskList; -import org.tasks.data.Place; -import org.tasks.data.Tag; -import org.tasks.data.TagData; -import org.tasks.data.TaskAttachment; -import org.tasks.data.UserActivity; - -class BackupContainer { - - private final List tasks; - private final List places; - private final List tags; - private final List filters; - private final List googleTaskLists; - private final List googleTaskAccounts; - private final List caldavAccounts; - private final List caldavCalendars; - private final Map intPrefs; - private final Map longPrefs; - private final Map stringPrefs; - private final Map boolPrefs; - - BackupContainer( - List tasks, - List places, - List tags, - List filters, - List googleTaskAccounts, - List googleTaskLists, - List caldavAccounts, - List caldavCalendars, - Map intPrefs, - Map longPrefs, - Map stringPrefs, - Map boolPrefs) { - this.tasks = tasks; - this.places = places; - this.tags = tags; - this.filters = filters; - this.googleTaskAccounts = googleTaskAccounts; - this.googleTaskLists = googleTaskLists; - this.caldavAccounts = caldavAccounts; - this.caldavCalendars = caldavCalendars; - this.intPrefs = intPrefs; - this.longPrefs = longPrefs; - this.stringPrefs = stringPrefs; - this.boolPrefs = boolPrefs; - } - - public List getTasks() { - return tasks == null ? emptyList() : tasks; - } - - public List getTags() { - return tags == null ? emptyList() : tags; - } - - public List getFilters() { - return filters == null ? emptyList() : filters; - } - - List getGoogleTaskLists() { - return googleTaskLists == null ? emptyList() : googleTaskLists; - } - - List getCaldavAccounts() { - return caldavAccounts == null ? emptyList() : caldavAccounts; - } - - List getCaldavCalendars() { - return caldavCalendars == null ? emptyList() : caldavCalendars; - } - - List getGoogleTaskAccounts() { - return googleTaskAccounts == null ? emptyList() : googleTaskAccounts; - } - - public List getPlaces() { - return places == null ? emptyList() : places; - } - - Map getIntPrefs() { - return intPrefs == null ? emptyMap() : intPrefs; - } - - Map getLongPrefs() { - return longPrefs == null ? emptyMap() : longPrefs; - } - - Map getStringPrefs() { - return stringPrefs == null ? emptyMap() : stringPrefs; - } - - Map getBoolPrefs() { - return boolPrefs == null ? emptyMap() : boolPrefs; - } - - static class TaskBackup { - - final Task task; - final List alarms; - final List locations; - final List tags; - final List google; - final List comments; - private final List geofences; - private final List attachments; - private final List caldavTasks; - - TaskBackup( - Task task, - List alarms, - List geofences, - List tags, - List google, - List comments, - List attachments, - List caldavTasks) { - this.task = task; - this.alarms = alarms; - this.geofences = geofences; - this.tags = tags; - this.google = google; - this.comments = comments; - this.attachments = attachments; - this.caldavTasks = caldavTasks; - locations = emptyList(); - } - - List getAttachments() { - return attachments == null ? emptyList() : attachments; - } - - List getCaldavTasks() { - return caldavTasks == null ? emptyList() : caldavTasks; - } - - List getGeofences() { - return geofences == null ? emptyList() : geofences; - } - } -} diff --git a/app/src/main/java/org/tasks/backup/BackupContainer.kt b/app/src/main/java/org/tasks/backup/BackupContainer.kt new file mode 100644 index 000000000..629ecae6f --- /dev/null +++ b/app/src/main/java/org/tasks/backup/BackupContainer.kt @@ -0,0 +1,33 @@ +package org.tasks.backup + +import com.todoroo.astrid.data.Task +import org.tasks.backup.TasksJsonImporter.LegacyLocation +import org.tasks.data.* + +class BackupContainer( + val tasks: List?, + val places: List?, + val tags: List?, + val filters: List?, + val googleTaskAccounts: List?, + val googleTaskLists: List?, + val caldavAccounts: List?, + val caldavCalendars: List?, + val intPrefs: Map?, + val longPrefs: Map?, + val stringPrefs: Map?, + val boolPrefs: Map?) { + + class TaskBackup( + val task: Task, + val alarms: List, + val geofences: List?, + val tags: List, + val google: List, + val comments: List, + val attachments: List?, + val caldavTasks: List?) { + + val locations: List = emptyList() + } +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/backup/TasksJsonExporter.java b/app/src/main/java/org/tasks/backup/TasksJsonExporter.java deleted file mode 100755 index c03b27475..000000000 --- a/app/src/main/java/org/tasks/backup/TasksJsonExporter.java +++ /dev/null @@ -1,266 +0,0 @@ -package org.tasks.backup; - -import static org.tasks.date.DateTimeUtils.newDateTime; - -import android.app.Activity; -import android.app.ProgressDialog; -import android.app.backup.BackupManager; -import android.content.Context; -import android.net.Uri; -import android.os.Handler; -import android.widget.Toast; -import androidx.annotation.Nullable; -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.dao.TaskDao; -import com.todoroo.astrid.data.Task; -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import javax.inject.Inject; -import org.tasks.BuildConfig; -import org.tasks.R; -import org.tasks.data.AlarmDao; -import org.tasks.data.CaldavDao; -import org.tasks.data.FilterDao; -import org.tasks.data.GoogleTaskDao; -import org.tasks.data.GoogleTaskListDao; -import org.tasks.data.LocationDao; -import org.tasks.data.TagDao; -import org.tasks.data.TagDataDao; -import org.tasks.data.TaskAttachmentDao; -import org.tasks.data.UserActivityDao; -import org.tasks.files.FileHelper; -import org.tasks.jobs.WorkManager; -import org.tasks.preferences.Preferences; -import timber.log.Timber; - -public class TasksJsonExporter { - - @SuppressWarnings("CharsetObjectCanBeUsed") - static final Charset UTF_8 = Charset.forName("UTF-8"); - private static final String MIME = "application/json"; - private static final String EXTENSION = ".json"; - - // --- public interface - private final TagDataDao tagDataDao; - - // --- implementation - private final AlarmDao alarmDao; - private final LocationDao locationDao; - private final TagDao tagDao; - private final GoogleTaskDao googleTaskDao; - private final FilterDao filterDao; - private final GoogleTaskListDao googleTaskListDao; - private final TaskAttachmentDao taskAttachmentDao; - private final CaldavDao caldavDao; - private final WorkManager workManager; - private final TaskDao taskDao; - private final UserActivityDao userActivityDao; - private final Preferences preferences; - private Context context; - private int exportCount = 0; - private ProgressDialog progressDialog; - private Handler handler; - - @Inject - public TasksJsonExporter( - TagDataDao tagDataDao, - TaskDao taskDao, - UserActivityDao userActivityDao, - Preferences preferences, - AlarmDao alarmDao, - LocationDao locationDao, - TagDao tagDao, - GoogleTaskDao googleTaskDao, - FilterDao filterDao, - GoogleTaskListDao googleTaskListDao, - TaskAttachmentDao taskAttachmentDao, - CaldavDao caldavDao, - WorkManager workManager) { - this.tagDataDao = tagDataDao; - this.taskDao = taskDao; - this.userActivityDao = userActivityDao; - this.preferences = preferences; - this.alarmDao = alarmDao; - this.locationDao = locationDao; - this.tagDao = tagDao; - this.googleTaskDao = googleTaskDao; - this.filterDao = filterDao; - this.googleTaskListDao = googleTaskListDao; - this.taskAttachmentDao = taskAttachmentDao; - this.caldavDao = caldavDao; - this.workManager = workManager; - } - - private static String getDateForExport() { - return newDateTime().toString("yyMMdd-HHmm"); - } - - private void post(Runnable runnable) { - if (handler != null) { - handler.post(runnable); - } - } - - private void setProgress(final int taskNumber, final int total) { - post( - () -> { - progressDialog.setMax(total); - progressDialog.setProgress(taskNumber); - }); - } - - public void exportTasks( - final Context context, - final ExportType exportType, - @Nullable final ProgressDialog progressDialog) { - this.context = context; - this.exportCount = 0; - this.progressDialog = progressDialog; - - if (exportType == ExportType.EXPORT_TYPE_MANUAL) { - handler = new Handler(); - new Thread(() -> runBackup(exportType)).start(); - } else { - runBackup(exportType); - } - } - - private void runBackup(ExportType exportType) { - try { - String filename = getFileName(exportType); - List tasks = taskDao.getAll(); - if (tasks.size() > 0) { - File file = - new File( - String.format("%s/%s", context.getFilesDir(), BackupConstants.INTERNAL_BACKUP)); - file.delete(); - file.createNewFile(); - Uri internalStorageBackup = Uri.fromFile(file); - OutputStream os = context.getContentResolver().openOutputStream(internalStorageBackup); - doTasksExport(os, tasks); - os.close(); - - Uri externalStorageBackup = - FileHelper.newFile( - context, - preferences.getBackupDirectory(), - MIME, - Files.getNameWithoutExtension(filename), - EXTENSION); - - FileHelper.copyStream(context, internalStorageBackup, externalStorageBackup); - - workManager.scheduleDriveUpload(externalStorageBackup, exportType == ExportType.EXPORT_TYPE_SERVICE); - - new BackupManager(context).dataChanged(); - } - - if (exportType == ExportType.EXPORT_TYPE_MANUAL) { - onFinishExport(filename); - } - } catch (IOException e) { - Timber.e(e); - } finally { - post( - () -> { - if (progressDialog != null - && progressDialog.isShowing() - && context instanceof Activity) { - DialogUtilities.dismissDialog((Activity) context, progressDialog); - } - }); - } - } - - private void doTasksExport(OutputStream os, List tasks) throws IOException { - - List taskBackups = new ArrayList<>(); - - for (Task task : tasks) { - setProgress(taskBackups.size(), tasks.size()); - long taskId = task.getId(); - taskBackups.add( - new BackupContainer.TaskBackup( - task, - alarmDao.getAlarms(taskId), - locationDao.getGeofencesForTask(taskId), - tagDao.getTagsForTask(taskId), - googleTaskDao.getAllByTaskId(taskId), - userActivityDao.getCommentsForTask(task.getUuid()), - taskAttachmentDao.getAttachments(task.getUuid()), - caldavDao.getTasks(taskId))); - } - - Map data = new HashMap<>(); - data.put("version", BuildConfig.VERSION_CODE); - data.put("timestamp", System.currentTimeMillis()); - data.put( - "data", - new BackupContainer( - taskBackups, - locationDao.getPlaces(), - tagDataDao.getAll(), - filterDao.getAll(), - googleTaskListDao.getAccounts(), - googleTaskListDao.getAllLists(), - caldavDao.getAccounts(), - caldavDao.getCalendars(), - preferences.getPrefs(Integer.class), - preferences.getPrefs(Long.class), - preferences.getPrefs(String.class), - preferences.getPrefs(Boolean.class))); - - OutputStreamWriter out = new OutputStreamWriter(os, UTF_8); - Gson gson = BuildConfig.DEBUG ? new GsonBuilder().setPrettyPrinting().create() : new Gson(); - out.write(gson.toJson(data)); - out.close(); - exportCount = taskBackups.size(); - } - - private void onFinishExport(final String outputFile) { - post( - () -> { - if (exportCount == 0) { - Toast.makeText( - context, context.getString(R.string.export_toast_no_tasks), Toast.LENGTH_LONG) - .show(); - } else { - CharSequence text = - String.format( - context.getString(R.string.export_toast), - context - .getResources() - .getQuantityString(R.plurals.Ntasks, exportCount, exportCount), - outputFile); - Toast.makeText(context, text, Toast.LENGTH_LONG).show(); - } - }); - } - - private String getFileName(ExportType type) { - switch (type) { - case EXPORT_TYPE_SERVICE: - return String.format(BackupConstants.BACKUP_FILE_NAME, getDateForExport()); - case EXPORT_TYPE_MANUAL: - return String.format(BackupConstants.EXPORT_FILE_NAME, getDateForExport()); - default: - throw new UnsupportedOperationException("Unhandled export type"); - } - } - - public enum ExportType { - EXPORT_TYPE_SERVICE, - EXPORT_TYPE_MANUAL - } -} diff --git a/app/src/main/java/org/tasks/backup/TasksJsonExporter.kt b/app/src/main/java/org/tasks/backup/TasksJsonExporter.kt new file mode 100755 index 000000000..1fdfaee2d --- /dev/null +++ b/app/src/main/java/org/tasks/backup/TasksJsonExporter.kt @@ -0,0 +1,185 @@ +package org.tasks.backup + +import android.app.Activity +import android.app.ProgressDialog +import android.app.backup.BackupManager +import android.content.Context +import android.net.Uri +import android.os.Handler +import android.widget.Toast +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.dao.TaskDao +import com.todoroo.astrid.data.Task +import org.tasks.BuildConfig +import org.tasks.R +import org.tasks.backup.BackupContainer.TaskBackup +import org.tasks.data.* +import org.tasks.date.DateTimeUtils.newDateTime +import org.tasks.files.FileHelper +import org.tasks.jobs.WorkManager +import org.tasks.preferences.Preferences +import timber.log.Timber +import java.io.File +import java.io.IOException +import java.io.OutputStream +import java.io.OutputStreamWriter +import java.nio.charset.Charset +import java.util.* +import javax.inject.Inject + +class TasksJsonExporter @Inject constructor( + private val tagDataDao: TagDataDao, + private val taskDao: TaskDao, + private val userActivityDao: UserActivityDao, + private val preferences: Preferences, + private val alarmDao: AlarmDao, + private val locationDao: LocationDao, + private val tagDao: TagDao, + private val googleTaskDao: GoogleTaskDao, + private val filterDao: FilterDao, + private val googleTaskListDao: GoogleTaskListDao, + private val taskAttachmentDao: TaskAttachmentDao, + private val caldavDao: CaldavDao, + private val workManager: WorkManager) { + + private var context: Context? = null + private var exportCount = 0 + private var progressDialog: ProgressDialog? = null + private var handler: Handler? = null + + private fun post(runnable: () -> Unit) = handler?.post(runnable) + + private fun setProgress(taskNumber: Int, total: Int) = post { + progressDialog?.max = total + progressDialog?.progress = taskNumber + } + + fun exportTasks(context: Context?, exportType: ExportType, progressDialog: ProgressDialog?) { + this.context = context + exportCount = 0 + this.progressDialog = progressDialog + if (exportType == ExportType.EXPORT_TYPE_MANUAL) { + handler = Handler() + Thread(Runnable { runBackup(exportType) }).start() + } else { + runBackup(exportType) + } + } + + private fun runBackup(exportType: ExportType) { + try { + val filename = getFileName(exportType) + val tasks = taskDao.getAll() + if (tasks.isNotEmpty()) { + val file = File(String.format("%s/%s", context!!.filesDir, BackupConstants.INTERNAL_BACKUP)) + file.delete() + file.createNewFile() + val internalStorageBackup = Uri.fromFile(file) + val os = context!!.contentResolver.openOutputStream(internalStorageBackup) + doTasksExport(os, tasks) + os!!.close() + val externalStorageBackup = FileHelper.newFile( + context, + preferences.backupDirectory, + MIME, + Files.getNameWithoutExtension(filename), + EXTENSION) + FileHelper.copyStream(context, internalStorageBackup, externalStorageBackup) + workManager.scheduleDriveUpload(externalStorageBackup, exportType == ExportType.EXPORT_TYPE_SERVICE) + BackupManager(context).dataChanged() + } + if (exportType == ExportType.EXPORT_TYPE_MANUAL) { + onFinishExport(filename) + } + } catch (e: IOException) { + Timber.e(e) + } finally { + post { + if (progressDialog != null && progressDialog!!.isShowing + && context is Activity) { + DialogUtilities.dismissDialog(context as Activity?, progressDialog) + } + } + } + } + + @Throws(IOException::class) + private fun doTasksExport(os: OutputStream?, tasks: List) { + val taskBackups: MutableList = ArrayList() + for (task in tasks) { + setProgress(taskBackups.size, tasks.size) + val taskId = task.id + taskBackups.add( + TaskBackup( + task, + alarmDao.getAlarms(taskId), + locationDao.getGeofencesForTask(taskId), + tagDao.getTagsForTask(taskId), + googleTaskDao.getAllByTaskId(taskId), + userActivityDao.getCommentsForTask(task.uuid), + taskAttachmentDao.getAttachments(task.uuid), + caldavDao.getTasks(taskId))) + } + val data: MutableMap = HashMap() + data["version"] = BuildConfig.VERSION_CODE + data["timestamp"] = System.currentTimeMillis() + data["data"] = BackupContainer( + taskBackups, + locationDao.getPlaces(), + tagDataDao.getAll(), + filterDao.getAll(), + googleTaskListDao.getAccounts(), + googleTaskListDao.getAllLists(), + caldavDao.getAccounts(), + caldavDao.getCalendars(), + preferences.getPrefs(Int::class.java), + preferences.getPrefs(Long::class.java), + preferences.getPrefs(String::class.java), + preferences.getPrefs(Boolean::class.java)) + val out = OutputStreamWriter(os, UTF_8) + val gson = if (BuildConfig.DEBUG) GsonBuilder().setPrettyPrinting().create() else Gson() + out.write(gson.toJson(data)) + out.close() + exportCount = taskBackups.size + } + + private fun onFinishExport(outputFile: String) = post { + if (exportCount == 0) { + Toast.makeText( + context, context!!.getString(R.string.export_toast_no_tasks), Toast.LENGTH_LONG) + .show() + } else { + val text: CharSequence = String.format( + context!!.getString(R.string.export_toast), + context!! + .resources + .getQuantityString(R.plurals.Ntasks, exportCount, exportCount), + outputFile) + Toast.makeText(context, text, Toast.LENGTH_LONG).show() + } + } + + + private fun getFileName(type: ExportType): String { + return when (type) { + ExportType.EXPORT_TYPE_SERVICE -> String.format(BackupConstants.BACKUP_FILE_NAME, dateForExport) + ExportType.EXPORT_TYPE_MANUAL -> String.format(BackupConstants.EXPORT_FILE_NAME, dateForExport) + } + } + + enum class ExportType { + EXPORT_TYPE_SERVICE, EXPORT_TYPE_MANUAL + } + + companion object { + val UTF_8: Charset = Charset.forName("UTF-8") + private const val MIME = "application/json" + private const val EXTENSION = ".json" + private val dateForExport: String + get() = newDateTime().toString("yyMMdd-HHmm") + } +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/backup/TasksJsonImporter.java b/app/src/main/java/org/tasks/backup/TasksJsonImporter.java deleted file mode 100644 index defd61ec7..000000000 --- a/app/src/main/java/org/tasks/backup/TasksJsonImporter.java +++ /dev/null @@ -1,300 +0,0 @@ -package org.tasks.backup; - -import static org.tasks.backup.TasksJsonExporter.UTF_8; -import static org.tasks.data.Place.newPlace; -import static org.tasks.preferences.Preferences.P_CURRENT_VERSION; - -import android.app.ProgressDialog; -import android.content.Context; -import android.net.Uri; -import android.os.Handler; -import androidx.annotation.Nullable; -import com.google.gson.Gson; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.todoroo.astrid.dao.TaskDao; -import com.todoroo.astrid.data.Task; -import com.todoroo.astrid.service.TaskMover; -import com.todoroo.astrid.service.Upgrader; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.Map.Entry; -import javax.inject.Inject; -import org.tasks.LocalBroadcastManager; -import org.tasks.R; -import org.tasks.data.Alarm; -import org.tasks.data.AlarmDao; -import org.tasks.data.CaldavAccount; -import org.tasks.data.CaldavCalendar; -import org.tasks.data.CaldavDao; -import org.tasks.data.CaldavTask; -import org.tasks.data.Filter; -import org.tasks.data.FilterDao; -import org.tasks.data.Geofence; -import org.tasks.data.GoogleTask; -import org.tasks.data.GoogleTaskAccount; -import org.tasks.data.GoogleTaskDao; -import org.tasks.data.GoogleTaskList; -import org.tasks.data.GoogleTaskListDao; -import org.tasks.data.LocationDao; -import org.tasks.data.Place; -import org.tasks.data.Tag; -import org.tasks.data.TagDao; -import org.tasks.data.TagData; -import org.tasks.data.TagDataDao; -import org.tasks.data.TaskAttachment; -import org.tasks.data.TaskAttachmentDao; -import org.tasks.data.UserActivity; -import org.tasks.data.UserActivityDao; -import org.tasks.preferences.Preferences; -import timber.log.Timber; - -public class TasksJsonImporter { - - private final TagDataDao tagDataDao; - private final UserActivityDao userActivityDao; - private final TaskDao taskDao; - private final LocalBroadcastManager localBroadcastManager; - private final AlarmDao alarmDao; - private final TagDao tagDao; - private final GoogleTaskDao googleTaskDao; - private final GoogleTaskListDao googleTaskListDao; - private final FilterDao filterDao; - private final TaskAttachmentDao taskAttachmentDao; - private final CaldavDao caldavDao; - private final Preferences preferences; - private final TaskMover taskMover; - private final LocationDao locationDao; - - private final ImportResult result = new ImportResult(); - - @Inject - public TasksJsonImporter( - TagDataDao tagDataDao, - UserActivityDao userActivityDao, - TaskDao taskDao, - LocationDao locationDao, - LocalBroadcastManager localBroadcastManager, - AlarmDao alarmDao, - TagDao tagDao, - GoogleTaskDao googleTaskDao, - GoogleTaskListDao googleTaskListDao, - FilterDao filterDao, - TaskAttachmentDao taskAttachmentDao, - CaldavDao caldavDao, - Preferences preferences, - TaskMover taskMover) { - this.tagDataDao = tagDataDao; - this.userActivityDao = userActivityDao; - this.taskDao = taskDao; - this.locationDao = locationDao; - this.localBroadcastManager = localBroadcastManager; - this.alarmDao = alarmDao; - this.tagDao = tagDao; - this.googleTaskDao = googleTaskDao; - this.googleTaskListDao = googleTaskListDao; - this.filterDao = filterDao; - this.taskAttachmentDao = taskAttachmentDao; - this.caldavDao = caldavDao; - this.preferences = preferences; - this.taskMover = taskMover; - } - - private void setProgressMessage( - Handler handler, ProgressDialog progressDialog, final String message) { - if (progressDialog == null) { - return; - } - - handler.post(() -> progressDialog.setMessage(message)); - } - - public ImportResult importTasks(Context context, Uri backupFile, @Nullable ProgressDialog progressDialog) { - Handler handler = new Handler(context.getMainLooper()); - Gson gson = new Gson(); - InputStream is; - try { - is = context.getContentResolver().openInputStream(backupFile); - } catch (FileNotFoundException e) { - throw new IllegalStateException(e); - } - InputStreamReader reader = new InputStreamReader(is, UTF_8); - JsonObject input = gson.fromJson(reader, JsonObject.class); - - try { - JsonElement data = input.get("data"); - int version = input.get("version").getAsInt(); - BackupContainer backupContainer = gson.fromJson(data, BackupContainer.class); - for (TagData tagData : backupContainer.getTags()) { - tagData.setColor(themeToColor(context, version, tagData.getColor())); - if (tagDataDao.getByUuid(tagData.getRemoteId()) == null) { - tagDataDao.createNew(tagData); - } - } - for (GoogleTaskAccount googleTaskAccount : backupContainer.getGoogleTaskAccounts()) { - if (googleTaskListDao.getAccount(googleTaskAccount.getAccount()) == null) { - googleTaskListDao.insert(googleTaskAccount); - } - } - for (Place place : backupContainer.getPlaces()) { - if (locationDao.getByUid(place.getUid()) == null) { - locationDao.insert(place); - } - } - for (GoogleTaskList googleTaskList : backupContainer.getGoogleTaskLists()) { - googleTaskList.setColor(themeToColor(context, version, googleTaskList.getColor())); - if (googleTaskListDao.getByRemoteId(googleTaskList.getRemoteId()) == null) { - googleTaskListDao.insert(googleTaskList); - } - } - for (Filter filter : backupContainer.getFilters()) { - filter.setColor(themeToColor(context, version, filter.getColor())); - if (filterDao.getByName(filter.getTitle()) == null) { - filterDao.insert(filter); - } - } - for (CaldavAccount account : backupContainer.getCaldavAccounts()) { - if (caldavDao.getAccountByUuid(account.getUuid()) == null) { - caldavDao.insert(account); - } - } - for (CaldavCalendar calendar : backupContainer.getCaldavCalendars()) { - calendar.setColor(themeToColor(context, version, calendar.getColor())); - if (caldavDao.getCalendarByUuid(calendar.getUuid()) == null) { - caldavDao.insert(calendar); - } - } - for (BackupContainer.TaskBackup backup : backupContainer.getTasks()) { - result.taskCount++; - setProgressMessage( - handler, - progressDialog, - context.getString(R.string.import_progress_read, result.taskCount)); - Task task = backup.task; - if (taskDao.fetch(task.getUuid()) != null) { - result.skipCount++; - continue; - } - task.suppressRefresh(); - task.suppressSync(); - taskDao.createNew(task); - long taskId = task.getId(); - String taskUuid = task.getUuid(); - for (Alarm alarm : backup.alarms) { - alarm.setTask(taskId); - alarmDao.insert(alarm); - } - for (UserActivity comment : backup.comments) { - comment.setTargetId(taskUuid); - if (version < 546) { - comment.convertPictureUri(); - } - userActivityDao.createNew(comment); - } - for (GoogleTask googleTask : backup.google) { - googleTask.setTask(taskId); - googleTaskDao.insert(googleTask); - } - for (LegacyLocation location : backup.locations) { - Place place = newPlace(); - place.setLongitude(location.longitude); - place.setLatitude(location.latitude); - place.setName(location.name); - place.setAddress(location.address); - place.setUrl(location.url); - place.setPhone(location.phone); - locationDao.insert(place); - Geofence geofence = new Geofence(); - geofence.setTask(taskId); - geofence.setPlace(place.getUid()); - geofence.setRadius(location.radius); - geofence.setArrival(location.arrival); - geofence.setDeparture(location.departure); - locationDao.insert(geofence); - } - for (Tag tag : backup.tags) { - tag.setTask(taskId); - tag.setTaskUid(taskUuid); - tagDao.insert(tag); - } - for (Geofence geofence : backup.getGeofences()) { - geofence.setTask(taskId); - locationDao.insert(geofence); - } - for (TaskAttachment attachment : backup.getAttachments()) { - attachment.setTaskId(taskUuid); - if (version < 546) { - attachment.convertPathUri(); - } - taskAttachmentDao.insert(attachment); - } - for (CaldavTask caldavTask : backup.getCaldavTasks()) { - caldavTask.setTask(taskId); - caldavDao.insert(caldavTask); - } - result.importCount++; - } - - googleTaskDao.updateParents(); - caldavDao.updateParents(); - - for (Entry entry : backupContainer.getIntPrefs().entrySet()) { - if (P_CURRENT_VERSION.equals(entry.getKey())) { - continue; - } - preferences.setInt(entry.getKey(), entry.getValue()); - } - for (Entry entry : backupContainer.getLongPrefs().entrySet()) { - preferences.setLong(entry.getKey(), entry.getValue()); - } - for (Entry entry : backupContainer.getStringPrefs().entrySet()) { - preferences.setString(entry.getKey(), entry.getValue()); - } - for (Entry entry : backupContainer.getBoolPrefs().entrySet()) { - preferences.setBoolean(entry.getKey(), entry.getValue()); - } - - if (version < Upgrader.V8_2) { - int themeIndex = preferences.getInt(R.string.p_theme_color, 7); - preferences.setInt( - R.string.p_theme_color, - Upgrader.getAndroidColor(context, themeIndex)); - } - if (version < Upgrader.V9_6) { - taskMover.migrateLocalTasks(); - } - - reader.close(); - is.close(); - } catch (IOException e) { - Timber.e(e); - } - - localBroadcastManager.broadcastRefresh(); - return result; - } - - private int themeToColor(Context context, int version, int color) { - return version < Upgrader.V8_2 ? Upgrader.getAndroidColor(context, color) : color; - } - - public static class ImportResult { - public int taskCount; - public int importCount; - public int skipCount; - } - - static class LegacyLocation { - String name; - String address; - String phone; - String url; - double latitude; - double longitude; - int radius; - boolean arrival; - boolean departure; - } -} diff --git a/app/src/main/java/org/tasks/backup/TasksJsonImporter.kt b/app/src/main/java/org/tasks/backup/TasksJsonImporter.kt new file mode 100644 index 000000000..a52bd4208 --- /dev/null +++ b/app/src/main/java/org/tasks/backup/TasksJsonImporter.kt @@ -0,0 +1,230 @@ +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.service.TaskMover +import com.todoroo.astrid.service.Upgrader +import com.todoroo.astrid.service.Upgrader.Companion.getAndroidColor +import org.tasks.LocalBroadcastManager +import org.tasks.R +import org.tasks.data.* +import org.tasks.data.Place.Companion.newPlace +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 googleTaskDao: GoogleTaskDao, + private val googleTaskListDao: GoogleTaskListDao, + private val filterDao: FilterDao, + private val taskAttachmentDao: TaskAttachmentDao, + private val caldavDao: CaldavDao, + private val preferences: Preferences, + private val taskMover: TaskMover) { + + private val result = ImportResult() + + private fun setProgressMessage( + handler: Handler, progressDialog: ProgressDialog?, message: String) { + if (progressDialog == null) { + return + } + handler.post { progressDialog.setMessage(message) } + } + + fun importTasks(context: Context, backupFile: Uri?, progressDialog: ProgressDialog?): ImportResult { + val handler = Handler(context.mainLooper) + val gson = Gson() + val `is`: InputStream? + `is` = 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 -> + tagData.setColor(themeToColor(context, version, tagData.getColor()!!)) + if (tagDataDao.getByUuid(tagData.remoteId!!) == null) { + tagDataDao.createNew(tagData) + } + } + backupContainer.googleTaskAccounts?.forEach { googleTaskAccount -> + if (googleTaskListDao.getAccount(googleTaskAccount.account!!) == null) { + googleTaskListDao.insert(googleTaskAccount) + } + } + backupContainer.places?.forEach { place -> + if (locationDao.getByUid(place.uid!!) == null) { + locationDao.insert(place) + } + } + backupContainer.googleTaskLists?.forEach { googleTaskList -> + googleTaskList.setColor(themeToColor(context, version, googleTaskList.getColor()!!)) + if (googleTaskListDao.getByRemoteId(googleTaskList.remoteId!!) == null) { + googleTaskListDao.insert(googleTaskList) + } + } + 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 -> + calendar.color = themeToColor(context, version, calendar.color) + if (caldavDao.getCalendarByUuid(calendar.uuid!!) == null) { + caldavDao.insert(calendar) + } + } + backupContainer.tasks?.forEach { backup -> + result.taskCount++ + setProgressMessage( + handler, + progressDialog, + context.getString(R.string.import_progress_read, result.taskCount)) + val task = backup.task + if (taskDao.fetch(task.uuid) != 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) + } + for (comment in backup.comments) { + comment.targetId = taskUuid + if (version < 546) { + comment.convertPictureUri() + } + userActivityDao.createNew(comment) + } + for (googleTask in backup.google) { + googleTask.task = taskId + googleTaskDao.insert(googleTask) + } + 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.radius = location.radius + geofence.isArrival = location.arrival + geofence.isDeparture = location.departure + locationDao.insert(geofence) + } + for (tag in backup.tags) { + tag.task = taskId + tag.setTaskUid(taskUuid) + tagDao.insert(tag) + } + backup.geofences?.forEach { geofence -> + geofence.task = taskId + locationDao.insert(geofence) + } + backup.attachments?.forEach { attachment -> + attachment.taskId = taskUuid + if (version < 546) { + attachment.convertPathUri() + } + taskAttachmentDao.insert(attachment) + } + backup.caldavTasks?.forEach { caldavTask -> + caldavTask.task = taskId + caldavDao.insert(caldavTask) + } + result.importCount++ + } + googleTaskDao.updateParents() + caldavDao.updateParents() + backupContainer.intPrefs?.forEach { (key, value) -> + if (Preferences.P_CURRENT_VERSION == key) { + return@forEach + } + preferences.setInt(key, value) + } + backupContainer.longPrefs?.forEach { (key, value) -> + preferences.setLong(key, value) + } + backupContainer.stringPrefs?.forEach { (key, value) -> + preferences.setString(key, value) + } + backupContainer.boolPrefs?.forEach { (key, value) -> + preferences.setBoolean(key, value) + } + 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 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 + } +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/dialogs/ImportTasksDialog.java b/app/src/main/java/org/tasks/dialogs/ImportTasksDialog.java index 2d89bd789..1e5fcc8a2 100644 --- a/app/src/main/java/org/tasks/dialogs/ImportTasksDialog.java +++ b/app/src/main/java/org/tasks/dialogs/ImportTasksDialog.java @@ -82,9 +82,12 @@ public class ImportTasksDialog extends DialogFragment { context.getString( R.string.import_summary_message, "", - r.getQuantityString(R.plurals.Ntasks, result.taskCount, result.taskCount), - r.getQuantityString(R.plurals.Ntasks, result.importCount, result.importCount), - r.getQuantityString(R.plurals.Ntasks, result.skipCount, result.skipCount), + r.getQuantityString(R.plurals.Ntasks, result.getTaskCount(), result.getTaskCount()), + r.getQuantityString( + R.plurals.Ntasks, + result.getImportCount(), + result.getImportCount()), + r.getQuantityString(R.plurals.Ntasks, result.getSkipCount(), result.getSkipCount()), r.getQuantityString(R.plurals.Ntasks, 0, 0))) .setPositiveButton(android.R.string.ok, (dialog, id) -> dialog.dismiss()) .show();