From 78b50e82faa6f011e92691b7981d27a482d1953e Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 11 Feb 2018 14:32:21 +0100 Subject: [PATCH] Fix crash in settings activity with export location The way this works will never be a fully working solution, because uris are *not supposed* to be resolved to a filesystem path. So while this may work right now, it will most probably fail in the future, with other content providers. See e.g. https://github.com/iPaulPro/aFileChooser/issues where this code originally came from (via Stackoverflow). We won't crash anymore, but we won't support certain content providers. In the long run, we should search for the proper solution. Closes #982 --- .../activities/SettingsActivity.java | 24 ++++----- .../gadgetbridge/util/AndroidUtils.java | 52 ++++++++++++++----- 2 files changed, 50 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java index 8316564bc..2cbc63ddc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java @@ -377,7 +377,7 @@ public class SettingsActivity extends AbstractSettingsActivity { } /* - Either returns the file path of the selected document, or the display name + Either returns the file path of the selected document, or the display name, or an empty string */ private String getAutoExportLocationSummary() { String autoExportLocation = GBApplication.getPrefs().getString(GBPrefs.AUTO_EXPORT_LOCATION, null); @@ -386,20 +386,16 @@ public class SettingsActivity extends AbstractSettingsActivity { } Uri uri = Uri.parse(autoExportLocation); try { - String filePath = AndroidUtils.getFilePath(getApplicationContext(), uri); - if (filePath != null) { - return filePath; + return AndroidUtils.getFilePath(getApplicationContext(), uri); + } catch (IllegalArgumentException e) { + Cursor cursor = getContentResolver().query( + uri, + new String[] { DocumentsContract.Document.COLUMN_DISPLAY_NAME }, + null, null, null, null + ); + if (cursor != null && cursor.moveToFirst()) { + return cursor.getString(cursor.getColumnIndex(DocumentsContract.Document.COLUMN_DISPLAY_NAME)); } - } catch (URISyntaxException e) { - return ""; - } - Cursor cursor = getContentResolver().query( - uri, - new String[] { DocumentsContract.Document.COLUMN_DISPLAY_NAME }, - null, null, null, null - ); - if (cursor != null && cursor.moveToFirst()) { - return cursor.getString(cursor.getColumnIndex(DocumentsContract.Document.COLUMN_DISPLAY_NAME)); } return ""; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java index 794f82652..48fa8c3ff 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java @@ -30,7 +30,10 @@ import android.os.ParcelUuid; import android.os.Parcelable; import android.provider.DocumentsContract; import android.provider.MediaStore; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.v4.content.LocalBroadcastManager; +import android.text.TextUtils; import java.net.URISyntaxException; import java.util.Locale; @@ -121,6 +124,28 @@ public class AndroidUtils { + Integer.toHexString(Color.blue(color)); } + /** + * As seen on stackoverflow https://stackoverflow.com/a/36714242/1207186 + * Try to find the file path of a document uri + * @param context the application context + * @param uri the Uri for which the path should be resolved + * @return the path corresponding to the Uri as a String + * @throws IllegalArgumentException on any problem decoding the uri to a path + */ + public static @NonNull String getFilePath(@NonNull Context context, @NonNull Uri uri) throws IllegalArgumentException { + try { + String path = internalGetFilePath(context, uri); + if (TextUtils.isEmpty(path)) { + throw new IllegalArgumentException("Unable to decode the given uri to a file path: " + uri); + } + return path; + } catch (IllegalArgumentException ex) { + throw ex; + } catch (Exception ex) { + throw new IllegalArgumentException("Unable to decode the given uri to a file path: " + uri, ex); + } + } + /** * As seen on stackoverflow https://stackoverflow.com/a/36714242/1207186 * Try to find the file path of a document uri @@ -129,9 +154,10 @@ public class AndroidUtils { * @return the path corresponding to the Uri as a String * @throws URISyntaxException */ - public static String getFilePath(Context context, Uri uri) throws URISyntaxException { + private static @Nullable String internalGetFilePath(@NonNull Context context, @NonNull Uri uri) throws URISyntaxException { String selection = null; String[] selectionArgs = null; + // Uri is different in versions after KITKAT (Android 4.4), we need to if (Build.VERSION.SDK_INT >= 19 && DocumentsContract.isDocumentUri(context.getApplicationContext(), uri)) { if ("com.android.externalstorage.documents".equals(uri.getAuthority())) { @@ -140,8 +166,13 @@ public class AndroidUtils { return Environment.getExternalStorageDirectory() + "/" + split[1]; } else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) { final String id = DocumentsContract.getDocumentId(uri); - uri = ContentUris.withAppendedId( - Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); + if (!TextUtils.isEmpty(id)) { + if (id.startsWith("raw:")) { + return id.replaceFirst("raw:", ""); + } + uri = ContentUris.withAppendedId( + Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); + } } else if ("com.android.providers.media.documents".equals(uri.getAuthority())) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); @@ -164,18 +195,15 @@ public class AndroidUtils { MediaStore.Images.Media.DATA }; Cursor cursor = null; - try { - cursor = context.getContentResolver() - .query(uri, projection, selection, selectionArgs, null); - int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); - if (cursor.moveToFirst()) { - return cursor.getString(column_index); - } - } catch (Exception e) { + cursor = context.getContentResolver() + .query(uri, projection, selection, selectionArgs, null); + int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); + if (cursor.moveToFirst()) { + return cursor.getString(column_index); } } else if ("file".equalsIgnoreCase(uri.getScheme())) { return uri.getPath(); } - return null; + throw new IllegalArgumentException("Unable to decode the given uri to a file path: " + uri); } }