Search code examples
androidkotlinandroid-filestorage-access-framework

Open any file type using the system dialog for viewing


In my app, I want the user to be able to select any file they want (.mp4, .pdf, etc.), display the file name & extension within the app, and finally allow them to open the referenced file using the system dialog. I've already succeeded in opening the file picker dialog, getting the selected file's path and info, but I'm having trouble understanding how to go about using that file's path to trigger the OS dialog that will handle the file opening process. I've read up a bit on the SAF and the ACTION_OPEN_DOCUMENT action, but I can't seem to understand if that's necessary or how to even go about it.

Here's my file retrieval process:

private fun openFilePicker() { 
   val intent = Intent(Intent.ACTION_GET_CONTENT)
   intent.type = "*/*"
   startActivityForResult(intent, PICKFILE_RESULT_CODE)
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        when (requestCode) {
            PICKFILE_RESULT_CODE -> if (resultCode == RESULT_OK) {
                val filePath: String? = data?.data?.path
                if (filePath != null) attachmentsAdapter.addFilePath(filePath) else Toast.makeText(
                    requireContext(),
                    "Something went wrong, please try again!",
                    Toast.LENGTH_SHORT
                ).show()
            }
            else -> super.onActivityResult(requestCode, resultCode, data)
        }
    }

I know that the SAF was introduced to improve the user's privacy and that apps can initially only access their own dirs (and some public ones I think), but do I really need to do something like creating my own dir, copying the referenced files there and then doing whatever I need to do to open them?

Update based on CommonsWare's answer:

As CommonsWare stated, the trick is to simply avoid using the path of the returned URI and instead use the URI itself. See their answer for the details.

Furthermore, since I'm only using the file's URI, getting its name and extension is a bit different. Here's how that can be done using a Cursor:

private fun getFileNameWithExtensionFromUri(currentFileUri: String): String? {
    val returnCursor: Cursor? = context.contentResolver.query(Uri.parse(currentFileUri), null, null, null, null)
    /*
     * Get the column indexes of the data in the Cursor,
     * move to the first row in the Cursor, get the data,
     * and display it.
     */
    val nameIndex = returnCursor?.getColumnIndex(OpenableColumns.DISPLAY_NAME)
    returnCursor?.moveToFirst()
    val fileName = returnCursor?.getString(nameIndex!!)
    returnCursor?.close()
    return fileName
}

Solution

  • Here's my file retrieval process

    A Uri is not a file. data?.data?.path will not usually return a filesystem path.

    I'm having trouble understanding how to go about using that file's path to trigger the OS dialog that will handle the file opening process

    Android does not have an "OS dialog that will handle the file opening process", at least as I would use those terms.

    If you want to try opening some app that can view the content, create an ACTION_VIEW Intent:

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
            when (requestCode) {
                PICKFILE_RESULT_CODE -> if (resultCode == RESULT_OK) {
                    data?.data?.let { uri ->
                      try {
                        startActivity(Intent(Intent.ACTION_VIEW, uri)).addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                      } catch (e: Exception) {
                        Toast.makeText(
                          requireContext(),
                          "Something went wrong, please try again!",
                          Toast.LENGTH_SHORT
                        ).show()
                      }
                    }
                }
                else -> super.onActivityResult(requestCode, resultCode, data)
            }
        }
    

    The try/catch is important, as you are allowing the user to select any sort of content, and there may not be an app installed that offers support for viewing that particular type of content.