diff --git a/doc/activities.txt b/doc/activities.txt index 3536f7c73..f385ac47a 100644 --- a/doc/activities.txt +++ b/doc/activities.txt @@ -47,8 +47,9 @@ MarkerDetailActivity --> CoordinateOnMapIntent : URI SettingsActivity --> AboutActivity SettingsActivity --> HelpActivity SettingsActivity --> ExportActivity -SettingsActivity --> DirectoryChooserActivity -SettingsActivity --> DirectoryChooserActivity +SettingsActivity --> DirectoryChooserActivity$ImportDirectoryChooserActivity +SettingsActivity --> DirectoryChooserActivity$ExportDirectoryChooserActivity +SettingsActivity --> DirectoryChooserActivity$DefaultTrackExportDirectoryChooserActivity DirectoryChooserActivity$ImportDirectoryChooserActivity --> ImportActivity DirectoryChooserActivity$ExportDirectoryChooserActivity --> ExportActivity diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 8aa455e91..81d7cf1ea 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -152,6 +152,7 @@ limitations under the License. + - - listeners = new ArrayList<>(); - private Track.Id lastTrackId; @Override public void onCreate() { @@ -197,19 +194,11 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand voiceExecutor = null; } - if (PreferencesUtils.shouldInstantExportAfterWorkout(getApplicationContext())) { - TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(this); - - Uri directoryUri = PreferencesUtils.getDefaultExportDirectoryUri(getApplicationContext()); - exportTrackToUri(directoryUri, getApplicationContext(), contentProviderUtils, trackFileFormat, lastTrackId); - } - contentProviderUtils = null; binder.detachFromService(); binder = null; - // This should be the next to last operation wakeLock = SystemUtils.releaseWakeLock(wakeLock); @@ -424,7 +413,6 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand // Need to remember the recordingTrackId before setting it to -1L Track.Id trackId = recordingTrackId; - lastTrackId = recordingTrackId; boolean wasPaused = recordingTrackPaused; updateRecordingState(null, true); @@ -442,6 +430,9 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand updateTrackTotalTime(track); } } + + ExportUtils.postWorkoutExport(this, track); + endRecording(true); } diff --git a/src/main/java/de/dennisguse/opentracks/settings/SettingsActivity.java b/src/main/java/de/dennisguse/opentracks/settings/SettingsActivity.java index bfe3c2292..e22e19f8a 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/SettingsActivity.java +++ b/src/main/java/de/dennisguse/opentracks/settings/SettingsActivity.java @@ -1,13 +1,13 @@ package de.dennisguse.opentracks.settings; import android.content.SharedPreferences; -import android.net.Uri; import android.os.Bundle; import android.util.Log; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; +import androidx.documentfile.provider.DocumentFile; import androidx.fragment.app.DialogFragment; import androidx.preference.EditTextPreference; import androidx.preference.ListPreference; @@ -89,17 +89,13 @@ public class SettingsActivity extends AppCompatActivity implements ChooseActivit setExportTrackFileFormatOptions(); setWheelCircumferenceInputFilter(); - Preference instantExportDirectoryPreference = findPreference(getString(R.string.preference_key_default_export_uri)); - if (instantExportDirectoryPreference != null) { - instantExportDirectoryPreference.setSummaryProvider(preference -> { - Uri uri = PreferencesUtils.getDefaultExportDirectoryUri(getContext()); - return uri != null ? uri.getPath() : getString(R.string.summay_default_export_directory_not_set_yet); - }); - } - } - private boolean isDefaultExportDirectorySet() { - return PreferencesUtils.getDefaultExportDirectoryUri(getContext()) != null; + Preference instantExportDirectoryPreference = findPreference(getString(R.string.settings_default_export_directory_key)); + instantExportDirectoryPreference.setSummaryProvider(preference -> { + DocumentFile directory = PreferencesUtils.getDefaultExportDirectoryUri(getContext()); + //Use same value for not set as Androidx ListPreference and EditTextPreference + return directory != null ? directory.getName() : getString(R.string.not_set); + }); } @Override @@ -109,20 +105,13 @@ public class SettingsActivity extends AppCompatActivity implements ChooseActivit updateUnits(); updateReset(); updateBluetooth(); - updateExportDirectory(); - setInstantExportPreferenceEnabled(); + + updatePostWorkoutExport(); } - private void setInstantExportPreferenceEnabled() { - Preference instantExportEnabledPreference = findPreference("instantExportEnabled"); - assert instantExportEnabledPreference != null; - instantExportEnabledPreference.setEnabled(isDefaultExportDirectorySet()); - } - - private void updateExportDirectory() { - Preference defaultExportDirectoryPreference = findPreference("defaultExportUri"); - assert defaultExportDirectoryPreference != null; - HackUtils.invalidatePreference(defaultExportDirectoryPreference); + private void updatePostWorkoutExport() { + Preference instantExportEnabledPreference = findPreference(getString(R.string.post_workout_export_enabled_key)); + instantExportEnabledPreference.setEnabled(PreferencesUtils.isDefaultExportDirectoryUri(getContext())); } @Override diff --git a/src/main/java/de/dennisguse/opentracks/util/ExportUtils.java b/src/main/java/de/dennisguse/opentracks/util/ExportUtils.java new file mode 100644 index 000000000..2e1ab83fc --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/util/ExportUtils.java @@ -0,0 +1,65 @@ +package de.dennisguse.opentracks.util; + +import android.content.Context; +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.io.file.TrackFileFormat; +import de.dennisguse.opentracks.io.file.exporter.TrackExporter; + +public class ExportUtils { + + private static final String TAG = ExportUtils.class.getSimpleName(); + + public static void postWorkoutExport(Context context, Track track) { + if (PreferencesUtils.shouldInstantExportAfterWorkout(context)) { + TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(context); + DocumentFile directory = PreferencesUtils.getDefaultExportDirectoryUri(context); + + exportTrack(context, trackFileFormat, directory, track); + } + } + + public static boolean exportTrack(Context context, TrackFileFormat trackFileFormat, DocumentFile directory, Track track) { + TrackExporter trackExporter = trackFileFormat.newTrackExporter(context); + + DocumentFile exportDocumentFile = ExportUtils.getExportDocumentFile(track.getId(), trackFileFormat.getExtension(), directory, trackFileFormat.getMimeType()); + + try (OutputStream outputStream = context.getContentResolver().openOutputStream(exportDocumentFile.getUri())) { + if (trackExporter.writeTrack(track, outputStream)) { + return true; + } else { + if (!exportDocumentFile.delete()) { + Log.e(TAG, "Unable to delete exportDocumentFile"); + } + Log.e(TAG, "Unable to export track"); + return false; + } + } catch (FileNotFoundException e) { + Log.e(TAG, "Unable to open exportDocumentFile " + exportDocumentFile.getName(), e); + return false; + } catch (IOException e) { + Log.e(TAG, "Unable to close exportDocumentFile output stream", e); + return false; + } + } + + public static DocumentFile getExportDocumentFile(Track.Id trackId, String trackFileFormatExtension, DocumentFile directory, String mimeType) { + String fileName = getExportFileNameByTrackId(trackId, trackFileFormatExtension); + DocumentFile file = directory.findFile(fileName); + if (file == null) { + file = directory.createFile(mimeType, fileName); + } + return file; + } + + private static String getExportFileNameByTrackId(Track.Id trackId, String trackFileFormatExtension) { + return trackId.getId() + "." + trackFileFormatExtension; + } +} diff --git a/src/main/java/de/dennisguse/opentracks/util/PreferencesUtils.java b/src/main/java/de/dennisguse/opentracks/util/PreferencesUtils.java index 29e2ccada..0f0d037d2 100644 --- a/src/main/java/de/dennisguse/opentracks/util/PreferencesUtils.java +++ b/src/main/java/de/dennisguse/opentracks/util/PreferencesUtils.java @@ -20,8 +20,10 @@ import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.net.Uri; +import android.util.Log; import androidx.annotation.VisibleForTesting; +import androidx.documentfile.provider.DocumentFile; import androidx.preference.PreferenceManager; import de.dennisguse.opentracks.R; @@ -35,6 +37,8 @@ import de.dennisguse.opentracks.io.file.TrackFileFormat; */ public class PreferencesUtils { + private final static String TAG = PreferencesUtils.class.getSimpleName(); + private PreferencesUtils() { } @@ -339,8 +343,8 @@ public class PreferencesUtils { } public static boolean shouldInstantExportAfterWorkout(Context context) { - final boolean INSTANT_POST_WORKOUT_EXPORT_DEFAULT = context.getResources().getBoolean(R.bool.instant_export_enabled_default); - return getBoolean(context, R.string.instant_export_enabled_key, INSTANT_POST_WORKOUT_EXPORT_DEFAULT); + final boolean INSTANT_POST_WORKOUT_EXPORT_DEFAULT = context.getResources().getBoolean(R.bool.post_workout_export_enabled_default); + return getBoolean(context, R.string.post_workout_export_enabled_key, INSTANT_POST_WORKOUT_EXPORT_DEFAULT); } public static TrackFileFormat getExportTrackFileFormat(Context context) { @@ -390,19 +394,25 @@ public class PreferencesUtils { PreferenceManager.setDefaultValues(context, R.xml.settings, readAgain); } - public static Uri getDefaultExportDirectoryUri(Context context) - { + public static DocumentFile getDefaultExportDirectoryUri(Context context) { String singleExportDirectorySettingsKey = getString(context, R.string.settings_default_export_directory_key, null); if (singleExportDirectorySettingsKey == null) { return null; } - return Uri.parse(singleExportDirectorySettingsKey); + try { + return DocumentFile.fromTreeUri(context, Uri.parse(singleExportDirectorySettingsKey)); + } catch (Exception e) { + Log.w(TAG, "Could not decode default export directory: " + e.getMessage()); + } + return null; } public static void setDefaultExportDirectoryUri(Context context, Uri directoryUri) { - setString(context, - R.string.settings_default_export_directory_key, - directoryUri.toString() - ); + String value = directoryUri != null ? directoryUri.toString() : null; + setString(context, R.string.settings_default_export_directory_key, value); + } + + public static boolean isDefaultExportDirectoryUri(Context context) { + return getDefaultExportDirectoryUri(context) != null; } } diff --git a/src/main/res/values/settings.xml b/src/main/res/values/settings.xml index 4634c8723..4b3b571aa 100644 --- a/src/main/res/values/settings.xml +++ b/src/main/res/values/settings.xml @@ -27,10 +27,8 @@ 2135 settingsDefaultExportDirectory - false - Instant post-workout export - instantExportEnabled - defaultExportUri + instantExportEnabled + false defaultActivity @string/activity_type_unknown diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 42444c06f..0534380ce 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -602,8 +602,9 @@ limitations under the License. Hold to stop recording Hold to pause recording Last track exported - Export track to storage after recording finished - Set track export directory - defaultExportUri - not set yet + + Export track to storage after recording is finished + Instant post-workout export + + Track export directory diff --git a/src/main/res/xml/settings.xml b/src/main/res/xml/settings.xml index e9e68b596..8e23c55ab 100644 --- a/src/main/res/xml/settings.xml +++ b/src/main/res/xml/settings.xml @@ -52,11 +52,11 @@ limitations under the License. android:key="@string/stats_show_coordinate_key" android:title="@string/settings_stats_coordinate" /> @@ -151,22 +151,21 @@ limitations under the License. + + + - - -