Android opening a file with ACTION_GET_CONTENT results into different Uri's

前端 未结 4 728
不思量自难忘°
不思量自难忘° 2020-11-30 23:21

I am trying to open files by using Intent.ACTION_GET_CONTENT.

Dependent on the Android version/device brand the file browser opens and I get the follo

相关标签:
4条回答
  • 2020-11-30 23:27

    The accepted answer on kotlin

    @Suppress("SpellCheckingInspection")
    object PathCompat {
    
        @WorkerThread
        fun getFilePath(context: Context, uri: Uri): String? = context.run {
            return try {
                when {
                    Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT -> getDataColumn(uri, null, null)
                    else -> getPathKitkatPlus(uri)
                }
            } catch (e: Throwable) {
                Timber.e(e)
                null
            }
        }
    
        @Suppress("DEPRECATION")
        @SuppressLint("NewApi", "DefaultLocale")
        private fun Context.getPathKitkatPlus(uri: Uri): String? {
            when {
                DocumentsContract.isDocumentUri(applicationContext, uri) -> {
                    val docId = DocumentsContract.getDocumentId(uri)
                    when {
                        uri.isExternalStorageDocument -> {
                            val parts = docId.split(":")
                            if ("primary".equals(parts[0], true)) {
                                return "${Environment.getExternalStorageDirectory()}/${parts[1]}"
                            }
                        }
                        uri.isDownloadsDocument -> {
                            val contentUri = ContentUris.withAppendedId(
                                Uri.parse("content://downloads/public_downloads"),
                                docId.toLong()
                            )
                            return getDataColumn(contentUri, null, null)
                        }
                        uri.isMediaDocument -> {
                            val parts = docId.split(":")
                            val contentUri = when (parts[0].toLowerCase()) {
                                "image" -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
                                "video" -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI
                                "audio" -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
                                else -> return null
                            }
                            return getDataColumn(contentUri, "_id=?", arrayOf(parts[1]))
                        }
                    }
                }
                "content".equals(uri.scheme, true) -> {
                    return if (uri.isGooglePhotosUri) {
                        uri.lastPathSegment
                    } else {
                        getDataColumn(uri, null, null)
                    }
                }
                "file".equals(uri.scheme, true) -> {
                    return uri.path
                }
            }
            return null
        }
    
        private fun Context.getDataColumn(uri: Uri, selection: String?, args: Array<String>?): String? {
            contentResolver?.query(uri, arrayOf("_data"), selection, args, null)?.use {
                if (it.moveToFirst()) {
                    return it.getString(it.getColumnIndexOrThrow("_data"))
                }
            }
            return null
        }
    
        private val Uri.isExternalStorageDocument: Boolean
            get() = authority == "com.android.externalstorage.documents"
    
        private val Uri.isDownloadsDocument: Boolean
            get() = authority == "com.android.providers.downloads.documents"
    
        private val Uri.isMediaDocument: Boolean
            get() = authority == "com.android.providers.media.documents"
    
        private val Uri.isGooglePhotosUri: Boolean
            get() = authority == "com.google.android.apps.photos.content"
    }
    
    0 讨论(0)
  • 2020-11-30 23:32

    Use below code.This will work for sure.

    public static String getPath(Context context, Uri uri) {
    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            // DocumentProvider
            if (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;
    }
    
    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;
    }
    
    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());
    }
    

    Use the below code to browse the file in any format.

    public void browseClick() {
    
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.setType("*/*");
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        //intent.putExtra("browseCoa", itemToBrowse);
        //Intent chooser = Intent.createChooser(intent, "Select a File to Upload");
        try {
            //startActivityForResult(chooser, FILE_SELECT_CODE);
            startActivityForResult(Intent.createChooser(intent, "Select a File to Upload"),FILE_SELECT_CODE);
        } catch (Exception ex) {
            System.out.println("browseClick :"+ex);//android.content.ActivityNotFoundException ex
        }
    }
    

    Then get that file path in the onActivityResult like below.

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == FILE_SELECT_CODE) {
            if (resultCode == RESULT_OK) {
                try {
                  Uri uri = data.getData();
    
                    if (filesize >= FILE_SIZE_LIMIT) {
                        Toast.makeText(this,"The selected file is too large. Selet a new file with size less than 2mb",Toast.LENGTH_LONG).show();
                    } else {
                        String mimeType = getContentResolver().getType(uri);
                        if (mimeType == null) {
                            String path = getPath(this, uri);
                            if (path == null) {
                                filename = FilenameUtils.getName(uri.toString());
                            } else {
                                File file = new File(path);
                                filename = file.getName();
                            }
                        } else {
                            Uri returnUri = data.getData();
                            Cursor returnCursor = getContentResolver().query(returnUri, null, null, null, null);
                            int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
                            int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
                            returnCursor.moveToFirst();
                            filename = returnCursor.getString(nameIndex);
                            String size = Long.toString(returnCursor.getLong(sizeIndex));
                        }
       File fileSave = getExternalFilesDir(null);
        String sourcePath = getExternalFilesDir(null).toString();
        try {
                            copyFileStream(new File(sourcePath + "/" + filename), uri,this);
    
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
      }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
    private void copyFileStream(File dest, Uri uri, Context context)
            throws IOException {
        InputStream is = null;
        OutputStream os = null;
        try {
            is = context.getContentResolver().openInputStream(uri);
            os = new FileOutputStream(dest);
            byte[] buffer = new byte[1024];
            int length;
    
            while ((length = is.read(buffer)) > 0) {
                os.write(buffer, 0, length);
    
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            is.close();
            os.close();
        }
    }
    

    After this you can open this file from your application external storage where you saved the file with appropriate action.

    0 讨论(0)
  • 2020-11-30 23:44

    There is a bug I just faced

    final String docId = DocumentsContract.getDocumentId(uri);
    

    return different URI (e.g: content://com.android.providers.downloads.documents/document/11 and sometime content://com.android.providers.downloads.documents/document/abc%aile.jpg in that case Long.valueOf(id) throws an exception to fix that

    String id = DocumentsContract.getDocumentId(uri);
                    if (id.startsWith("raw:")) {
                        id = id.replaceFirst("raw:", "");
                        return id;
                    }
                    final Uri contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
    
                    return getDataColumn(contentUri, null, null);
    

    do this return the id, it worked for me

    0 讨论(0)
  • 2020-11-30 23:51

    Pick any file using System File Picker:

    val intent = Intent(Intent.ACTION_GET_CONTENT)
    intent.type = "*/*"
    startActivityForResult(intent, 1)
    

    onActivityResult:

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == 1 && resultCode == Activity.RESULT_OK) {
            data?.data?.let {
                getFileFromUri(requireContext().contentResolver, uri, requireContext().cacheDir)
            }
        }
    }
    

    Get File:

    private fun getFileFromUri(contentResolver: ContentResolver, uri: Uri, directory: File): File {
        val file =
            File.createTempFile("suffix", "prefix", directory)
        file.outputStream().use {
            contentResolver.openInputStream(uri)?.copyTo(it)
        }
    
        return file
    }
    
    0 讨论(0)
提交回复
热议问题