diff --git a/MyTracks/AndroidManifest.xml b/MyTracks/AndroidManifest.xml
index 6a17ae991..07f38a5e2 100755
--- a/MyTracks/AndroidManifest.xml
+++ b/MyTracks/AndroidManifest.xml
@@ -274,7 +274,10 @@ limitations under the License.
-
+
+
+
+
- Restoring a backup from the SD card will overwrite all your current tracks and settings. Are you
- sure?
+ Restoring a backup from the SD card will overwrite all your current tracks and settings.
REVERSE_DATE_ORDER =
- new Comparator() {
- @Override
- public int compare(Date s1, Date s2) {
- return s2.compareTo(s1);
- }
- };
-
- private final FileUtils fileUtils;
- private final ExternalFileBackup backup;
- private final Activity activity;
-
- public BackupActivityHelper(Activity activity) {
- this.activity = activity;
- this.fileUtils = new FileUtils();
- this.backup = new ExternalFileBackup(activity, fileUtils);
- }
-
- /**
- * Writes a full backup to the default file.
- * This shows the results to the user.
- */
- public void writeBackup() {
- if (!fileUtils.isSdCardAvailable()) {
- showToast(R.string.sd_card_error_no_storage);
- return;
- }
-
- if (!backup.isBackupsDirectoryAvailable(true)) {
- showToast(R.string.sd_card_error_create_dir);
- return;
- }
-
- final ProgressDialog progressDialog = ProgressDialog.show(
- activity,
- activity.getString(R.string.generic_progress_title),
- activity.getString(R.string.settings_backup_now_progress_message),
- true);
-
- // Do the writing in another thread
- new Thread() {
- @Override
- public void run() {
- try {
- backup.writeToDefaultFile();
- showToast(R.string.sd_card_success_write_file);
- } catch (IOException e) {
- Log.e(Constants.TAG, "Failed to write backup", e);
- showToast(R.string.sd_card_error_write_file);
- } finally {
- dismissDialog(progressDialog);
- }
- }
- }.start();
- }
-
- /**
- * Restores a full backup from the SD card.
- * The user will be given a choice of which backup to restore as well as a
- * confirmation dialog.
- */
- public void restoreBackup() {
- // Get the list of existing backups
- if (!fileUtils.isSdCardAvailable()) {
- showToast(R.string.sd_card_error_no_storage);
- return;
- }
-
- if (!backup.isBackupsDirectoryAvailable(false)) {
- showToast(R.string.settings_backup_restore_no_backup);
- return;
- }
-
- final Date[] backupDates = backup.getAvailableBackups();
- if (backupDates == null || backupDates.length == 0) {
- showToast(R.string.settings_backup_restore_no_backup);
- return;
- }
- Arrays.sort(backupDates, REVERSE_DATE_ORDER);
-
- // Show a confirmation dialog
- Builder confirmationDialogBuilder = new AlertDialog.Builder(activity);
- confirmationDialogBuilder.setMessage(R.string.settings_backup_restore_confirm_message);
- confirmationDialogBuilder.setCancelable(true);
- confirmationDialogBuilder.setPositiveButton(android.R.string.yes,
- new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- pickBackupForRestore(backupDates);
- }
- });
- confirmationDialogBuilder.setNegativeButton(android.R.string.no, null);
- confirmationDialogBuilder.create().show();
- }
-
- /**
- * Shows a backup list for the user to pick, then restores it.
- *
- * @param backupDates the list of available backup files
- */
- private void pickBackupForRestore(final Date[] backupDates) {
- if (backupDates.length == 1) {
- // Only one choice, don't bother showing the list
- restoreFromDateAsync(backupDates[0]);
- return;
- }
-
- // Make a user-visible version of the backup filenames
- final String backupDateStrs[] = new String[backupDates.length];
- for (int i = 0; i < backupDates.length; i++) {
- backupDateStrs[i] = StringUtils.formatDateTime(activity, backupDates[i].getTime());
- }
-
- // Show a dialog for the user to pick which backup to restore
- Builder dialogBuilder = new AlertDialog.Builder(activity);
- dialogBuilder.setCancelable(true);
- dialogBuilder.setTitle(R.string.settings_backup_restore_select_title);
- dialogBuilder.setItems(backupDateStrs, new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- // User picked to restore this one
- restoreFromDateAsync(backupDates[which]);
- }
- });
- dialogBuilder.create().show();
- }
-
- /**
- * Shows a progress dialog, then starts restoring the backup asynchronously.
- *
- * @param date the date
- */
- private void restoreFromDateAsync(final Date date) {
- // Show a progress dialog
- ProgressDialog.show(
- activity,
- activity.getString(R.string.generic_progress_title),
- activity.getString(R.string.settings_backup_restore_progress_message),
- true);
-
- // Do the actual importing in another thread (don't block the UI)
- new Thread() {
- @Override
- public void run() {
- try {
- backup.restoreFromDate(date);
- showToast(R.string.sd_card_success_read_file);
- } catch (IOException e) {
- Log.e(Constants.TAG, "Failed to restore backup", e);
- showToast(R.string.sd_card_error_read_file);
- } finally {
- // Data may have been restored, "reboot" the app to catch it
- restartApplication();
- }
- }
- }.start();
- }
-
- /**
- * Restarts My Tracks completely.
- * This forces any modified data to be re-read.
- */
- private void restartApplication() {
- Intent intent = new Intent(activity, MyTracks.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- activity.startActivity(intent);
- }
-
- /**
- * Shows a toast with the given contents.
- */
- private void showToast(final int resId) {
- activity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(activity, resId, Toast.LENGTH_LONG).show();
- }
- });
- }
-
- /**
- * Safely dismisses the given dialog.
- */
- private void dismissDialog(final Dialog dialog) {
- activity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- dialog.dismiss();
- }
- });
- }
-}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/backup/BackupAsyncTask.java b/MyTracks/src/com/google/android/apps/mytracks/io/backup/BackupAsyncTask.java
new file mode 100644
index 000000000..3ef19ad31
--- /dev/null
+++ b/MyTracks/src/com/google/android/apps/mytracks/io/backup/BackupAsyncTask.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.android.apps.mytracks.io.backup;
+
+import com.google.android.apps.mytracks.util.FileUtils;
+import com.google.android.maps.mytracks.R;
+
+import android.os.AsyncTask;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * AsyncTask to backup data to the SD card.
+ *
+ * @author Jimmy Shih
+ */
+public class BackupAsyncTask extends AsyncTask {
+
+ private static final String TAG = BackupAsyncTask.class.getSimpleName();
+
+ private BackupActivity backupActivity;
+ private final ExternalFileBackup externalFileBackup;
+
+ // true if the AsyncTask result is success
+ private boolean success;
+
+ // true if the AsyncTask has completed
+ private boolean completed;
+
+ // message id to return to the activity
+ private int messageId;
+
+ /**
+ * Creates an AsyncTask.
+ *
+ * @param backupActivity the activity currently associated with this
+ * AsyncTask
+ */
+ public BackupAsyncTask(BackupActivity backupActivity) {
+ this.backupActivity = backupActivity;
+ this.externalFileBackup = new ExternalFileBackup(backupActivity, new FileUtils());
+ success = false;
+ completed = false;
+ messageId = R.string.sd_card_error_write_file;
+ }
+
+ /**
+ * Sets the current {@link BackupActivity} associated with this AyncTask.
+ *
+ * @param activity the current {@link BackupActivity}, can be null
+ */
+ public void setActivity(BackupActivity activity) {
+ this.backupActivity = activity;
+ if (completed && backupActivity != null) {
+ backupActivity.onAsyncTaskCompleted(success, messageId);
+ }
+ }
+
+ @Override
+ protected void onPreExecute() {
+ if (backupActivity != null) {
+ backupActivity.showProgressDialog();
+ }
+ }
+
+ @Override
+ protected Boolean doInBackground(Void... params) {
+ FileUtils fileUtils = new FileUtils();
+
+ if (!fileUtils.isSdCardAvailable()) {
+ messageId = R.string.sd_card_error_no_storage;
+ return false;
+ }
+
+ if (!externalFileBackup.isBackupsDirectoryAvailable(true)) {
+ messageId = R.string.sd_card_error_create_dir;
+ return false;
+ }
+
+ try {
+ externalFileBackup.writeToDefaultFile();
+ messageId = R.string.sd_card_success_write_file;
+ return true;
+ } catch (IOException e) {
+ Log.d(TAG, "IO exception", e);
+ return false;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ success = result;
+ completed = true;
+ if (backupActivity != null) {
+ backupActivity.onAsyncTaskCompleted(success, messageId);
+ }
+ }
+}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/backup/RestoreActivity.java b/MyTracks/src/com/google/android/apps/mytracks/io/backup/RestoreActivity.java
new file mode 100644
index 000000000..5e3492394
--- /dev/null
+++ b/MyTracks/src/com/google/android/apps/mytracks/io/backup/RestoreActivity.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.android.apps.mytracks.io.backup;
+
+import com.google.android.apps.mytracks.MyTracks;
+import com.google.android.maps.mytracks.R;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Toast;
+
+import java.util.Date;
+
+/**
+ * An activity to restore data from the SD card.
+ *
+ * @author Jimmy Shih
+ */
+public class RestoreActivity extends Activity {
+
+ public static final String EXTRA_DATE = "date";
+
+ private static final String TAG = RestoreActivity.class.getSimpleName();
+ private static final int DIALOG_PROGRESS_ID = 0;
+
+ private RestoreAsyncTask restoreAsyncTask;
+ private ProgressDialog progressDialog;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Object retained = getLastNonConfigurationInstance();
+ if (retained instanceof RestoreAsyncTask) {
+ restoreAsyncTask = (RestoreAsyncTask) retained;
+ restoreAsyncTask.setActivity(this);
+ } else {
+ long date = getIntent().getLongExtra(EXTRA_DATE, -1L);
+ if (date == -1L) {
+ Log.d(TAG, "Invalid date");
+ finish();
+ }
+ restoreAsyncTask = new RestoreAsyncTask(this, new Date(date));
+ restoreAsyncTask.execute();
+ }
+ }
+
+ @Override
+ public Object onRetainNonConfigurationInstance() {
+ restoreAsyncTask.setActivity(null);
+ return restoreAsyncTask;
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ switch (id) {
+ case DIALOG_PROGRESS_ID:
+ progressDialog = new ProgressDialog(this);
+ progressDialog.setCancelable(true);
+ progressDialog.setIcon(android.R.drawable.ic_dialog_info);
+ progressDialog.setIndeterminate(true);
+ progressDialog.setMessage(getString(R.string.settings_backup_restore_progress_message));
+ progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ finish();
+ }
+ });
+ progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
+ progressDialog.setTitle(R.string.generic_progress_title);
+ return progressDialog;
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Invokes when the associated AsyncTask completes.
+ *
+ * @param success true if the AsyncTask is successful
+ * @param messageId message id to display to user
+ */
+ public void onAsyncTaskCompleted(boolean success, int messageId) {
+ removeDialog(DIALOG_PROGRESS_ID);
+ Toast.makeText(this, messageId, success ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG).show();
+ startActivity(new Intent(this, MyTracks.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
+ finish();
+ }
+
+ /**
+ * Shows the progress dialog.
+ */
+ public void showProgressDialog() {
+ showDialog(DIALOG_PROGRESS_ID);
+ }
+}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/backup/RestoreAsyncTask.java b/MyTracks/src/com/google/android/apps/mytracks/io/backup/RestoreAsyncTask.java
new file mode 100644
index 000000000..23917d521
--- /dev/null
+++ b/MyTracks/src/com/google/android/apps/mytracks/io/backup/RestoreAsyncTask.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.android.apps.mytracks.io.backup;
+
+import com.google.android.apps.mytracks.util.FileUtils;
+import com.google.android.maps.mytracks.R;
+
+import android.os.AsyncTask;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.Date;
+
+/**
+ * AsyncTask to restore data from the SD card.
+ *
+ * @author Jimmy Shih
+ */
+public class RestoreAsyncTask extends AsyncTask {
+
+ private static final String TAG = RestoreAsyncTask.class.getSimpleName();
+
+ private RestoreActivity restoreActivity;
+ private final Date date;
+ private final ExternalFileBackup externalFileBackup;
+
+ // true if the AsyncTask result is success
+ private boolean success;
+
+ // true if the AsyncTask has completed
+ private boolean completed;
+
+ // message id to return to the activity
+ private int messageId;
+
+ /**
+ * Creates an AsyncTask.
+ *
+ * @param restoreActivity the activity currently associated with this
+ * AsyncTask
+ * @param date the date to retore from
+ */
+ public RestoreAsyncTask(RestoreActivity restoreActivity, Date date) {
+ this.restoreActivity = restoreActivity;
+ this.date = date;
+ this.externalFileBackup = new ExternalFileBackup(restoreActivity, new FileUtils());
+ success = false;
+ completed = false;
+ messageId = R.string.sd_card_error_read_file;
+ }
+
+ /**
+ * Sets the current {@link RestoreActivity} associated with this AyncTask.
+ *
+ * @param activity the current {@link RestoreActivity}, can be null
+ */
+ public void setActivity(RestoreActivity activity) {
+ this.restoreActivity = activity;
+ if (completed && restoreActivity != null) {
+ restoreActivity.onAsyncTaskCompleted(success, messageId);
+ }
+ }
+
+ @Override
+ protected void onPreExecute() {
+ if (restoreActivity != null) {
+ restoreActivity.showProgressDialog();
+ }
+ }
+
+ @Override
+ protected Boolean doInBackground(Void... params) {
+ try {
+ externalFileBackup.restoreFromDate(date);
+ messageId = R.string.sd_card_success_read_file;
+ return true;
+ } catch (IOException e) {
+ Log.d(TAG, "IO exception", e);
+ return false;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ success = result;
+ completed = true;
+ if (restoreActivity != null) {
+ restoreActivity.onAsyncTaskCompleted(success, messageId);
+ }
+ }
+}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/backup/RestoreChooserActivity.java b/MyTracks/src/com/google/android/apps/mytracks/io/backup/RestoreChooserActivity.java
new file mode 100644
index 000000000..8b5fc81c7
--- /dev/null
+++ b/MyTracks/src/com/google/android/apps/mytracks/io/backup/RestoreChooserActivity.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.android.apps.mytracks.io.backup;
+
+import com.google.android.apps.mytracks.util.FileUtils;
+import com.google.android.apps.mytracks.util.StringUtils;
+import com.google.android.maps.mytracks.R;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.Toast;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Date;
+
+/**
+ * An activity to choose a date to restore from.
+ *
+ * @author Jimmy Shih
+ */
+public class RestoreChooserActivity extends Activity {
+
+ private static final Comparator REVERSE_DATE_ORDER = new Comparator() {
+ @Override
+ public int compare(Date s1, Date s2) {
+ return s2.compareTo(s1);
+ }
+ };
+ private static final int DIALOG_CHOOSER_ID = 0;
+
+ private Date[] backupDates;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ FileUtils fileUtils = new FileUtils();
+ ExternalFileBackup externalFileBackup = new ExternalFileBackup(this, fileUtils);
+
+ // Get the list of existing backups
+ if (!fileUtils.isSdCardAvailable()) {
+ Toast.makeText(this, R.string.sd_card_error_no_storage, Toast.LENGTH_LONG).show();
+ finish();
+ return;
+ }
+
+ if (!externalFileBackup.isBackupsDirectoryAvailable(false)) {
+ Toast.makeText(this, R.string.settings_backup_restore_no_backup, Toast.LENGTH_LONG).show();
+ finish();
+ return;
+ }
+
+ backupDates = externalFileBackup.getAvailableBackups();
+ if (backupDates == null || backupDates.length == 0) {
+ Toast.makeText(this, R.string.settings_backup_restore_no_backup, Toast.LENGTH_LONG).show();
+ finish();
+ return;
+ }
+
+ if (backupDates.length == 1) {
+ startActivity(new Intent(this, RestoreActivity.class)
+ .putExtra(RestoreActivity.EXTRA_DATE, backupDates[0].getTime()));
+ finish();
+ return;
+ }
+
+ Arrays.sort(backupDates, REVERSE_DATE_ORDER);
+ showDialog(DIALOG_CHOOSER_ID);
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ switch (id) {
+ case DIALOG_CHOOSER_ID:
+ String items[] = new String[backupDates.length];
+ for (int i = 0; i < backupDates.length; i++) {
+ items[i] = StringUtils.formatDateTime(this, backupDates[i].getTime());
+ }
+ return new AlertDialog.Builder(this)
+ .setCancelable(true)
+ .setItems(items, new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ startActivity(new Intent(RestoreChooserActivity.this, RestoreActivity.class)
+ .putExtra(RestoreActivity.EXTRA_DATE, backupDates[which].getTime()));
+ finish();
+ }
+ })
+ .setOnCancelListener(new OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ finish();
+ }
+ })
+ .setTitle(R.string.settings_backup_restore_select_title)
+ .create();
+ default:
+ return null;
+ }
+ }
+}