Android Gallery on Android 4.4 (KitKat) returns different URI for Intent.ACTION_GET_CONTENT

后端 未结 19 2263
温柔的废话
温柔的废话 2020-11-22 02:44

Before KitKat (or before the new Gallery) the Intent.ACTION_GET_CONTENT returned a URI like this

content://media/external/images/media/39

相关标签:
19条回答
  • 2020-11-22 03:26

    Please try to avoid using takePersistableUriPermission method because it raised runtime exception for me. /** * Select from gallery. */

    public void selectFromGallery() {
        if (Build.VERSION.SDK_INT < AppConstants.KITKAT_API_VERSION) {
    
            Intent intent = new Intent(); 
            intent.setType("image/*");
            intent.setAction(Intent.ACTION_GET_CONTENT);
            ((Activity)mCalledContext).startActivityForResult(intent,AppConstants.GALLERY_INTENT_CALLED);
    
        } else {
    
            Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
            intent.setType("image/*");
            ((Activity)mCalledContext).startActivityForResult(intent, AppConstants.GALLERY_AFTER_KITKAT_INTENT_CALLED);
        }
    }
    

    OnActivity for result to handle the image data:

    @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        //gallery intent result handling before kit-kat version
        if(requestCode==AppConstants.GALLERY_INTENT_CALLED 
                && resultCode == RESULT_OK) {
    
            Uri selectedImage = data.getData();
            String[] filePathColumn = {MediaStore.Images.Media.DATA};
            Cursor cursor = getContentResolver().query(selectedImage,filePathColumn, null, null, null);
            cursor.moveToFirst();
            int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
            String filePath = cursor.getString(columnIndex);
            cursor.close();
            photoFile = new File(filePath);
            mImgCropping.startCropImage(photoFile,AppConstants.REQUEST_IMAGE_CROP);
    
        }
        //gallery intent result handling after kit-kat version
        else if (requestCode == AppConstants.GALLERY_AFTER_KITKAT_INTENT_CALLED 
                && resultCode == RESULT_OK) {
    
            Uri selectedImage = data.getData();
            InputStream input = null;
            OutputStream output = null;
    
            try {
                //converting the input stream into file to crop the 
                //selected image from sd-card.
                input = getApplicationContext().getContentResolver().openInputStream(selectedImage);
                try {
                    photoFile = mImgCropping.createImageFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }catch(Exception e) {
                    e.printStackTrace();
                }
                output = new FileOutputStream(photoFile);
    
                int read = 0;
                byte[] bytes = new byte[1024];
    
                while ((read = input.read(bytes)) != -1) {
                    try {
                        output.write(bytes, 0, read);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
    
    
        }
    
    0 讨论(0)
  • 2020-11-22 03:28

    @paul burke's answer works fine for both camera and gallery pictures for API level 19 and above, but it doesn't work if your Android project's minimum SDK is set to below 19, and some answers referring above doesn't work for both gallery and camera. Well, I have modified @paul burke's code which works for API level below 19. Below is the code.

    public static String getPath(final Context context, final Uri uri) {
    
        final boolean isKitKat = Build.VERSION.SDK_INT >=
                                 Build.VERSION_CODES.KITKAT;
        Log.i("URI",uri+"");
        String result = uri+"";
    
        // DocumentProvider
        // if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
        if (isKitKat && (result.contains("media.documents"))) {
    
            String[] ary = result.split("/");
            int length = ary.length;
            String imgary = ary[length-1];
            final String[] dat = imgary.split("%3A");
    
            final String docId = dat[1];
            final String type = dat[0];
    
            Uri contentUri = null;
            if ("image".equals(type)) {
                contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
            }
            else if ("video".equals(type)) {
            }
            else if ("audio".equals(type)) {
            }
    
            final String selection = "_id=?";
            final String[] selectionArgs = new String[] {
                dat[1]
            };
    
            return getDataColumn(context, contentUri, selection, selectionArgs);
        }
        else
        if ("content".equalsIgnoreCase(uri.getScheme())) {
            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }
    
        return null;
    }
    
    public static String getDataColumn(Context context, Uri uri, String selection,
                                       String[] selectionArgs) {
        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = {
                column
        };
    
        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                    null);
            if (cursor != null && cursor.moveToFirst()) {
                final int column_index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(column_index);
            }
        }
        finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }
    
    0 讨论(0)
  • 2020-11-22 03:30

    Just as Commonsware mentioned, you shouldn't assume, that the stream you get via ContentResolver is convertable into file.

    What you really should do is to open the InputStream from the ContentProvider, then create a Bitmap out of it. And it works on 4.4 and earlier versions as well, no need for reflection.

        //cxt -> current context
    
        InputStream input;
        Bitmap bmp;
        try {
            input = cxt.getContentResolver().openInputStream(fileUri);
            bmp = BitmapFactory.decodeStream(input);
        } catch (FileNotFoundException e1) {
    
        }
    

    Of course if you handle big images, you should load them with appropriate inSampleSize: http://developer.android.com/training/displaying-bitmaps/load-bitmap.html. But that's another topic.

    0 讨论(0)
  • 2020-11-22 03:31

    This Android library handles the case changes in KitKat(including the oldere versions - 2.1+):
    https://github.com/iPaulPro/aFileChooser

    Use the String path = FileUtils.getPath(context, uri) to convert the returned Uri to a path string useable on all OS version. See more about it here: https://stackoverflow.com/a/20559175/860488

    0 讨论(0)
  • 2020-11-22 03:32

    This requires no special permissions, and works with the Storage Access Framework, as well as the unofficial ContentProvider pattern (file path in _data field).

    /**
     * Get a file path from a Uri. This will get the the path for Storage Access
     * Framework Documents, as well as the _data field for the MediaStore and
     * other file-based ContentProviders.
     *
     * @param context The context.
     * @param uri The Uri to query.
     * @author paulburke
     */
    public static String getPath(final Context context, final Uri uri) {
    
        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
    
        // DocumentProvider
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
            // ExternalStorageProvider
            if (isExternalStorageDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];
    
                if ("primary".equalsIgnoreCase(type)) {
                    return Environment.getExternalStorageDirectory() + "/" + split[1];
                }
    
                // TODO handle non-primary volumes
            }
            // DownloadsProvider
            else if (isDownloadsDocument(uri)) {
    
                final String id = DocumentsContract.getDocumentId(uri);
                final Uri contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
    
                return getDataColumn(context, contentUri, null, null);
            }
            // MediaProvider
            else if (isMediaDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];
    
                Uri contentUri = null;
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                }
    
                final String selection = "_id=?";
                final String[] selectionArgs = new String[] {
                        split[1]
                };
    
                return getDataColumn(context, contentUri, selection, selectionArgs);
            }
        }
        // MediaStore (and general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {
    
            // Return the remote address
            if (isGooglePhotosUri(uri))
                return uri.getLastPathSegment();
    
            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }
    
        return null;
    }
    
    /**
     * Get the value of the data column for this Uri. This is useful for
     * MediaStore Uris, and other file-based ContentProviders.
     *
     * @param context The context.
     * @param uri The Uri to query.
     * @param selection (Optional) Filter used in the query.
     * @param selectionArgs (Optional) Selection arguments used in the query.
     * @return The value of the _data column, which is typically a file path.
     */
    public static String getDataColumn(Context context, Uri uri, String selection,
            String[] selectionArgs) {
    
        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = {
                column
        };
    
        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                    null);
            if (cursor != null && cursor.moveToFirst()) {
                final int index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(index);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }
    
    
    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is ExternalStorageProvider.
     */
    public static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }
    
    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is DownloadsProvider.
     */
    public static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }
    
    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is MediaProvider.
     */
    public static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }
    
    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is Google Photos.
     */
    public static boolean isGooglePhotosUri(Uri uri) {
        return "com.google.android.apps.photos.content".equals(uri.getAuthority());
    }
    

    See an up-to-date version of this method here.

    0 讨论(0)
  • 2020-11-22 03:32

    Building up on Paul Burke's answer I faced many problems resolving external SD card's URI path as most of the suggested "built-in" functions return paths which do not get resolved to files.

    However, this is my approach of his // TODO handle non-primary volumes.

    String resolvedPath = "";
    File[] possibleExtSdComposites = context.getExternalFilesDirs(null);
    for (File f : possibleExtSdComposites) {
        // Reset final path
        resolvedPath = "";
    
        // Construct list of folders
        ArrayList<String> extSdSplit = new ArrayList<>(Arrays.asList(f.getPath().split("/")));
    
        // Look for folder "<your_application_id>"
        int idx = extSdSplit.indexOf(BuildConfig.APPLICATION_ID);
    
        // ASSUMPTION: Expected to be found at depth 2 (in this case ExtSdCard's root is /storage/0000-0000/) - e.g. /storage/0000-0000/Android/data/<your_application_id>/files
        ArrayList<String> hierarchyList = new ArrayList<>(extSdSplit.subList(0, idx - 2));
    
        // Construct list containing full possible path to the file
        hierarchyList.add(tail);
        String possibleFilePath = TextUtils.join("/", hierarchyList);
    
        // If file is found --> success
        if (idx != -1 && new File(possibleFilePath).exists()) {
            resolvedPath = possibleFilePath;
            break;
        }
    }
    
    if (!resolvedPath.equals("")) {
        return resolvedPath;
    } else {
        return null;
    }
    

    Note it depends on hierarchy which might be different on every phone manufacturer - I have not tested them all (it worked well so far on Xperia Z3 API 23 and Samsung Galaxy A3 API 23).

    Please confirm if it does not perform well elsewhere.

    0 讨论(0)
提交回复
热议问题