From e8bbae8ef91b9dc6a22986f3d9055ea2dc424764 Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Wed, 23 Mar 2016 18:01:25 -0400 Subject: [PATCH] Allow user to export a full copy of the database --- .../org/isoron/uhabits/HabitsApplication.java | 12 +-- .../java/org/isoron/uhabits/MainActivity.java | 9 +- .../uhabits/fragments/ListHabitsFragment.java | 6 ++ .../uhabits/fragments/SettingsFragment.java | 3 +- .../uhabits/helpers/DatabaseHelper.java | 50 ++++++++++ .../isoron/uhabits/io/HabitsCSVExporter.java | 2 +- .../isoron/uhabits/tasks/ExportDBTask.java | 94 +++++++++++++++++++ app/src/main/res/values/strings.xml | 7 +- app/src/main/res/xml/preferences.xml | 1 + 9 files changed, 167 insertions(+), 17 deletions(-) create mode 100644 app/src/main/java/org/isoron/uhabits/tasks/ExportDBTask.java diff --git a/app/src/main/java/org/isoron/uhabits/HabitsApplication.java b/app/src/main/java/org/isoron/uhabits/HabitsApplication.java index a7573d946..4e4ff918e 100644 --- a/app/src/main/java/org/isoron/uhabits/HabitsApplication.java +++ b/app/src/main/java/org/isoron/uhabits/HabitsApplication.java @@ -24,7 +24,7 @@ import android.app.Application; import com.activeandroid.ActiveAndroid; import com.activeandroid.Configuration; -import java.io.File; +import org.isoron.uhabits.helpers.DatabaseHelper; public class HabitsApplication extends Application { @@ -41,14 +41,6 @@ public class HabitsApplication extends Application } } - private void deleteDB(String databaseFilename) - { - File databaseFile = new File(String.format("%s/../databases/%s", - getApplicationContext().getFilesDir().getPath(), databaseFilename)); - - if(databaseFile.exists()) databaseFile.delete(); - } - @Override public void onCreate() { @@ -58,7 +50,7 @@ public class HabitsApplication extends Application if (isTestMode()) { databaseFilename = "test.db"; - deleteDB(databaseFilename); + DatabaseHelper.deleteDatabase(this, databaseFilename); } Configuration dbConfig = new Configuration.Builder(this) diff --git a/app/src/main/java/org/isoron/uhabits/MainActivity.java b/app/src/main/java/org/isoron/uhabits/MainActivity.java index 95b7477b6..d058890be 100644 --- a/app/src/main/java/org/isoron/uhabits/MainActivity.java +++ b/app/src/main/java/org/isoron/uhabits/MainActivity.java @@ -61,7 +61,8 @@ public class MainActivity extends ReplayableActivity public static final String ACTION_REFRESH = "org.isoron.uhabits.ACTION_REFRESH"; public static final int RESULT_IMPORT_DATA = 1; - public static final int RESULT_EXPORT_ALL_AS_CSV = 2; + public static final int RESULT_EXPORT_CSV = 2; + public static final int RESULT_EXPORT_DB = 3; @Override protected void onCreate(Bundle savedInstanceState) @@ -155,9 +156,13 @@ public class MainActivity extends ReplayableActivity onActionImportClicked(); break; - case RESULT_EXPORT_ALL_AS_CSV: + case RESULT_EXPORT_CSV: listHabitsFragment.exportAllHabits(); break; + + case RESULT_EXPORT_DB: + listHabitsFragment.exportDB(); + break; } } diff --git a/app/src/main/java/org/isoron/uhabits/fragments/ListHabitsFragment.java b/app/src/main/java/org/isoron/uhabits/fragments/ListHabitsFragment.java index c5696b024..c49665d67 100644 --- a/app/src/main/java/org/isoron/uhabits/fragments/ListHabitsFragment.java +++ b/app/src/main/java/org/isoron/uhabits/fragments/ListHabitsFragment.java @@ -63,6 +63,7 @@ import org.isoron.uhabits.helpers.ReminderHelper; import org.isoron.uhabits.loaders.HabitListLoader; import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.tasks.ExportCSVTask; +import org.isoron.uhabits.tasks.ExportDBTask; import org.isoron.uhabits.tasks.ImportDataTask; import java.io.File; @@ -472,4 +473,9 @@ public class ListHabitsFragment extends Fragment { new ExportCSVTask(activity, Habit.getAll(true), progressBar).execute(); } + + public void exportDB() + { + new ExportDBTask(activity, progressBar).execute(); + } } diff --git a/app/src/main/java/org/isoron/uhabits/fragments/SettingsFragment.java b/app/src/main/java/org/isoron/uhabits/fragments/SettingsFragment.java index 20b5ce28d..1d3c4abb3 100644 --- a/app/src/main/java/org/isoron/uhabits/fragments/SettingsFragment.java +++ b/app/src/main/java/org/isoron/uhabits/fragments/SettingsFragment.java @@ -38,7 +38,8 @@ public class SettingsFragment extends PreferenceFragment addPreferencesFromResource(R.xml.preferences); setResultOnPreferenceClick("importData", MainActivity.RESULT_IMPORT_DATA); - setResultOnPreferenceClick("exportCSV", MainActivity.RESULT_EXPORT_ALL_AS_CSV); + setResultOnPreferenceClick("exportCSV", MainActivity.RESULT_EXPORT_CSV); + setResultOnPreferenceClick("exportDB", MainActivity.RESULT_EXPORT_DB); } private void setResultOnPreferenceClick(String key, final int result) diff --git a/app/src/main/java/org/isoron/uhabits/helpers/DatabaseHelper.java b/app/src/main/java/org/isoron/uhabits/helpers/DatabaseHelper.java index 2891eeb5d..804eeb2db 100644 --- a/app/src/main/java/org/isoron/uhabits/helpers/DatabaseHelper.java +++ b/app/src/main/java/org/isoron/uhabits/helpers/DatabaseHelper.java @@ -1,9 +1,32 @@ package org.isoron.uhabits.helpers; +import android.content.Context; +import android.support.annotation.NonNull; + import com.activeandroid.ActiveAndroid; +import org.isoron.uhabits.BuildConfig; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.text.SimpleDateFormat; + public class DatabaseHelper { + public static void copy(File src, File dst) throws IOException + { + FileInputStream inStream = new FileInputStream(src); + FileOutputStream outStream = new FileOutputStream(dst); + FileChannel inChannel = inStream.getChannel(); + FileChannel outChannel = outStream.getChannel(); + inChannel.transferTo(0, inChannel.size(), outChannel); + inStream.close(); + outStream.close(); + } + public interface Command { void execute(); @@ -22,4 +45,31 @@ public class DatabaseHelper ActiveAndroid.endTransaction(); } } + + @SuppressWarnings("ResultOfMethodCallIgnored") + public static String saveDatabaseCopy(Context context, File dir) throws IOException + { + File db = getDatabaseFile(context, BuildConfig.databaseFilename); + + SimpleDateFormat dateFormat = DateHelper.getCSVDateFormat(); + String date = dateFormat.format(DateHelper.getStartOfToday()); + File dbCopy = new File(String.format("%s/Loop-Habits-Backup-%s.db", dir.getAbsolutePath(), date)); + + copy(db, dbCopy); + + return dbCopy.getAbsolutePath(); + } + + public static void deleteDatabase(Context context, String databaseFilename) + { + File db = getDatabaseFile(context, databaseFilename); + if(db.exists()) db.delete(); + } + + @NonNull + private static File getDatabaseFile(Context context, String databaseFilename) + { + return new File(String.format("%s/../databases/%s", + context.getApplicationContext().getFilesDir().getPath(), databaseFilename)); + } } diff --git a/app/src/main/java/org/isoron/uhabits/io/HabitsCSVExporter.java b/app/src/main/java/org/isoron/uhabits/io/HabitsCSVExporter.java index 0ac07ec4a..ef23e997b 100644 --- a/app/src/main/java/org/isoron/uhabits/io/HabitsCSVExporter.java +++ b/app/src/main/java/org/isoron/uhabits/io/HabitsCSVExporter.java @@ -98,7 +98,7 @@ public class HabitsCSVExporter { SimpleDateFormat dateFormat = DateHelper.getCSVDateFormat(); String date = dateFormat.format(DateHelper.getStartOfToday()); - String zipFilename = String.format("%s/Loop-Habits-%s.zip", exportDirName, date); + String zipFilename = String.format("%s/Loop-Habits-CSV-%s.zip", exportDirName, date); FileOutputStream fos = new FileOutputStream(zipFilename); ZipOutputStream zos = new ZipOutputStream(fos); diff --git a/app/src/main/java/org/isoron/uhabits/tasks/ExportDBTask.java b/app/src/main/java/org/isoron/uhabits/tasks/ExportDBTask.java new file mode 100644 index 000000000..c8ae50681 --- /dev/null +++ b/app/src/main/java/org/isoron/uhabits/tasks/ExportDBTask.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +package org.isoron.uhabits.tasks; + +import android.content.Intent; +import android.net.Uri; +import android.os.AsyncTask; +import android.view.View; +import android.widget.ProgressBar; + +import org.isoron.uhabits.R; +import org.isoron.uhabits.ReplayableActivity; +import org.isoron.uhabits.helpers.DatabaseHelper; + +import java.io.File; +import java.io.IOException; + +public class ExportDBTask extends AsyncTask +{ + private final ReplayableActivity activity; + private ProgressBar progressBar; + private String filename; + + public ExportDBTask(ReplayableActivity activity, ProgressBar progressBar) + { + this.progressBar = progressBar; + this.activity = activity; + } + + @Override + protected void onPreExecute() + { + if(progressBar != null) + { + progressBar.setIndeterminate(true); + progressBar.setVisibility(View.VISIBLE); + } + } + + @Override + protected void onPostExecute(Void aVoid) + { + if(filename != null) + { + Intent intent = new Intent(); + intent.setAction(Intent.ACTION_SEND); + intent.setType("application/octet-stream"); + intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(filename))); + + activity.startActivity(intent); + } + else + { + activity.showToast(R.string.could_not_export); + } + + if(progressBar != null) + progressBar.setVisibility(View.GONE); + } + + @Override + protected Void doInBackground(Void... params) + { + filename = null; + + try + { + filename = DatabaseHelper.saveDatabaseCopy(activity, activity.getExternalCacheDir()); + } + catch(IOException e) + { + e.printStackTrace(); + } + + return null; + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c314b3d37..43a91f004 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -142,9 +142,10 @@ Failed to import habits from file. File type not recognized. Habits imported successfully. - Supports files exported by Loop, Tickmate, HabitBull or Rewire. See FAQ for more information. + Supports full backups exported by this app, as well as files generated by Tickmate, HabitBull or Rewire. See FAQ for more information. Import data - This archive contains files that can be opened by spreadsheet software such as Microsoft Excel or OpenOffice Calc, but cannot be imported back. + Generates files that can be opened by spreadsheet software such as Microsoft Excel or OpenOffice Calc, but cannot be imported back. Export full backup - This file contains all your data and can be imported back. + Generates a file that contains all your data, and that can be imported back. + Full backup successfully exported. \ No newline at end of file diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index b1bbf36f6..8999ef51b 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -46,6 +46,7 @@ android:title="Database">