From bb7efaa87ca43a8cbe0ffa0f8cd4eeb4f1647bc2 Mon Sep 17 00:00:00 2001 From: Peter Storch Date: Thu, 25 Nov 2021 20:55:46 +0100 Subject: [PATCH] fix #1025 exportdir failed to create document fix #254 illegal filename characters --- .../provider/ShareContentProvider.java | 1 + .../io/file/exporter/ExportService.java | 15 ++++++++--- .../importer/DirectoryChooserActivity.java | 26 ++++++++++++++++--- .../opentracks/util/ExportUtils.java | 2 +- .../dennisguse/opentracks/util/FileUtils.java | 1 - .../opentracks/util/IntentUtils.java | 11 ++++---- 6 files changed, 41 insertions(+), 15 deletions(-) diff --git a/src/main/java/de/dennisguse/opentracks/content/provider/ShareContentProvider.java b/src/main/java/de/dennisguse/opentracks/content/provider/ShareContentProvider.java index a38120234..e260fd8f4 100644 --- a/src/main/java/de/dennisguse/opentracks/content/provider/ShareContentProvider.java +++ b/src/main/java/de/dennisguse/opentracks/content/provider/ShareContentProvider.java @@ -29,6 +29,7 @@ import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.TracksColumns; import de.dennisguse.opentracks.io.file.TrackFileFormat; import de.dennisguse.opentracks.io.file.exporter.TrackExporter; +import de.dennisguse.opentracks.util.FileUtils; /** * A content provider that mimics the behavior of {@link androidx.core.content.FileProvider}, which shares virtual (non-existing) KML-files. diff --git a/src/main/java/de/dennisguse/opentracks/io/file/exporter/ExportService.java b/src/main/java/de/dennisguse/opentracks/io/file/exporter/ExportService.java index 54a7a122f..14861cfb0 100644 --- a/src/main/java/de/dennisguse/opentracks/io/file/exporter/ExportService.java +++ b/src/main/java/de/dennisguse/opentracks/io/file/exporter/ExportService.java @@ -6,6 +6,7 @@ import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.os.ResultReceiver; +import android.util.Log; import androidx.annotation.NonNull; import androidx.core.app.JobIntentService; @@ -24,6 +25,7 @@ public class ExportService extends JobIntentService { private static final String EXTRA_TRACK_ID = "extra_track_id"; private static final String EXTRA_TRACK_FILE_FORMAT = "extra_track_file_format"; private static final String EXTRA_DIRECTORY_URI = "extra_directory_uri"; + private static final String TAG = ExportService.class.getSimpleName(); public static void enqueue(Context context, ExportServiceResultReceiver receiver, Track.Id trackId, TrackFileFormat trackFileFormat, Uri directoryUri) { Intent intent = new Intent(context, JobService.class); @@ -42,18 +44,23 @@ public class ExportService extends JobIntentService { TrackFileFormat trackFileFormat = (TrackFileFormat) intent.getSerializableExtra(EXTRA_TRACK_FILE_FORMAT); Uri directoryUri = intent.getParcelableExtra(EXTRA_DIRECTORY_URI); + // Prepare resultCode and bundle to send to the receiver. + Bundle bundle = new Bundle(); + bundle.putParcelable(ExportServiceResultReceiver.RESULT_EXTRA_TRACK_ID, trackId); + // Build directory file. DocumentFile directoryFile = DocumentFile.fromTreeUri(this, directoryUri); + if (directoryFile == null || !directoryFile.canWrite()) { + Log.e(TAG, "Can't write to directory: " + directoryFile); + resultReceiver.send(ExportServiceResultReceiver.RESULT_CODE_ERROR, bundle); + return; + } // Export. ContentProviderUtils contentProviderUtils = new ContentProviderUtils(this); Track track = contentProviderUtils.getTrack(trackId); boolean success = ExportUtils.exportTrack(this, trackFileFormat, directoryFile, track); - // Prepare resultCode and bundle to send to the receiver. - Bundle bundle = new Bundle(); - bundle.putParcelable(ExportServiceResultReceiver.RESULT_EXTRA_TRACK_ID, trackId); - // Send result to the receiver. int resultCode = success ? ExportServiceResultReceiver.RESULT_CODE_SUCCESS : ExportServiceResultReceiver.RESULT_CODE_ERROR; resultReceiver.send(resultCode, bundle); 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 be5cfc48c..f4a28d31e 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 @@ -3,10 +3,12 @@ package de.dennisguse.opentracks.io.file.importer; import android.app.Activity; import android.content.Intent; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.provider.DocumentsContract; import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; import androidx.documentfile.provider.DocumentFile; @@ -24,7 +26,7 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); DocumentFile directoryUri = configureDirectoryChooserIntent(intent); - if (directoryUri == null) { + if (!isDirectoryValid(directoryUri)) { startActivityForResult(intent, DIRECTORY_PICKER_REQUEST_CODE); } else { startActivity(createNextActivityIntent(directoryUri.getUri())); //TODO Refactor to DocumentFile @@ -32,6 +34,10 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity { } } + protected boolean isDirectoryValid(final DocumentFile directoryUri) { + return directoryUri != null && directoryUri.isDirectory() && directoryUri.canRead(); + } + @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent resultData) { super.onActivityResult(requestCode, resultCode, resultData); @@ -83,6 +89,11 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity { return PreferencesUtils.getDefaultExportDirectoryUri(this); } + @Override + protected boolean isDirectoryValid(final DocumentFile directoryUri) { + return super.isDirectoryValid(directoryUri) && directoryUri.canWrite(); + } + @Override protected Intent createNextActivityIntent(Uri directoryUri) { Intent intent = IntentUtils.newIntent(this, ExportActivity.class); @@ -102,7 +113,7 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity { Uri directoryUri = resultData.getData(); PreferencesUtils.setDefaultExportDirectoryUri(directoryUri); - IntentUtils.persistDirectoryAccessPermission(getApplicationContext(), directoryUri); + IntentUtils.persistDirectoryAccessPermission(getApplicationContext(), directoryUri, resultData.getFlags()); break; case RESULT_CANCELED: PreferencesUtils.setDefaultExportDirectoryUri(null); @@ -114,12 +125,19 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity { finish(); } + @Override + protected boolean isDirectoryValid(final DocumentFile directoryUri) { + return super.isDirectoryValid(directoryUri) && directoryUri.canWrite(); + } + @Override protected DocumentFile configureDirectoryChooserIntent(Intent intent) { super.configureDirectoryChooserIntent(intent); - intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); if (PreferencesUtils.isDefaultExportDirectoryUri(this)) { - intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, PreferencesUtils.getDefaultExportDirectoryUri(this).getUri()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, PreferencesUtils.getDefaultExportDirectoryUri(this).getUri()); + } } return null; } diff --git a/src/main/java/de/dennisguse/opentracks/util/ExportUtils.java b/src/main/java/de/dennisguse/opentracks/util/ExportUtils.java index 9c5ca2539..a682bdc52 100644 --- a/src/main/java/de/dennisguse/opentracks/util/ExportUtils.java +++ b/src/main/java/de/dennisguse/opentracks/util/ExportUtils.java @@ -100,7 +100,7 @@ public class ExportUtils { } private static String getExportFileNameForTrack(Track track, String trackFileFormatExtension) { - return track.getUuid().toString().substring(0, 8) + "_" + track.getName() + "." + trackFileFormatExtension; + return track.getUuid().toString().substring(0, 8) + "_" + FileUtils.sanitizeFileName(track.getName()) + "." + trackFileFormatExtension; } private static Uri findFile(Context context, Uri directoryUri, String exportFileName) { diff --git a/src/main/java/de/dennisguse/opentracks/util/FileUtils.java b/src/main/java/de/dennisguse/opentracks/util/FileUtils.java index 66390e5c5..cdedc96fd 100644 --- a/src/main/java/de/dennisguse/opentracks/util/FileUtils.java +++ b/src/main/java/de/dennisguse/opentracks/util/FileUtils.java @@ -149,7 +149,6 @@ public class FileUtils { * * @param name name */ - // TODO Check if this function is still needed. public static String sanitizeFileName(String name) { StringBuilder builder = new StringBuilder(name.length()); for (int i = 0; i < name.length(); i++) { diff --git a/src/main/java/de/dennisguse/opentracks/util/IntentUtils.java b/src/main/java/de/dennisguse/opentracks/util/IntentUtils.java index f62ec3ee7..762f4346c 100644 --- a/src/main/java/de/dennisguse/opentracks/util/IntentUtils.java +++ b/src/main/java/de/dennisguse/opentracks/util/IntentUtils.java @@ -92,8 +92,9 @@ public class IntentUtils { continue; } - Pair uriTrackFile = ShareContentProvider.createURI(trackId, track.getName(), PreferencesUtils.getExportTrackFileFormat()); - Pair uriSharePicture = ShareContentProvider.createURI(trackId, track.getName(), TrackFileFormat.SHARE_PICTURE_PNG); + String trackName = FileUtils.sanitizeFileName(track.getName()); + Pair uriTrackFile = ShareContentProvider.createURI(trackId, trackName, PreferencesUtils.getExportTrackFileFormat()); + Pair uriSharePicture = ShareContentProvider.createURI(trackId, trackName, TrackFileFormat.SHARE_PICTURE_PNG); uris.addAll(Arrays.asList(uriSharePicture.first, uriTrackFile.first)); } @@ -203,8 +204,8 @@ public class IntentUtils { return new Pair<>(intent, photoUri); } - public static void persistDirectoryAccessPermission(Context context, Uri directoryUri) { - int flags = Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION; - context.getContentResolver().takePersistableUriPermission(directoryUri, flags); + public static void persistDirectoryAccessPermission(Context context, Uri directoryUri, int existingFlags) { + int newFlags = existingFlags | (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + context.getContentResolver().takePersistableUriPermission(directoryUri, newFlags); } }