diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 078121a..2101dcd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -8,6 +8,7 @@
+
target) {
- loadHeadersFromResource(R.xml.pref_headers, target);
- }*/
-
- /**
- * Binds a preference's summary to its value. More specifically, when the
- * preference's value is changed, its summary (line of text below the
- * preference title) is updated to reflect the value. The summary is also
- * immediately updated upon calling this method. The exact display format is
- * dependent on the type of preference.
- *
- * @see #sBindPreferenceSummaryToValueListener
- */
private static void bindPreferenceSummaryToValue(Preference preference) {
// Set the listener to watch for value changes.
preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);
@@ -119,6 +148,8 @@ public class SettingsActivity extends PreferenceActivity {
.getString(preference.getKey(), ""));
}
+ private Handler mHandler= new Handler();
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -131,9 +162,117 @@ public class SettingsActivity extends PreferenceActivity {
bindPreferenceSummaryToValue(findPreference("unitSystem"));
findPreference("weight").setOnPreferenceClickListener(preference -> showWeightPicker());
+ findPreference("import").setOnPreferenceClickListener(preference -> showImportDialog());
+ findPreference("export").setOnPreferenceClickListener(preference -> showExportDialog());
}
+ private boolean showExportDialog(){
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.exportData)
+ .setMessage(R.string.exportDataSummary)
+ .setNegativeButton(R.string.cancel, null)
+ .setPositiveButton(R.string.backup, (dialog, which) -> {
+ exportBackup();
+ }).create().show();
+ return true;
+ }
+
+ private void exportBackup(){
+ ProgressDialogController dialogController= new ProgressDialogController(this, getString(R.string.backup));
+ dialogController.show();
+ new Thread(() -> {
+ try{
+ String file= getFilesDir().getAbsolutePath() + "/shared/backup.ftb";
+ new File(file).getParentFile().mkdirs();
+ Uri uri= FileProvider.getUriForFile(getBaseContext(), "de.tadris.fitness.fileprovider", new File(file));
+
+ Exporter.exportData(getBaseContext(), new File(file),
+ (progress, action) -> mHandler.post(() -> dialogController.setProgress(progress, action)));
+
+ mHandler.post(() -> {
+ dialogController.cancel();
+ shareFile(uri);
+ });
+ }catch (Exception e){
+ e.printStackTrace();
+ mHandler.post(() -> {
+ dialogController.cancel();
+ showErrorDialog(e, R.string.error, R.string.errorExportFailed);
+ });
+ }
+ }).start();
+ }
+
+ private boolean showImportDialog(){
+ if(!hasPermission()){
+ requestPermissions();
+ return true;
+ }
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.importBackup)
+ .setMessage(R.string.importBackupMessage)
+ .setNegativeButton(R.string.cancel, null)
+ .setPositiveButton(R.string.restore, (dialog, which) -> {
+ importBackup();
+ }).create().show();
+ return true;
+ }
+
+ void requestPermissions(){
+ if (!hasPermission()) {
+ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 10);
+ }
+ }
+
+ public boolean hasPermission(){
+ return ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private static final int FILE_SELECT_CODE= 21;
+ private void importBackup(){
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.setType("*/*");
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ try {
+ startActivityForResult(Intent.createChooser(intent, getString(R.string.chooseBackupFile)), FILE_SELECT_CODE);
+ } catch (android.content.ActivityNotFoundException ignored) { }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case FILE_SELECT_CODE:
+ if (resultCode == RESULT_OK){
+ importBackup(data.getData());
+ }
+ break;
+ }
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+
+ private void importBackup(Uri uri){
+ ProgressDialogController dialogController= new ProgressDialogController(this, getString(R.string.backup));
+ dialogController.show();
+ new Thread(() -> {
+ try{
+ Exporter.importData(getBaseContext(), uri,
+ (progress, action) -> mHandler.post(() -> dialogController.setProgress(progress, action)));
+
+ mHandler.post(() -> {
+ // DO on backup finished
+ dialogController.cancel();
+ });
+ }catch (Exception e){
+ e.printStackTrace();
+ mHandler.post(() -> {
+ dialogController.cancel();
+ showErrorDialog(e, R.string.error, R.string.errorImportFailed);
+ });
+ }
+ }).start();
+ }
+
private boolean showWeightPicker() {
final AlertDialog.Builder d = new AlertDialog.Builder(this);
final SharedPreferences preferences= PreferenceManager.getDefaultSharedPreferences(this);
diff --git a/app/src/main/java/de/tadris/fitness/activity/ShowWorkoutActivity.java b/app/src/main/java/de/tadris/fitness/activity/ShowWorkoutActivity.java
index 3bc8b4a..b9f464e 100644
--- a/app/src/main/java/de/tadris/fitness/activity/ShowWorkoutActivity.java
+++ b/app/src/main/java/de/tadris/fitness/activity/ShowWorkoutActivity.java
@@ -39,6 +39,7 @@ import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.TextView;
+import androidx.annotation.StringRes;
import androidx.core.app.ActivityCompat;
import androidx.core.content.FileProvider;
@@ -347,46 +348,12 @@ public class ShowWorkoutActivity extends FitoTrackActivity {
dialogController.cancel();
mHandler.post(() -> shareFile(uri));
}catch (Exception e){
- mHandler.post(() -> showErrorDialog(e));
+ mHandler.post(() -> showErrorDialog(e, R.string.error, R.string.errorGpxExportFailed));
}
}).start();
}
- private void shareFile(Uri uri){
- Intent intentShareFile = new Intent(Intent.ACTION_SEND);
- intentShareFile.setDataAndType(uri, getContentResolver().getType(uri));
- intentShareFile.putExtra(Intent.EXTRA_STREAM, uri);
- intentShareFile.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- startActivity(Intent.createChooser(intentShareFile, getString(R.string.shareFile)));
-
- Log.d("Export", uri.toString());
- Log.d("Export", getContentResolver().getType(uri));
- try {
- Log.d("Export", new BufferedInputStream(getContentResolver().openInputStream(uri)).toString());
- } catch (FileNotFoundException e) {
-
- }
- }
-
- /*void requestPermissions(){
- if (!hasPermission()) {
- ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 10);
- }
- }
-
- public boolean hasPermission(){
- return ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
- }*/
-
-
- private void showErrorDialog(Exception e){
- new AlertDialog.Builder(this)
- .setTitle(R.string.error)
- .setMessage(getString(R.string.errorGpxExportFailed) + "\n\n" + e.getMessage())
- .setPositiveButton(R.string.okay, null)
- .create().show();
- }
@Override
public boolean onOptionsItemSelected(MenuItem item) {
diff --git a/app/src/main/java/de/tadris/fitness/data/WorkoutDao.java b/app/src/main/java/de/tadris/fitness/data/WorkoutDao.java
index eeea868..ba84deb 100644
--- a/app/src/main/java/de/tadris/fitness/data/WorkoutDao.java
+++ b/app/src/main/java/de/tadris/fitness/data/WorkoutDao.java
@@ -46,5 +46,8 @@ public interface WorkoutDao {
@Update
void updateWorkout(Workout workout);
+ @Update
+ void insertSample(WorkoutSample sample);
+
}
diff --git a/app/src/main/java/de/tadris/fitness/util/export/Exporter.java b/app/src/main/java/de/tadris/fitness/util/export/Exporter.java
new file mode 100644
index 0000000..d7c6df8
--- /dev/null
+++ b/app/src/main/java/de/tadris/fitness/util/export/Exporter.java
@@ -0,0 +1,106 @@
+package de.tadris.fitness.util.export;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.net.Uri;
+import android.preference.PreferenceManager;
+
+import com.fasterxml.jackson.dataformat.xml.XmlMapper;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import de.tadris.fitness.Instance;
+import de.tadris.fitness.R;
+import de.tadris.fitness.data.AppDatabase;
+import de.tadris.fitness.data.UserPreferences;
+import de.tadris.fitness.data.Workout;
+import de.tadris.fitness.data.WorkoutSample;
+import de.tadris.fitness.util.unit.UnitUtils;
+
+public class Exporter {
+
+ public static final int VERSION= 1;
+
+ public static void exportData(Context context, File output, ExportStatusListener listener) throws IOException {
+ listener.onStatusChanged(0, context.getString(R.string.initialising));
+ UserPreferences preferences= Instance.getInstance(context).userPreferences;
+ AppDatabase database= Instance.getInstance(context).db;
+ UnitUtils.setUnit(context);
+
+ FitoTrackDataContainer container= new FitoTrackDataContainer();
+ container.version= VERSION;
+ container.workouts= new ArrayList<>();
+ container.samples= new ArrayList<>();
+
+ listener.onStatusChanged(10, context.getString(R.string.preferences));
+ FitoTrackSettings settings= new FitoTrackSettings();
+ settings.weight= preferences.getUserWeight();
+ settings.preferredUnitSystem= String.valueOf(UnitUtils.CHOSEN_SYSTEM.getId());
+ container.settings= settings;
+
+ listener.onStatusChanged(20, context.getString(R.string.workouts));
+ container.workouts.addAll(Arrays.asList(database.workoutDao().getWorkouts()));
+ listener.onStatusChanged(40, context.getString(R.string.locationData));
+ container.workouts.addAll(Arrays.asList(database.workoutDao().getWorkouts()));
+
+ listener.onStatusChanged(60, context.getString(R.string.converting));
+
+ XmlMapper mapper= new XmlMapper();
+ mapper.writeValue(output, container);
+
+ listener.onStatusChanged(100, context.getString(R.string.finished));
+ }
+
+ @SuppressLint("ApplySharedPref")
+ public static void importData(Context context, Uri input, ExportStatusListener listener) throws IOException{
+ listener.onStatusChanged(0, context.getString(R.string.loadingFile));
+ XmlMapper xmlMapper = new XmlMapper();
+ FitoTrackDataContainer container = xmlMapper.readValue(context.getContentResolver().openInputStream(input), FitoTrackDataContainer.class);
+
+ if(container.version != 1){
+ throw new UnsupportedEncodingException("Version Code" + container.version + " is unsupported!");
+ }
+
+ listener.onStatusChanged(40, context.getString(R.string.preferences));
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit().clear()
+ .putInt("weight", container.settings.weight)
+ .putString("unitSystem", container.settings.preferredUnitSystem)
+ .commit();
+
+ AppDatabase database= Instance.getInstance(context).db;
+ database.clearAllTables();
+
+ listener.onStatusChanged(60, context.getString(R.string.workouts));
+ if(container.workouts != null){
+ for(Workout workout : container.workouts){
+ database.workoutDao().insertWorkout(workout);
+ }
+ }
+
+ listener.onStatusChanged(80, context.getString(R.string.locationData));
+ if(container.samples != null){
+ for(WorkoutSample sample : container.samples){
+ database.workoutDao().insertSample(sample);
+ }
+ }
+
+ listener.onStatusChanged(100, context.getString(R.string.finished));
+ }
+
+
+ public interface ExportStatusListener{
+ void onStatusChanged(int progress, String action);
+ }
+
+ public static class UnsupportedVersionException extends Exception{
+ public UnsupportedVersionException(String message) {
+ super(message);
+ }
+ }
+
+}
diff --git a/app/src/main/java/de/tadris/fitness/util/export/FitoTrackDataContainer.java b/app/src/main/java/de/tadris/fitness/util/export/FitoTrackDataContainer.java
new file mode 100644
index 0000000..2ad55bb
--- /dev/null
+++ b/app/src/main/java/de/tadris/fitness/util/export/FitoTrackDataContainer.java
@@ -0,0 +1,58 @@
+package de.tadris.fitness.util.export;
+
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
+
+import java.util.List;
+
+import de.tadris.fitness.data.Workout;
+import de.tadris.fitness.data.WorkoutSample;
+
+@JacksonXmlRootElement(localName = "fito-track")
+public class FitoTrackDataContainer {
+
+ int version;
+ List workouts;
+ List samples;
+ FitoTrackSettings settings;
+
+ public FitoTrackDataContainer(){}
+
+ public FitoTrackDataContainer(int version, List workouts, List samples, FitoTrackSettings settings) {
+ this.version = version;
+ this.workouts = workouts;
+ this.samples = samples;
+ this.settings = settings;
+ }
+
+ public int getVersion() {
+ return version;
+ }
+
+ public void setVersion(int version) {
+ this.version = version;
+ }
+
+ public List getWorkouts() {
+ return workouts;
+ }
+
+ public void setWorkouts(List workouts) {
+ this.workouts = workouts;
+ }
+
+ public List getSamples() {
+ return samples;
+ }
+
+ public void setSamples(List samples) {
+ this.samples = samples;
+ }
+
+ public FitoTrackSettings getSettings() {
+ return settings;
+ }
+
+ public void setSettings(FitoTrackSettings settings) {
+ this.settings = settings;
+ }
+}
diff --git a/app/src/main/java/de/tadris/fitness/util/export/FitoTrackSettings.java b/app/src/main/java/de/tadris/fitness/util/export/FitoTrackSettings.java
new file mode 100644
index 0000000..645b9af
--- /dev/null
+++ b/app/src/main/java/de/tadris/fitness/util/export/FitoTrackSettings.java
@@ -0,0 +1,30 @@
+package de.tadris.fitness.util.export;
+
+public class FitoTrackSettings {
+
+ String preferredUnitSystem;
+ int weight;
+
+ public FitoTrackSettings(){}
+
+ public FitoTrackSettings(String preferredUnitSystem, int weight) {
+ this.preferredUnitSystem = preferredUnitSystem;
+ this.weight = weight;
+ }
+
+ public String getPreferredUnitSystem() {
+ return preferredUnitSystem;
+ }
+
+ public void setPreferredUnitSystem(String preferredUnitSystem) {
+ this.preferredUnitSystem = preferredUnitSystem;
+ }
+
+ public int getWeight() {
+ return weight;
+ }
+
+ public void setWeight(int weight) {
+ this.weight = weight;
+ }
+}
diff --git a/app/src/main/java/de/tadris/fitness/view/ProgressDialogController.java b/app/src/main/java/de/tadris/fitness/view/ProgressDialogController.java
index e52906c..f418c3f 100644
--- a/app/src/main/java/de/tadris/fitness/view/ProgressDialogController.java
+++ b/app/src/main/java/de/tadris/fitness/view/ProgressDialogController.java
@@ -53,6 +53,6 @@ public class ProgressDialogController {
}
public void cancel(){
- dialog.dismiss();
+ dialog.cancel();
}
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index ee2a099..07b4de3 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -25,8 +25,22 @@
Exporting
Error
- The GPX export has been failed.
+ The GPX export has failed.
+ The data export has failed.
+ The data import has failed.
Share file
+ Initialising
+ Preferences
+ Workouts
+ Location data
+ Converting
+ Finished
+ Loading file
+ Choose Backup-File
+
+ WARNING: All your existing data in the app will be cleared. Are you sure, you want to restore this backup?
+ Restore
+ Backup
Silent
@@ -80,4 +94,8 @@
Your weight is needed to calculate the burned calories
Preferred system of units
Settings
+ Export Data
+ This takes a backup of all your preferences and workout data
+ Import Data Backup
+ Restore a taken backup
diff --git a/app/src/main/res/xml/preferences_main.xml b/app/src/main/res/xml/preferences_main.xml
index 87faae1..e8a0ec8 100644
--- a/app/src/main/res/xml/preferences_main.xml
+++ b/app/src/main/res/xml/preferences_main.xml
@@ -14,5 +14,14 @@
android:summary="@string/pref_weight_summary"
android:title="@string/pref_weight" />
+
+
+
\ No newline at end of file