forked from upstream-mirrors/OpenTracks
Change handling of Uri and DocumentFile handling for #1080
The Uris from Intent.getData() and DocumentFile.getUri() seems to be different. I think PreferencesUtils should only work with the original Uri as well as the permission handling logic. IntentUtils.toDocumentFile can be used to convert it to a DocumentFile later on to actually work with. Adds "No file manager found" message if no activity with OPEN_DOCUMENT_TREE is found. #1095 Removed deprecation of `startActivityForResult`.
This commit is contained in:
+36
-49
@@ -1,23 +1,34 @@
|
||||
package de.dennisguse.opentracks.io.file.importer;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.io.file.exporter.ExportActivity;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
||||
|
||||
private static final int DIRECTORY_PICKER_REQUEST_CODE = 6;
|
||||
protected final ActivityResultLauncher<Intent> directoryIntentLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {
|
||||
if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) {
|
||||
onActivityResultCustom(result.getData());
|
||||
}
|
||||
finish();
|
||||
});
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
@@ -26,9 +37,13 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||
DocumentFile directoryUri = configureDirectoryChooserIntent(intent);
|
||||
if (!isDirectoryValid(directoryUri)) {
|
||||
startActivityForResult(intent, DIRECTORY_PICKER_REQUEST_CODE);
|
||||
try {
|
||||
directoryIntentLauncher.launch(intent);
|
||||
} catch (final ActivityNotFoundException exception) {
|
||||
Toast.makeText(this, R.string.no_compatible_file_manager_installed, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
} else {
|
||||
startActivity(createNextActivityIntent(directoryUri.getUri())); //TODO Refactor to DocumentFile
|
||||
startActivity(createNextActivityIntent(directoryUri.getUri()));
|
||||
finish();
|
||||
}
|
||||
}
|
||||
@@ -37,25 +52,13 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
||||
return directoryUri != null && directoryUri.isDirectory() && directoryUri.canRead();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent resultData) {
|
||||
super.onActivityResult(requestCode, resultCode, resultData);
|
||||
onActivityResultCustom(requestCode, resultCode, resultData);
|
||||
}
|
||||
protected void onActivityResultCustom(@NonNull Intent resultData) {
|
||||
Uri directoryUri = resultData.getData();
|
||||
int takeFlags = resultData.getFlags();
|
||||
takeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
getContentResolver().takePersistableUriPermission(directoryUri, takeFlags);
|
||||
|
||||
protected void onActivityResultCustom(int requestCode, int resultCode, @Nullable Intent resultData) {
|
||||
if (requestCode == DIRECTORY_PICKER_REQUEST_CODE) {
|
||||
if (resultCode == Activity.RESULT_OK && resultData != null) {
|
||||
Uri directoryUri = resultData.getData();
|
||||
|
||||
int takeFlags = resultData.getFlags();
|
||||
takeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
getContentResolver().takePersistableUriPermission(directoryUri, takeFlags);
|
||||
|
||||
startActivity(createNextActivityIntent(directoryUri));
|
||||
}
|
||||
}
|
||||
finish();
|
||||
startActivity(createNextActivityIntent(directoryUri));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,8 +87,7 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
||||
protected DocumentFile configureDirectoryChooserIntent(Intent intent) {
|
||||
super.configureDirectoryChooserIntent(intent);
|
||||
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
|
||||
|
||||
return PreferencesUtils.getDefaultExportDirectoryUri(this);
|
||||
return IntentUtils.toDocumentFile(this, PreferencesUtils.getDefaultExportDirectoryUri());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -105,29 +107,15 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
||||
public static class DefaultTrackExportDirectoryChooserActivity extends DirectoryChooserActivity {
|
||||
|
||||
@Override
|
||||
protected void onActivityResultCustom(int requestCode, int resultCode, @Nullable Intent resultData) {
|
||||
if (requestCode == DIRECTORY_PICKER_REQUEST_CODE) {
|
||||
DocumentFile oldDirectoryUri = PreferencesUtils.getDefaultExportDirectoryUri(this);
|
||||
switch (resultCode) {
|
||||
case RESULT_OK:
|
||||
if (resultData != null) {
|
||||
Uri newDirectoryUri = resultData.getData();
|
||||
if (oldDirectoryUri != null && !newDirectoryUri.equals(oldDirectoryUri.getUri())) {
|
||||
IntentUtils.releaseDirectoryAccessPermission(getApplicationContext(), oldDirectoryUri);
|
||||
}
|
||||
|
||||
PreferencesUtils.setDefaultExportDirectoryUri(newDirectoryUri);
|
||||
IntentUtils.persistDirectoryAccessPermission(getApplicationContext(), newDirectoryUri, resultData.getFlags());
|
||||
}
|
||||
break;
|
||||
case RESULT_CANCELED:
|
||||
IntentUtils.releaseDirectoryAccessPermission(getApplicationContext(), oldDirectoryUri);
|
||||
PreferencesUtils.setDefaultExportDirectoryUri(null);
|
||||
break;
|
||||
}
|
||||
|
||||
protected void onActivityResultCustom(@NonNull Intent resultData) {
|
||||
Uri oldDirectoryUri = PreferencesUtils.getDefaultExportDirectoryUri();
|
||||
Uri newDirectoryUri = resultData.getData();
|
||||
if (oldDirectoryUri != null && !newDirectoryUri.equals(oldDirectoryUri)) {
|
||||
IntentUtils.releaseDirectoryAccessPermission(getApplicationContext(), oldDirectoryUri);
|
||||
}
|
||||
finish();
|
||||
|
||||
PreferencesUtils.setDefaultExportDirectoryUri(newDirectoryUri);
|
||||
IntentUtils.persistDirectoryAccessPermission(getApplicationContext(), newDirectoryUri, resultData.getFlags());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -139,9 +127,9 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
||||
protected DocumentFile configureDirectoryChooserIntent(Intent intent) {
|
||||
super.configureDirectoryChooserIntent(intent);
|
||||
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
|
||||
if (PreferencesUtils.isDefaultExportDirectoryUri(this)) {
|
||||
if (PreferencesUtils.isDefaultExportDirectoryUri()) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, PreferencesUtils.getDefaultExportDirectoryUri(this).getUri());
|
||||
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, PreferencesUtils.getDefaultExportDirectoryUri());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -149,7 +137,6 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected Intent createNextActivityIntent(Uri directoryUri) {
|
||||
finish();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package de.dennisguse.opentracks.settings;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.UriPermission;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
@@ -7,10 +10,12 @@ import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
|
||||
public class ImportExportSettingsFragment extends PreferenceFragmentCompat {
|
||||
|
||||
@@ -35,7 +40,7 @@ public class ImportExportSettingsFragment extends PreferenceFragmentCompat {
|
||||
setExportDirectorySummary();
|
||||
|
||||
Preference instantExportEnabledPreference = findPreference(getString(R.string.post_workout_export_enabled_key));
|
||||
instantExportEnabledPreference.setEnabled(PreferencesUtils.isDefaultExportDirectoryUri(getContext()));
|
||||
instantExportEnabledPreference.setEnabled(PreferencesUtils.isDefaultExportDirectoryUri());
|
||||
}
|
||||
|
||||
private void setExportTrackFileFormatOptions() {
|
||||
@@ -64,13 +69,14 @@ public class ImportExportSettingsFragment extends PreferenceFragmentCompat {
|
||||
private void setExportDirectorySummary() {
|
||||
Preference instantExportDirectoryPreference = findPreference(getString(R.string.settings_default_export_directory_key));
|
||||
instantExportDirectoryPreference.setSummaryProvider(preference -> {
|
||||
DocumentFile directory = PreferencesUtils.getDefaultExportDirectoryUri(getContext());
|
||||
Uri directoryUri = PreferencesUtils.getDefaultExportDirectoryUri();
|
||||
DocumentFile directory = IntentUtils.toDocumentFile(getContext(), directoryUri);
|
||||
//Use same value for not set as Androidx ListPreference and EditTextPreference
|
||||
if (directory == null) {
|
||||
return getString(R.string.not_set);
|
||||
}
|
||||
|
||||
return directory.getUri().toString() + (directory.canWrite() ? "" : getString(R.string.export_dir_not_writable));
|
||||
return directoryUri.toString() + (directory.canWrite() ? "" : getString(R.string.export_dir_not_writable));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,9 +497,9 @@ public class PreferencesUtils {
|
||||
}
|
||||
|
||||
|
||||
public static boolean shouldInstantExportAfterWorkout(Context context) {
|
||||
public static boolean shouldInstantExportAfterWorkout() {
|
||||
final boolean INSTANT_POST_WORKOUT_EXPORT_DEFAULT = resources.getBoolean(R.bool.post_workout_export_enabled_default);
|
||||
return getBoolean(R.string.post_workout_export_enabled_key, INSTANT_POST_WORKOUT_EXPORT_DEFAULT) && isDefaultExportDirectoryUri(context);
|
||||
return getBoolean(R.string.post_workout_export_enabled_key, INSTANT_POST_WORKOUT_EXPORT_DEFAULT) && isDefaultExportDirectoryUri();
|
||||
}
|
||||
|
||||
public static TrackFileFormat getExportTrackFileFormat() {
|
||||
@@ -535,26 +535,29 @@ public class PreferencesUtils {
|
||||
PreferenceManager.setDefaultValues(context, R.xml.settings, readAgain);
|
||||
}
|
||||
|
||||
public static DocumentFile getDefaultExportDirectoryUri(Context context) {
|
||||
public static Uri getDefaultExportDirectoryUri() {
|
||||
String singleExportDirectory = getString(R.string.settings_default_export_directory_key, null);
|
||||
if (singleExportDirectory == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return DocumentFile.fromTreeUri(context, Uri.parse(singleExportDirectory));
|
||||
Log.d(TAG, "DefaultExportDirectoryUri: " + singleExportDirectory);
|
||||
return Uri.parse(singleExportDirectory);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Could not decode default export directory: " + e.getMessage());
|
||||
Log.w(TAG, "Could not parse default export directory Uri: " + e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void setDefaultExportDirectoryUri(Uri directoryUri) {
|
||||
String value = directoryUri != null ? directoryUri.toString() : null;
|
||||
Log.d(TAG, "Set ExportDirectoryUri: " + directoryUri);
|
||||
|
||||
setString(R.string.settings_default_export_directory_key, value);
|
||||
}
|
||||
|
||||
public static boolean isDefaultExportDirectoryUri(Context context) {
|
||||
return getDefaultExportDirectoryUri(context) != null;
|
||||
public static boolean isDefaultExportDirectoryUri() {
|
||||
return getDefaultExportDirectoryUri() != null;
|
||||
}
|
||||
|
||||
public static int getLayoutColumnsByDefault() {
|
||||
|
||||
@@ -31,9 +31,9 @@ public class ExportUtils {
|
||||
private static final String TAG = ExportUtils.class.getSimpleName();
|
||||
|
||||
public static void postWorkoutExport(Context context, Track.Id trackId, ExportServiceResultReceiver resultReceiver) {
|
||||
if (PreferencesUtils.shouldInstantExportAfterWorkout(context)) {
|
||||
if (PreferencesUtils.shouldInstantExportAfterWorkout()) {
|
||||
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat();
|
||||
DocumentFile directory = PreferencesUtils.getDefaultExportDirectoryUri(context);
|
||||
DocumentFile directory = IntentUtils.toDocumentFile(context, PreferencesUtils.getDefaultExportDirectoryUri());
|
||||
|
||||
if (directory == null || !directory.canWrite()) {
|
||||
Toast.makeText(context, R.string.export_cannot_write_to_dir, Toast.LENGTH_LONG).show();
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.UriPermission;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
|
||||
@@ -79,15 +80,27 @@ public class IntentUtils {
|
||||
context.getContentResolver().takePersistableUriPermission(directoryUri, newFlags);
|
||||
}
|
||||
|
||||
public static void releaseDirectoryAccessPermission(Context context, final DocumentFile documentFile) {
|
||||
if (documentFile == null) {
|
||||
public static void releaseDirectoryAccessPermission(Context context, final Uri documentUri) {
|
||||
if (documentUri == null) {
|
||||
return;
|
||||
}
|
||||
final Uri documentUri = documentFile.getUri();
|
||||
|
||||
context.getContentResolver().getPersistedUriPermissions().stream()
|
||||
.map(UriPermission::getUri)
|
||||
.filter(documentUri::equals)
|
||||
.forEach(u -> context.getContentResolver().releasePersistableUriPermission(u, 0));
|
||||
}
|
||||
|
||||
public static DocumentFile toDocumentFile(Context context, Uri directoryUri) {
|
||||
if (directoryUri == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return DocumentFile.fromTreeUri(context, directoryUri);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Could not decode directory: " + e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -639,4 +639,5 @@ limitations under the License.
|
||||
<string name="field_set_primary">Primary</string>
|
||||
<string name="field_set_secondary">Secondary</string>
|
||||
<string name="field_remove_from_layout">Remove</string>
|
||||
<string name="no_compatible_file_manager_installed">No compatible file manager installed</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user