0

To get right to the point I'm having an issue where I am selecting a file using the Intent.createChooser and I've used an assortment of different way to get the absolute path to the file and they all result in content://downloads/public_downloads/1006.

Using this path in an emulator works perfectly fine and produces the expected results, but as soon as I build the app on my device (Galaxy S9) I get an error that the file does not exist.

I have requested permissions for Manifest.permission.READ_EXTERNAL_STORAGE and Manifest.permission.WRITE_EXTERNAL_STORAGE and verified that the permissions are enabled in the app.

After searching for a few hours I'm running out of ideas to try and would be happy if someone could guide me in the right way.

The method I'm currently using for getting the absolute path is

private fun getAbsoluteFilePath(uri: Uri): String {
    val id = DocumentsContract.getDocumentId(uri)
    val contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/my_downloads"), id.toLong())

    val projection = arrayOf(MediaStore.Images.Media.DATA)
    val cursor = contentResolver.query(contentUri, projection, null, null, null)
    val columnIndex = cursor!!.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)

    cursor.moveToFirst()

    return cursor.getString(columnIndex)
}   

but I've also tried

object PathUtil {
    /*
     * Gets the file path of the given Uri.
     */
    @SuppressLint("NewApi")
    @Throws(URISyntaxException::class)
    fun getPath(context: Context, uri: Uri): String? {
        var uri = uri
        val needToCheckUri = Build.VERSION.SDK_INT >= 19
        var selection: String? = null
        var selectionArgs: Array<String>? = null

        // Uri is different in versions after KITKAT (Android 4.4), we need to
        // deal with different Uris.
        if (needToCheckUri && DocumentsContract.isDocumentUri(context.applicationContext, uri)) {
            if (isExternalStorageDocument(uri)) {
                val docId = DocumentsContract.getDocumentId(uri)
                val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()

                return Environment.getExternalStorageDirectory().toString() + "/" + split[1]
            }

            if (isDownloadsDocument(uri)) {
                val id = DocumentsContract.getDocumentId(uri)
                uri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id))
            } else if (isMediaDocument(uri)) {
                val docId = DocumentsContract.getDocumentId(uri)
                val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
                val type = split[0]

                if ("image" == type) {
                    uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
                } else if ("video" == type) {
                    uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
                } else if ("audio" == type) {
                    uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
                }

                selection = "_id=?"
                selectionArgs = arrayOf(split[1])
            }
        }

        if ("content".equals(uri.scheme, ignoreCase = true)) {
            val projection = arrayOf(MediaStore.Images.Media.DATA)
            var cursor: Cursor? = null

            cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
            val columnIndex = cursor!!.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)

            if (cursor!!.moveToFirst()) {
                return cursor!!.getString(columnIndex)
            }
        } else if ("file".equals(uri.scheme, ignoreCase = true)) {
            return uri.path
        }

        return null
    }


    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is ExternalStorageProvider.
     */
    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.
     */
    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.
     */
    fun isMediaDocument(uri: Uri): Boolean {
        return "com.android.providers.media.documents" == uri.authority
    }
}

which yields the same result, my code is more or less this:

fun doSomething() {
    val intent = Intent().apply {
        action = Intent.ACTION_GET_CONTENT
        type = "*/*"
        addCategory(Intent.CATEGORY_OPENABLE)
    }

    val chooser = Intent.createChooser(intent, "Select")

    startActivityForResult(chooser, 1)
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    ZipFile(getAbsoluteFilePath(data.data))
}

Oh, and please don't close this as a duplicate - I've found lots of similar issues but no solution has worked for me yet, not sure what I'm missing but I bet it's something stupid

Marco
  • 2,329
  • 1
  • 21
  • 25
  • See also [this](https://stackoverflow.com/q/48510584/115145) and [this](https://stackoverflow.com/q/35870825/115145). – CommonsWare Oct 27 '18 at 22:05
  • Actually this might have just confused me more, what I need is something (either a path as a string or a `File` object) that I can pass to a library I depend on that uses `ZipFile` to read the file that I select using the chooser. What would be the right approach for this? – Marco Oct 28 '18 at 00:12
  • I suggest finding a better library, one that can work with streams rather than files. Otherwise, you will need to make a copy of the content to some file that you control, [using a `ContentResolver` and `openInputStream()` to get an `InputStream` on the content identified by the `Uri`](https://stackoverflow.com/a/35871320/115145). – CommonsWare Oct 28 '18 at 11:15
  • Awesome, thank you :) Solved it by copying the file (I just need to read it once and then I convert it to another format). I did try copying the file from the stream earlier as well, but I must have screwed up or done something else wrong because this time it worked – Marco Oct 28 '18 at 11:48

0 Answers0