forked from upstream-mirrors/OpenTracks
fix #1025 exportdir failed to create document
fix #254 illegal filename characters
This commit is contained in:
@@ -29,6 +29,7 @@ import de.dennisguse.opentracks.content.data.Track;
|
|||||||
import de.dennisguse.opentracks.content.data.TracksColumns;
|
import de.dennisguse.opentracks.content.data.TracksColumns;
|
||||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||||
import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
|
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.
|
* A content provider that mimics the behavior of {@link androidx.core.content.FileProvider}, which shares virtual (non-existing) KML-files.
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import android.content.Intent;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.ResultReceiver;
|
import android.os.ResultReceiver;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.core.app.JobIntentService;
|
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_ID = "extra_track_id";
|
||||||
private static final String EXTRA_TRACK_FILE_FORMAT = "extra_track_file_format";
|
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 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) {
|
public static void enqueue(Context context, ExportServiceResultReceiver receiver, Track.Id trackId, TrackFileFormat trackFileFormat, Uri directoryUri) {
|
||||||
Intent intent = new Intent(context, JobService.class);
|
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);
|
TrackFileFormat trackFileFormat = (TrackFileFormat) intent.getSerializableExtra(EXTRA_TRACK_FILE_FORMAT);
|
||||||
Uri directoryUri = intent.getParcelableExtra(EXTRA_DIRECTORY_URI);
|
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.
|
// Build directory file.
|
||||||
DocumentFile directoryFile = DocumentFile.fromTreeUri(this, directoryUri);
|
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.
|
// Export.
|
||||||
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(this);
|
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(this);
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
boolean success = ExportUtils.exportTrack(this, trackFileFormat, directoryFile, track);
|
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.
|
// Send result to the receiver.
|
||||||
int resultCode = success ? ExportServiceResultReceiver.RESULT_CODE_SUCCESS : ExportServiceResultReceiver.RESULT_CODE_ERROR;
|
int resultCode = success ? ExportServiceResultReceiver.RESULT_CODE_SUCCESS : ExportServiceResultReceiver.RESULT_CODE_ERROR;
|
||||||
resultReceiver.send(resultCode, bundle);
|
resultReceiver.send(resultCode, bundle);
|
||||||
|
|||||||
+22
-4
@@ -3,10 +3,12 @@ package de.dennisguse.opentracks.io.file.importer;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.provider.DocumentsContract;
|
import android.provider.DocumentsContract;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.documentfile.provider.DocumentFile;
|
import androidx.documentfile.provider.DocumentFile;
|
||||||
|
|
||||||
@@ -24,7 +26,7 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||||
DocumentFile directoryUri = configureDirectoryChooserIntent(intent);
|
DocumentFile directoryUri = configureDirectoryChooserIntent(intent);
|
||||||
if (directoryUri == null) {
|
if (!isDirectoryValid(directoryUri)) {
|
||||||
startActivityForResult(intent, DIRECTORY_PICKER_REQUEST_CODE);
|
startActivityForResult(intent, DIRECTORY_PICKER_REQUEST_CODE);
|
||||||
} else {
|
} else {
|
||||||
startActivity(createNextActivityIntent(directoryUri.getUri())); //TODO Refactor to DocumentFile
|
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
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent resultData) {
|
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent resultData) {
|
||||||
super.onActivityResult(requestCode, resultCode, resultData);
|
super.onActivityResult(requestCode, resultCode, resultData);
|
||||||
@@ -83,6 +89,11 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
|||||||
return PreferencesUtils.getDefaultExportDirectoryUri(this);
|
return PreferencesUtils.getDefaultExportDirectoryUri(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isDirectoryValid(final DocumentFile directoryUri) {
|
||||||
|
return super.isDirectoryValid(directoryUri) && directoryUri.canWrite();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Intent createNextActivityIntent(Uri directoryUri) {
|
protected Intent createNextActivityIntent(Uri directoryUri) {
|
||||||
Intent intent = IntentUtils.newIntent(this, ExportActivity.class);
|
Intent intent = IntentUtils.newIntent(this, ExportActivity.class);
|
||||||
@@ -102,7 +113,7 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
|||||||
Uri directoryUri = resultData.getData();
|
Uri directoryUri = resultData.getData();
|
||||||
|
|
||||||
PreferencesUtils.setDefaultExportDirectoryUri(directoryUri);
|
PreferencesUtils.setDefaultExportDirectoryUri(directoryUri);
|
||||||
IntentUtils.persistDirectoryAccessPermission(getApplicationContext(), directoryUri);
|
IntentUtils.persistDirectoryAccessPermission(getApplicationContext(), directoryUri, resultData.getFlags());
|
||||||
break;
|
break;
|
||||||
case RESULT_CANCELED:
|
case RESULT_CANCELED:
|
||||||
PreferencesUtils.setDefaultExportDirectoryUri(null);
|
PreferencesUtils.setDefaultExportDirectoryUri(null);
|
||||||
@@ -114,12 +125,19 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
|||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isDirectoryValid(final DocumentFile directoryUri) {
|
||||||
|
return super.isDirectoryValid(directoryUri) && directoryUri.canWrite();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected DocumentFile configureDirectoryChooserIntent(Intent intent) {
|
protected DocumentFile configureDirectoryChooserIntent(Intent intent) {
|
||||||
super.configureDirectoryChooserIntent(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)) {
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ public class ExportUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String getExportFileNameForTrack(Track track, String trackFileFormatExtension) {
|
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) {
|
private static Uri findFile(Context context, Uri directoryUri, String exportFileName) {
|
||||||
|
|||||||
@@ -149,7 +149,6 @@ public class FileUtils {
|
|||||||
*
|
*
|
||||||
* @param name name
|
* @param name name
|
||||||
*/
|
*/
|
||||||
// TODO Check if this function is still needed.
|
|
||||||
public static String sanitizeFileName(String name) {
|
public static String sanitizeFileName(String name) {
|
||||||
StringBuilder builder = new StringBuilder(name.length());
|
StringBuilder builder = new StringBuilder(name.length());
|
||||||
for (int i = 0; i < name.length(); i++) {
|
for (int i = 0; i < name.length(); i++) {
|
||||||
|
|||||||
@@ -92,8 +92,9 @@ public class IntentUtils {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<Uri, String> uriTrackFile = ShareContentProvider.createURI(trackId, track.getName(), PreferencesUtils.getExportTrackFileFormat());
|
String trackName = FileUtils.sanitizeFileName(track.getName());
|
||||||
Pair<Uri, String> uriSharePicture = ShareContentProvider.createURI(trackId, track.getName(), TrackFileFormat.SHARE_PICTURE_PNG);
|
Pair<Uri, String> uriTrackFile = ShareContentProvider.createURI(trackId, trackName, PreferencesUtils.getExportTrackFileFormat());
|
||||||
|
Pair<Uri, String> uriSharePicture = ShareContentProvider.createURI(trackId, trackName, TrackFileFormat.SHARE_PICTURE_PNG);
|
||||||
|
|
||||||
uris.addAll(Arrays.asList(uriSharePicture.first, uriTrackFile.first));
|
uris.addAll(Arrays.asList(uriSharePicture.first, uriTrackFile.first));
|
||||||
}
|
}
|
||||||
@@ -203,8 +204,8 @@ public class IntentUtils {
|
|||||||
return new Pair<>(intent, photoUri);
|
return new Pair<>(intent, photoUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void persistDirectoryAccessPermission(Context context, Uri directoryUri) {
|
public static void persistDirectoryAccessPermission(Context context, Uri directoryUri, int existingFlags) {
|
||||||
int flags = Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
|
int newFlags = existingFlags | (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||||
context.getContentResolver().takePersistableUriPermission(directoryUri, flags);
|
context.getContentResolver().takePersistableUriPermission(directoryUri, newFlags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user