1

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
}
Stelios Papamichail
  • 955
  • 2
  • 19
  • 57

2 Answers2

1

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.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • I'm sorry about the misuse of the Uri and File words, you are correct. The problem I had when I tried this solution earlier today, was with the FileProvider. I'm adding more details now. – Stelios Papamichail Jul 10 '21 at 23:08
  • @SteliosPapamichail: You do not need `FileProvider`. You *do* need to hold onto the entire `Uri` and stop calling `getPath()` on the `Uri`. However, I did forget the permission flag (sorry!), which I added to my answer. – CommonsWare Jul 10 '21 at 23:26
  • You are correct once again, thank you for your help. I'll read up on paths and Uris for now. I'll update my code too since getting the file's name and extension works a bit different now. – Stelios Papamichail Jul 11 '21 at 10:38
1

You need to set at least the file path and a correct mime-type.

val mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension("pdf")
intent.setDataAndType(Uri.parse("file://$filePath"), mime)
Darkman
  • 2,941
  • 2
  • 9
  • 14