Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you get the path of the android documents directory in android q?

Tags:

android

kotlin

I'm struggling to find a clear answer to this question. I know it somehow involves using media store but I can't quite work it out.

Thank you.

Edit: if it helps, I'm using a Pixel 2 at the moment.

like image 984
YellowBranch23 Avatar asked Sep 06 '25 15:09

YellowBranch23


2 Answers

If you are targeting to Android 11 API, you cannot directly get access to the file paths, as there are many restrictions in API 30(Android R). As scoped storage API was introduced in Android 10(API 29), the storage is now divided into scoped storage(private storage) and shared storage(public storage). Scoped storage is a kind you can only have access to the files that are created in your scoped storage directory(i.e. /Android/data/ or /Android/media/). You cannot access files from shared storage (i.e. internal storage/external SD card storage etc.)

The shared storage is again further divided into Media and Download collection. Media collection stores Image, Audio and Video files. Download collection would take care of non-media files.

To learn in more details about scoped storage and shared storage refer this link- Scoped Storage in Android 10 & Android 11 .

To get the document directory path, I have created URI utils class that will get the path for the file from URI.

    import android.content.ContentUris
    import android.content.Context
    import android.database.Cursor
    import android.net.Uri
    import android.os.Build
    import android.os.Environment
    import android.provider.DocumentsContract
    import android.provider.MediaStore
    import android.provider.OpenableColumns
    import android.util.Log
    import android.widget.Toast
    import java.io.File
    import java.io.FileOutputStream
    
    class UriPathUtils
    {
        fun getRealPathFromURI(context: Context, uri: Uri): String? {
            when {
                // DocumentProvider
                DocumentsContract.isDocumentUri(context, uri) -> {
                    when {
                        // ExternalStorageProvider
                        isExternalStorageDocument(uri) -> {
                            //Toast.makeText(context, "From Internal & External Storage dir", Toast.LENGTH_SHORT).show()
                            val docId = DocumentsContract.getDocumentId(uri)
                            val split = docId.split(":").toTypedArray()
                            val type = split[0]
                            // This is for checking Main Memory
                            return if ("primary".equals(type, ignoreCase = true)) {
                                if (split.size > 1) {
                                    Environment.getExternalStorageDirectory().toString() + "/" + split[1]
                                } else {
                                    Environment.getExternalStorageDirectory().toString() + "/"
                                }
                                // This is for checking SD Card
                            } else {
                                "storage" + "/" + docId.replace(":", "/")
                            }
                        }
                        isDownloadsDocument(uri) -> {
                            //Toast.makeText(context, "From Downloads dir", Toast.LENGTH_SHORT).show()
                            val fileName = getFilePath(context, uri)
                            if (fileName != null) {
                                return Environment.getExternalStorageDirectory().toString() + "/Download/" + fileName
                            }
                            var id = DocumentsContract.getDocumentId(uri)
                            if (id.startsWith("raw:")) {
                                id = id.replaceFirst("raw:".toRegex(), "")
                                val file = File(id)
                                if (file.exists()) return id
                            }
                            val contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id))
                            return getDataColumn(context, contentUri, null, null)
                        }
                        isMediaDocument(uri) -> {
                            //Toast.makeText(context, "From Documents dir", Toast.LENGTH_SHORT).show()
                            val docId = DocumentsContract.getDocumentId(uri)
                            val split = docId.split(":").toTypedArray()
                            val type = split[0]
                            var contentUri: Uri? = null
                            when (type) {
                                "image" -> {
                                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
                                }
                                "video" -> {
                                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
                                }
                                "audio" -> {
                                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
                                }
                                else -> {
                                    //non-media files i.e documents and other files
                                    contentUri = MediaStore.Files.getContentUri("external")
                                    val selection = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                                        MediaStore.MediaColumns.RELATIVE_PATH + "=?"
                                    } else "_id=?"
                                    val selectionArgs = arrayOf(Environment.DIRECTORY_DOCUMENTS)
                                    return getMediaDocumentPath(context, contentUri,selection,selectionArgs)
                                }
                            }
                            val selection = "_id=?"
                            val selectionArgs = arrayOf(split[1])
                            return getDataColumn(context, contentUri, selection, selectionArgs)
                        }
                        isGoogleDriveUri(uri) -> {
                            Toast.makeText(context, "From Google Drive", Toast.LENGTH_SHORT).show()
                            return getDriveFilePath(uri, context)
                        }
                        /*else -> {
                            Toast.makeText(context, "Unknown Directory", Toast.LENGTH_SHORT).show()
                        }*/
                    }
                }
                "content".equals(uri.scheme, ignoreCase = true) -> {
                    //Toast.makeText(context, "From Content Uri", Toast.LENGTH_SHORT).show()
                    // Return the remote address
                    return if (isGooglePhotosUri(uri)) uri.lastPathSegment else getDataColumn(context, uri, null, null)
                }
                "file".equals(uri.scheme, ignoreCase = true) -> {
                    //Toast.makeText(context, "From File Uri", Toast.LENGTH_SHORT).show()
                    return uri.path
                }
            }
            return null
        }
    
        private fun getDataColumn(context: Context, uri: Uri?, selection: String?, selectionArgs: Array<String>?): String? {
            var cursor: Cursor? = null
            val column = "_data"
            val projection = arrayOf(column)
            try {
                if (uri == null) return null
                cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
                if (cursor != null && cursor.moveToFirst()) {
                    val index = cursor.getColumnIndexOrThrow(column)
                    return cursor.getString(index)
                }
            } finally {
                cursor?.close()
            }
            return null
        }
    
        private fun getMediaDocumentPath(context: Context, uri: Uri?,selection: String?, selectionArgs: Array<String>?): String? {
            var cursor: Cursor? = null
            val column = "_data"
            val projection = arrayOf(column)
            try {
                if (uri == null) return null
                cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
                if (cursor != null && cursor.moveToFirst()) {
                    val index = cursor.getColumnIndexOrThrow(column)
                    //Toast.makeText(context, "From Media Document --- Non Media Path ${cursor.getString(index)} ", Toast.LENGTH_SHORT).show()
                    return cursor.getString(index)
                }
            } finally {
                cursor?.close()
            }
            return null
        }
    
        private fun getFilePath(context: Context, uri: Uri?): String? {
            var cursor: Cursor? = null
            val projection = arrayOf(MediaStore.MediaColumns.DISPLAY_NAME)
            try {
                if (uri == null) return null
                cursor = context.contentResolver.query(uri, projection, null, null,
                    null)
                if (cursor != null && cursor.moveToFirst()) {
                    val index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME)
                    return cursor.getString(index)
                }
            } finally {
                cursor?.close()
            }
            return null
        }
    
        private fun getDriveFilePath(uri: Uri, context: Context): String? {
            val returnCursor = context.contentResolver.query(uri, null, null, null, null)
            val nameIndex = returnCursor!!.getColumnIndex(OpenableColumns.DISPLAY_NAME)
            val sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE)
            returnCursor.moveToFirst()
            val name = returnCursor.getString(nameIndex)
            val size = returnCursor.getLong(sizeIndex).toString()
            val file = File(context.cacheDir, name)
            try {
                val inputStream = context.contentResolver.openInputStream(uri)
                val outputStream = FileOutputStream(file)
                var read = 0
                val maxBufferSize = 1 * 1024 * 1024
                val bytesAvailable = inputStream!!.available()
    
                //int bufferSize = 1024;
                val bufferSize = Math.min(bytesAvailable, maxBufferSize)
                val buffers = ByteArray(bufferSize)
                while (inputStream.read(buffers).also { read = it } != -1) {
                    outputStream.write(buffers, 0, read)
                }
                Log.e("File Size", "Size " + file.length())
                inputStream.close()
                outputStream.close()
                Log.e("File Path", "Path " + file.path)
                Log.e("File Size", "Size " + file.length())
            } catch (e: Exception) {
                Log.e("Exception", e.message!!)
            }
            return file.path
        }
    
        /**
         * @param uri The Uri to check.
         * @return Whether the Uri authority is ExternalStorageProvider.
         */
        private fun isExternalStorageDocument(uri: Uri): Boolean {
            return "com.android.externalstorage.documents" == uri.authority
        }
    
        /**
         * @param uri The Uri to check.
         * @return Whether the Uri authority is DownloadsProvider.
         */
        private fun isDownloadsDocument(uri: Uri): Boolean {
            return "com.android.providers.downloads.documents" == uri.authority
        }
    
        /**
         * @param uri The Uri to check.
         * @return Whether the Uri authority is MediaProvider.
         */
        private fun isMediaDocument(uri: Uri): Boolean {
            return "com.android.providers.media.documents" == uri.authority
        }
    
        /**
         * @param uri The Uri to check.
         * @return Whether the Uri authority is Google Photos.
         */
        private fun isGooglePhotosUri(uri: Uri): Boolean {
            return "com.google.android.apps.photos.content" == uri.authority
        }
    
        /**
         * @param uri The Uri to check.
         * @return Whether the Uri authority is Google Photos.
         */
        private fun isGoogleDriveUri(uri: Uri): Boolean {
            return "com.google.android.apps.docs.storage" == uri.authority || "com.google.android.apps.docs.storage.legacy" == uri.authority
        }
}

This utils class will help you to get your file path, by just calling this line ..

contentUri.let { UriPathUtils().getRealPathFromURI(this, it).toString() }

Cheers...!

like image 84
Rohan Shinde Avatar answered Sep 08 '25 12:09

Rohan Shinde


Uri contentUri = MediaStore.Files.getContentUri("external");

String selection = MediaStore.MediaColumns.RELATIVE_PATH + "=?";

String[] selectionArgs = new String[]{Environment.DIRECTORY_DOCUMENTS};

Cursor cursor = getContentResolver().query(contentUri, null, selection, selectionArgs, null);
like image 32
Sam Chen Avatar answered Sep 08 '25 11:09

Sam Chen