diff --git a/doc/activities.txt b/doc/activities.txt
index 47b00f8c1..3536f7c73 100644
--- a/doc/activities.txt
+++ b/doc/activities.txt
@@ -48,6 +48,8 @@ SettingsActivity --> AboutActivity
SettingsActivity --> HelpActivity
SettingsActivity --> ExportActivity
SettingsActivity --> DirectoryChooserActivity
-DirectoryChooserActivity --> ImportActivity
+SettingsActivity --> DirectoryChooserActivity
+DirectoryChooserActivity$ImportDirectoryChooserActivity --> ImportActivity
+DirectoryChooserActivity$ExportDirectoryChooserActivity --> ExportActivity
@enduml
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index 35321db8f..4d08c38eb 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -44,7 +44,7 @@ limitations under the License.
android:name=".HelpActivity"
android:label="@string/menu_help" />
-
+
+
+
diff --git a/src/main/java/de/dennisguse/opentracks/fragments/ExportFinishedDialogFragment.java b/src/main/java/de/dennisguse/opentracks/fragments/ExportFinishedDialogFragment.java
new file mode 100644
index 000000000..61818276b
--- /dev/null
+++ b/src/main/java/de/dennisguse/opentracks/fragments/ExportFinishedDialogFragment.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 de.dennisguse.opentracks.fragments;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.FragmentManager;
+
+import de.dennisguse.opentracks.R;
+
+public class ExportFinishedDialogFragment extends DialogFragment {
+
+ private static final String TAG = ExportFinishedDialogFragment.class.getSimpleName();
+
+ private static final String EXTRA_DIRECTORY_NAME_KEY = "directory_name";
+ private static final String EXTRA_EXPORTED_TRACK_COUNT = "exported_track_count";
+ private static final String EXTRA_TOTAL_TRACK_COUNT = "";
+
+ private static final String FINISHED_DIALOG_TAG = "finished_dialog_tag";
+
+ private DismissCallback caller;
+
+ public static void showDialog(FragmentManager fragmentManager, String directoryDisplayName, int successCount, int fileCount) {
+ Bundle bundle = new Bundle();
+ bundle.putString(EXTRA_DIRECTORY_NAME_KEY, directoryDisplayName);
+ bundle.putInt(EXTRA_EXPORTED_TRACK_COUNT, successCount);
+ bundle.putInt(EXTRA_TOTAL_TRACK_COUNT, fileCount);
+
+ ExportFinishedDialogFragment dialogFragment = new ExportFinishedDialogFragment();
+ dialogFragment.setArguments(bundle);
+ dialogFragment.show(fragmentManager, FINISHED_DIALOG_TAG);
+ }
+
+ @Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+ try {
+ caller = (DismissCallback) context;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(context + " must implement " + DismissCallback.class.getSimpleName());
+ }
+ }
+
+ @Override
+ @NonNull
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ String directoryDisplayName = getArguments().getString(EXTRA_DIRECTORY_NAME_KEY);
+ int exportedTracksCount = getArguments().getInt(EXTRA_EXPORTED_TRACK_COUNT);
+ int totalTracksCount = getArguments().getInt(EXTRA_TOTAL_TRACK_COUNT);
+
+ int iconId;
+ int titleId;
+ String message;
+ String totalTracks = getResources().getQuantityString(R.plurals.tracks, totalTracksCount, totalTracksCount);
+ if (exportedTracksCount == totalTracksCount) {
+ iconId = R.drawable.ic_dialog_success_24dp;
+ titleId = R.string.generic_success_title;
+ message = getString(
+ R.string.export_success, totalTracks, directoryDisplayName);
+ } else {
+ iconId = R.drawable.ic_dialog_error_24dp;
+ titleId = R.string.generic_error_title;
+ message = getString(R.string.export_error, exportedTracksCount, totalTracks, directoryDisplayName);
+ }
+ Dialog alertDialog = new AlertDialog.Builder(getContext()).setCancelable(true)
+ .setIcon(iconId).setMessage(message)
+ .setPositiveButton(R.string.generic_ok, (dialog, arg1) -> {
+ dialog.dismiss();
+ caller.onDismissed();
+ }).setTitle(titleId)
+ .create();
+
+ alertDialog.setCanceledOnTouchOutside(false);
+ return alertDialog;
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ caller = null;
+ }
+
+ public interface DismissCallback {
+ void onDismissed();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/de/dennisguse/opentracks/fragments/ExportProgressDialogFragment.java b/src/main/java/de/dennisguse/opentracks/fragments/ExportProgressDialogFragment.java
new file mode 100644
index 000000000..cdab84e76
--- /dev/null
+++ b/src/main/java/de/dennisguse/opentracks/fragments/ExportProgressDialogFragment.java
@@ -0,0 +1,245 @@
+/*
+ * 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 de.dennisguse.opentracks.fragments;
+
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.documentfile.provider.DocumentFile;
+import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.FragmentManager;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import de.dennisguse.opentracks.R;
+import de.dennisguse.opentracks.content.data.Track;
+import de.dennisguse.opentracks.content.data.TracksColumns;
+import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
+import de.dennisguse.opentracks.io.file.TrackFileFormat;
+import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
+import de.dennisguse.opentracks.util.DialogUtils;
+import de.dennisguse.opentracks.util.FileUtils;
+
+public class ExportProgressDialogFragment extends DialogFragment {
+
+ private static final String EXTRA_DIRECTORY_URI_KEY = "directory_uri";
+ private static final String EXTRA_TRACKFILEFORMAT_KEY = "trackfileformat";
+
+ private static final String TAG = ExportProgressDialogFragment.class.getSimpleName();
+
+ public static final String EXPORT_DIALOG_TAG = "import_dialog_tag";
+
+ private DismissCallback caller;
+
+ private ExportThread exportThread;
+
+ private String directoryDisplayName;
+
+ private int trackExportSuccessCount;
+ private int trackCount;
+
+ /**
+ * Create a new instance.
+ */
+ public static void showDialog(FragmentManager fragmentManager, String trackFileFormat, Uri documentFileUri) {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(EXTRA_DIRECTORY_URI_KEY, documentFileUri);
+ bundle.putString(EXTRA_DIRECTORY_URI_KEY, trackFileFormat);
+
+ ExportProgressDialogFragment dialogFragment = new ExportProgressDialogFragment();
+ dialogFragment.setArguments(bundle);
+ dialogFragment.setRetainInstance(true);
+ dialogFragment.show(fragmentManager, EXPORT_DIALOG_TAG);
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // This fragment uses a background thread, so it should not be recreated during activity re-creation.
+ setRetainInstance(true);
+
+ Uri directoryUri = getArguments().getParcelable(EXTRA_DIRECTORY_URI_KEY);
+ TrackFileFormat trackFileFormat = TrackFileFormat.valueOf(getArguments().getString(EXTRA_TRACKFILEFORMAT_KEY));
+ DocumentFile documentFile = DocumentFile.fromTreeUri(getContext(), directoryUri);
+
+ directoryDisplayName = FileUtils.getPath(documentFile);
+
+ exportThread = new ExportThread(trackFileFormat, documentFile);
+ }
+
+ @Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+ try {
+ caller = (DismissCallback) context;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(context + " must implement " + DismissCallback.class.getSimpleName());
+ }
+ }
+
+ @Override
+ @NonNull
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ ProgressDialog progressDialog = DialogUtils.createHorizontalProgressDialog(getContext(),
+ R.string.export_progress_message,
+ dialog -> {
+ exportThread.interrupt();
+ dialog.dismiss();
+ caller.onExportCanceled(directoryDisplayName, trackExportSuccessCount, trackCount);
+ }, directoryDisplayName);
+
+ progressDialog.setIndeterminate(false);
+ progressDialog.setMax(trackCount);
+ progressDialog.setProgress(Math.min(trackExportSuccessCount, trackCount));
+
+ return progressDialog;
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ setProgress();
+ if (!exportThread.isAlive()) {
+ exportThread.start();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ if (!exportThread.isAlive()) {
+ exportThread.interrupt();
+ }
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ caller = null;
+ }
+
+ private void onProgress(int trackImportSuccessCount, int fileCount) {
+ this.trackExportSuccessCount = trackImportSuccessCount;
+ this.trackCount = fileCount;
+ setProgress();
+ }
+
+ private void setProgress() {
+ ProgressDialog dialog = (ProgressDialog) getDialog();
+ if (dialog != null) {
+ dialog.setIndeterminate(false);
+ dialog.setMax(trackCount);
+ dialog.setProgress(Math.min(trackExportSuccessCount, trackCount));
+ }
+ }
+
+ private void onExportCompleted(int successCount, int fileCount) {
+ if (caller != null && !isDetached()) {
+ caller.onExportFinished(directoryDisplayName, successCount, fileCount);
+ }
+ dismissAllowingStateLoss();
+ }
+
+ private class ExportThread extends Thread {
+
+ private TrackFileFormat trackFileFormat;
+
+ private DocumentFile directory;
+
+ ExportThread(TrackFileFormat trackFileFormat, DocumentFile directory) {
+ this.directory = directory;
+ this.trackFileFormat = trackFileFormat;
+ }
+
+ @Override
+ public void run() {
+ Context context = ExportProgressDialogFragment.this.getContext();
+
+ ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
+
+ try (Cursor cursor = contentProviderUtils.getTrackCursor(null, null, TracksColumns._ID)) {
+ if (cursor == null) {
+ onExportCompleted(0, 0);
+ return;
+ }
+
+ trackCount = cursor.getCount();
+ for (int i = 0; i < trackCount; i++) {
+ onProgress(i, trackCount);
+ if (Thread.interrupted()) {
+ return;
+ }
+
+ cursor.moveToPosition(i);
+ Track track = contentProviderUtils.createTrack(cursor);
+ if (track != null && exportTrack(context, track)) {
+ trackExportSuccessCount++;
+ }
+ }
+
+ onExportCompleted(trackExportSuccessCount, trackCount);
+ }
+ }
+
+ private Boolean exportTrack(Context context, Track track) {
+ TrackExporter trackExporter = trackFileFormat.newTrackExporter(context, new Track[]{track});
+
+ //TODO Move to helper function
+ String fileName = track.getId() + "." + trackFileFormat.getExtension();
+
+ // Overwrite a file if it exists; DocumentFile.createFile() creates a new file appending a suffix if the displayname already exists.
+ DocumentFile file = directory.findFile(fileName);
+ if (file == null) {
+ file = directory.createFile(trackFileFormat.getMimeType(), fileName);
+ }
+
+ try (OutputStream outputStream = context.getContentResolver().openOutputStream(file.getUri())) {
+ if (trackExporter.writeTrack(context, outputStream)) {
+ return true;
+ } else {
+ if (!file.delete()) {
+ Log.e(TAG, "Unable to delete file");
+ }
+ Log.e(TAG, "Unable to export track");
+ return false;
+ }
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "Unable to open file " + file.getName(), e);
+ return false;
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to close file output stream", e);
+ return false;
+ }
+ }
+ }
+
+ public interface DismissCallback {
+ void onExportCanceled(String directoryDisplayName, int successCount, int fileCount);
+
+ void onExportFinished(String directoryDisplayName, int successCount, int fileCount);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/de/dennisguse/opentracks/fragments/ImportProgressDialogFragment.java b/src/main/java/de/dennisguse/opentracks/fragments/ImportProgressDialogFragment.java
index ac9084428..575072e05 100644
--- a/src/main/java/de/dennisguse/opentracks/fragments/ImportProgressDialogFragment.java
+++ b/src/main/java/de/dennisguse/opentracks/fragments/ImportProgressDialogFragment.java
@@ -100,18 +100,19 @@ public class ImportProgressDialogFragment extends DialogFragment {
@Override
@NonNull
public Dialog onCreateDialog(Bundle savedInstanceState) {
- ProgressDialog dialog = DialogUtils.createHorizontalProgressDialog(
+ ProgressDialog progressDialog = DialogUtils.createHorizontalProgressDialog(
getContext(), R.string.import_progress_message,
dialogInterface -> {
importThread.interrupt();
dialogInterface.dismiss();
+ caller.onImportCanceled(directoryDisplayName, trackImportSuccessCount, fileCount);
}, directoryDisplayName);
- if (trackImportSuccessCount == 0) {
- dialog.setIndeterminate(true);
- } else {
- setProgress();
- }
- return dialog;
+
+ progressDialog.setIndeterminate(false);
+ progressDialog.setMax(fileCount);
+ progressDialog.setProgress(Math.min(trackImportSuccessCount, fileCount));
+
+ return progressDialog;
}
@Override
@@ -169,7 +170,6 @@ public class ImportProgressDialogFragment extends DialogFragment {
@Override
public void run() {
- Log.w(TAG, "" + this);
Context context = ImportProgressDialogFragment.this.getContext();
List files = FileUtils.getFiles(file);
@@ -219,6 +219,8 @@ public class ImportProgressDialogFragment extends DialogFragment {
}
public interface DismissCallback {
+ void onImportCanceled(String directoryDisplayName, int successCount, int fileCount);
+
void onImportFinished(String directoryDisplayName, int successCount, int fileCount);
}
}
\ No newline at end of file
diff --git a/src/main/java/de/dennisguse/opentracks/io/file/exporter/ExportActivity.java b/src/main/java/de/dennisguse/opentracks/io/file/exporter/ExportActivity.java
index cb060c745..61a84e9ea 100644
--- a/src/main/java/de/dennisguse/opentracks/io/file/exporter/ExportActivity.java
+++ b/src/main/java/de/dennisguse/opentracks/io/file/exporter/ExportActivity.java
@@ -16,160 +16,59 @@
package de.dennisguse.opentracks.io.file.exporter;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.ProgressDialog;
-import android.content.DialogInterface;
-import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
-import androidx.annotation.Nullable;
-import androidx.documentfile.provider.DocumentFile;
+import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity;
-import de.dennisguse.opentracks.R;
-import de.dennisguse.opentracks.io.file.TrackFileFormat;
-import de.dennisguse.opentracks.util.DialogUtils;
-import de.dennisguse.opentracks.util.FileUtils;
-import de.dennisguse.opentracks.util.PreferencesUtils;
+import de.dennisguse.opentracks.fragments.ExportFinishedDialogFragment;
+import de.dennisguse.opentracks.fragments.ExportProgressDialogFragment;
/**
* An activity for saving tracks to the external storage.
*
* @author Rodrigo Damazio
*/
-public class ExportActivity extends FragmentActivity {
+public class ExportActivity extends FragmentActivity implements ExportProgressDialogFragment.DismissCallback, ExportFinishedDialogFragment.DismissCallback {
- private static final int DIRECTORY_PICKER_REQUEST_CODE = 6;
+ public static final String EXTRA_DIRECTORY_URI_KEY = "directory_uri";
- private static final int DIALOG_PROGRESS_ID = 0;
- private static final int DIALOG_RESULT_ID = 1;
+ public static final String EXTRA_TRACKFILEFORMAT_KEY = "trackfileformat";
- private String directoryDisplayName;
-
- private ExportAsyncTask exportAsyncTask;
- private ProgressDialog progressDialog;
-
- private int processedTrackCount;
- private int totalTrackCount;
+ private static final String BUNDLE_ACTIVITY_RECREATED = "activity_recreated";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
- intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- startActivityForResult(intent, DIRECTORY_PICKER_REQUEST_CODE);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent resultData) {
- super.onActivityResult(requestCode, resultCode, resultData);
- if (requestCode == DIRECTORY_PICKER_REQUEST_CODE) {
- if (resultCode == Activity.RESULT_OK) {
- Uri directoryUri = resultData.getData();
- DocumentFile pickedDirectory = DocumentFile.fromTreeUri(this, directoryUri);
- DocumentFile exportDirectory = pickedDirectory.findFile(FileUtils.EXPORT_DIR);
- if (exportDirectory == null) {
- exportDirectory = pickedDirectory.createDirectory(FileUtils.EXPORT_DIR);
- }
-
- TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(this);
-
- directoryDisplayName = FileUtils.getPath(exportDirectory);
- exportAsyncTask = new ExportAsyncTask(this, trackFileFormat, exportDirectory);
- exportAsyncTask.execute();
- } else {
- finish();
- }
+ if (savedInstanceState == null || !savedInstanceState.getBoolean(BUNDLE_ACTIVITY_RECREATED, false)) {
+ Uri directoryUri = getIntent().getParcelableExtra(EXTRA_DIRECTORY_URI_KEY);
+ String trackfileformat = getIntent().getStringExtra(EXTRA_TRACKFILEFORMAT_KEY);
+ ExportProgressDialogFragment.showDialog(getSupportFragmentManager(), trackfileformat, directoryUri);
}
}
@Override
- protected Dialog onCreateDialog(int id) {
- switch (id) {
- case DIALOG_PROGRESS_ID:
- progressDialog = DialogUtils.createHorizontalProgressDialog(this,
- R.string.export_progress_message,
- new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialog) {
- exportAsyncTask.cancel(true);
- dialog.dismiss();
- finish();
- }
- }, directoryDisplayName);
- return progressDialog;
- case DIALOG_RESULT_ID:
- int iconId;
- int titleId;
- String message;
- String totalTracks = getResources()
- .getQuantityString(R.plurals.tracks, totalTrackCount, totalTrackCount);
- if (processedTrackCount == totalTrackCount) {
- iconId = R.drawable.ic_dialog_success_24dp;
- titleId = R.string.generic_success_title;
- message = getString(
- R.string.export_success, totalTracks, directoryDisplayName);
- } else {
- iconId = R.drawable.ic_dialog_error_24dp;
- titleId = R.string.generic_error_title;
- message = getString(R.string.export_error, processedTrackCount, totalTracks, directoryDisplayName);
- }
- AlertDialog.Builder builder = new AlertDialog.Builder(this).setCancelable(true)
- .setIcon(iconId).setMessage(message)
- .setOnCancelListener(new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialog) {
- dialog.dismiss();
- finish();
- }
- }).setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int arg1) {
- dialog.dismiss();
- finish();
- }
- }).setTitle(titleId);
- return builder.create();
- default:
- return null;
- }
+ protected void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(BUNDLE_ACTIVITY_RECREATED, true);
}
- /**
- * Invokes when the associated AsyncTask completes.
- *
- * @param aSuccessCount the number of tracks successfully saved
- * @param aTotalCount the number of tracks to save
- */
- public void onAsyncTaskCompleted(int aSuccessCount, int aTotalCount) {
- processedTrackCount = aSuccessCount;
- totalTrackCount = aTotalCount;
- removeDialog(DIALOG_PROGRESS_ID);
- showDialog(DIALOG_RESULT_ID);
+ @Override
+ public void onExportCanceled(String directoryDisplayName, int successCount, int fileCount) {
+ onExportFinished(directoryDisplayName, successCount, fileCount);
}
- /**
- * Shows the progress dialog.
- */
- public void showProgressDialog() {
- showDialog(DIALOG_PROGRESS_ID);
+ @Override
+ public void onExportFinished(final String directoryDisplayName, final int successCount, final int fileCount) {
+ runOnUiThread(() -> {
+ ExportFinishedDialogFragment.showDialog(getSupportFragmentManager(), directoryDisplayName, successCount, fileCount);
+ });
}
- /**
- * Sets the progress dialog value.
- *
- * @param number the number of points saved
- * @param max the maximum number of points
- */
- public void setProgressDialogValue(int number, int max) {
- if (progressDialog != null) {
- progressDialog.setIndeterminate(false);
- progressDialog.setMax(max);
- progressDialog.setProgress(Math.min(number, max));
- }
+ @Override
+ public void onDismissed() {
+ finish();
}
}
diff --git a/src/main/java/de/dennisguse/opentracks/io/file/exporter/ExportAsyncTask.java b/src/main/java/de/dennisguse/opentracks/io/file/exporter/ExportAsyncTask.java
deleted file mode 100644
index 0a5c0bc53..000000000
--- a/src/main/java/de/dennisguse/opentracks/io/file/exporter/ExportAsyncTask.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * 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 de.dennisguse.opentracks.io.file.exporter;
-
-import android.database.Cursor;
-import android.os.AsyncTask;
-import android.os.PowerManager.WakeLock;
-import android.util.Log;
-
-import androidx.documentfile.provider.DocumentFile;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.OutputStream;
-
-import de.dennisguse.opentracks.content.data.Track;
-import de.dennisguse.opentracks.content.data.TracksColumns;
-import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
-import de.dennisguse.opentracks.io.file.TrackFileFormat;
-import de.dennisguse.opentracks.util.SystemUtils;
-
-/**
- * Async Task to save tracks to the external storage.
- *
- * @author Jimmy Shih
- */
-//TODO Make independent from ExportActivity?
-class ExportAsyncTask extends AsyncTask {
-
- private static final String TAG = ExportAsyncTask.class.getSimpleName();
- private final TrackFileFormat trackFileFormat;
- private final DocumentFile directory;
- private final ContentProviderUtils contentProviderUtils;
- private ExportActivity exportActivity;
- private WakeLock wakeLock;
-
- private int processedTrackCount = 0;
- private int totalTrackCount = 0;
-
- /**
- * Creates an AsyncTask.
- *
- * @param exportActivity the activity currently associated with this task
- * @param trackFileFormat the track file format
- * @param directory the directory to write the file
- */
- public ExportAsyncTask(ExportActivity exportActivity, TrackFileFormat trackFileFormat, DocumentFile directory) {
- this.exportActivity = exportActivity;
- this.trackFileFormat = trackFileFormat;
- this.directory = directory;
- contentProviderUtils = new ContentProviderUtils(exportActivity);
- }
-
- @Override
- protected void onPreExecute() {
- if (exportActivity != null) {
- exportActivity.showProgressDialog();
- }
- }
-
- @Override
- protected Boolean doInBackground(Void... params) {
- try {
- Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
-
- wakeLock = SystemUtils.acquireWakeLock(exportActivity, wakeLock);
-
- return exportAllTracks();
- } finally {
- wakeLock = SystemUtils.releaseWakeLock(wakeLock);
- }
- }
-
- @Override
- protected void onProgressUpdate(Integer... values) {
- if (exportActivity != null) {
- exportActivity.setProgressDialogValue(values[0], values[1]);
- }
- }
-
- @Override
- protected void onPostExecute(Boolean result) {
- if (exportActivity != null) {
- exportActivity.onAsyncTaskCompleted(processedTrackCount, totalTrackCount);
- }
- }
-
- @Override
- protected void onCancelled() {
- if (exportActivity != null) {
- exportActivity.onAsyncTaskCompleted(processedTrackCount, totalTrackCount);
- }
- }
-
- /**
- * Export the track to one file.
- *
- * @param track the track
- */
- private Boolean exportTrack(Track track) {
- TrackExporter trackExporter = trackFileFormat.newTrackExporter(exportActivity, new Track[]{track});
-
- String fileName = track.getId() + "." + trackFileFormat.getExtension();
-
- // Overwrite a file if it exists; DocumentFile.createFile() creates a new file appending a suffix if the displayname already exists.
- DocumentFile file = directory.findFile(fileName);
- if (file == null) {
- file = directory.createFile(trackFileFormat.getMimeType(), fileName);
- }
-
- try (OutputStream outputStream = exportActivity.getContentResolver().openOutputStream(file.getUri())) {
- if (trackExporter.writeTrack(exportActivity, outputStream)) {
- return true;
- } else {
- if (!file.delete()) {
- Log.d(TAG, "Unable to delete file");
- }
- Log.e(TAG, "Unable to export track");
- return false;
- }
- } catch (FileNotFoundException e) {
- Log.e(TAG, "Unable to open file " + file.getName(), e);
- return false;
- } catch (IOException e) {
- Log.e(TAG, "Unable to close file output stream", e);
- return false;
- }
- }
-
- /**
- * Saves all the tracks.
- */
- private Boolean exportAllTracks() {
- try (Cursor cursor = contentProviderUtils.getTrackCursor(null, null, TracksColumns._ID)) {
- if (cursor == null) {
- return false;
- }
- totalTrackCount = cursor.getCount();
-
- for (int i = 0; i < totalTrackCount; i++) {
- if (isCancelled()) {
- return false;
- }
- cursor.moveToPosition(i);
- Track track = contentProviderUtils.createTrack(cursor);
- if (track != null && exportTrack(track)) {
- processedTrackCount++;
- }
- publishProgress(i + 1, totalTrackCount);
- }
- return true;
- }
- }
-}
diff --git a/src/main/java/de/dennisguse/opentracks/io/file/importer/DirectoryChooserActivity.java b/src/main/java/de/dennisguse/opentracks/io/file/importer/DirectoryChooserActivity.java
index 20261a8fc..d60b8175b 100644
--- a/src/main/java/de/dennisguse/opentracks/io/file/importer/DirectoryChooserActivity.java
+++ b/src/main/java/de/dennisguse/opentracks/io/file/importer/DirectoryChooserActivity.java
@@ -8,9 +8,11 @@ import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
+import de.dennisguse.opentracks.io.file.exporter.ExportActivity;
import de.dennisguse.opentracks.util.IntentUtils;
+import de.dennisguse.opentracks.util.PreferencesUtils;
-public class DirectoryChooserActivity extends AppCompatActivity {
+public abstract class DirectoryChooserActivity extends AppCompatActivity {
private static final int DIRECTORY_PICKER_REQUEST_CODE = 6;
@@ -31,11 +33,34 @@ public class DirectoryChooserActivity extends AppCompatActivity {
if (resultCode == Activity.RESULT_OK) {
Uri directoryUri = resultData.getData();
- Intent intent = IntentUtils.newIntent(this, ImportActivity.class);
- intent.putExtra(ImportActivity.EXTRA_DIRECTORY_URI_KEY, directoryUri);
- startActivity(intent);
+
+ startActivity(createIntent(directoryUri));
}
finish();
}
}
+
+ protected abstract Intent createIntent(Uri directoryUri);
+
+
+ public static class ImportDirectoryChooserActivity extends DirectoryChooserActivity {
+
+ @Override
+ protected Intent createIntent(Uri directoryUri) {
+ Intent intent = IntentUtils.newIntent(this, ImportActivity.class);
+ intent.putExtra(ImportActivity.EXTRA_DIRECTORY_URI_KEY, directoryUri);
+ return intent;
+ }
+ }
+
+ public static class ExportDirectoryChooserActivity extends DirectoryChooserActivity {
+
+ @Override
+ protected Intent createIntent(Uri directoryUri) {
+ Intent intent = IntentUtils.newIntent(this, ExportActivity.class);
+ intent.putExtra(ExportActivity.EXTRA_DIRECTORY_URI_KEY, directoryUri);
+ intent.putExtra(ExportActivity.EXTRA_TRACKFILEFORMAT_KEY, PreferencesUtils.getExportTrackFileFormat(this));
+ return intent;
+ }
+ }
}
diff --git a/src/main/java/de/dennisguse/opentracks/io/file/importer/ImportActivity.java b/src/main/java/de/dennisguse/opentracks/io/file/importer/ImportActivity.java
index 95cdddad9..d5fab088b 100644
--- a/src/main/java/de/dennisguse/opentracks/io/file/importer/ImportActivity.java
+++ b/src/main/java/de/dennisguse/opentracks/io/file/importer/ImportActivity.java
@@ -54,6 +54,11 @@ public class ImportActivity extends FragmentActivity implements ImportProgressDi
outState.putBoolean(BUNDLE_ACTIVITY_RECREATED, true);
}
+ @Override
+ public void onImportCanceled(String directoryDisplayName, int successCount, int fileCount) {
+ onImportFinished(directoryDisplayName, successCount, fileCount);
+ }
+
@Override
public void onImportFinished(final String directoryDisplayName, final int successCount, final int fileCount) {
runOnUiThread(() -> {
diff --git a/src/main/res/xml/settings.xml b/src/main/res/xml/settings.xml
index 18d472198..a92987466 100644
--- a/src/main/res/xml/settings.xml
+++ b/src/main/res/xml/settings.xml
@@ -135,12 +135,12 @@ limitations under the License.