From 2dbab929acf3fe42541d5bc2a1a8c69202527d95 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Fri, 10 Jul 2020 16:18:51 +0200 Subject: [PATCH] Export: replaced async task with thread. Almost identical to the import. --- doc/activities.txt | 4 +- src/main/AndroidManifest.xml | 4 +- .../ExportFinishedDialogFragment.java | 105 ++++++++ .../ExportProgressDialogFragment.java | 245 ++++++++++++++++++ .../ImportProgressDialogFragment.java | 18 +- .../io/file/exporter/ExportActivity.java | 151 ++--------- .../io/file/exporter/ExportAsyncTask.java | 168 ------------ .../importer/DirectoryChooserActivity.java | 33 ++- .../io/file/importer/ImportActivity.java | 5 + src/main/res/xml/settings.xml | 4 +- 10 files changed, 427 insertions(+), 310 deletions(-) create mode 100644 src/main/java/de/dennisguse/opentracks/fragments/ExportFinishedDialogFragment.java create mode 100644 src/main/java/de/dennisguse/opentracks/fragments/ExportProgressDialogFragment.java delete mode 100644 src/main/java/de/dennisguse/opentracks/io/file/exporter/ExportAsyncTask.java 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.