Search code examples
javaandroidfilekotlinfile-permissions

How do I create files when all I have is an uri of a directory? (Persistable permission for the directory was granted by the Android user.)


I am trying to write and read files that are also visible and editable from a PC when the android device is connected to it via USB. These files might be created through the PC connection.

To achieve this I'm trying to take a persistable URI permission to a directory, and then create or read the files in that directory.

I am in the "proof of concept" phase.

Inside the MainActivity OnCreate method I have the following code:

startActivityForResult(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
    flags = Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
}, GET_PERSISTABLE_PERMISSION)

When I start te app, the directory picker pops up and I'm able to select a directory.

In MainActivity I also have:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if (requestCode == GET_PERSISTABLE_PERMISSION && resultCode == Activity.RESULT_OK) {
        val uri = data?.data!!
        val contentResolver = applicationContext.contentResolver
        val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
        contentResolver.takePersistableUriPermission(uri, takeFlags)

        val tmpFile = File(uri.path, "debugTestFile.txt")
        tmpFile.createNewFile()
        tmpFile.appendText("árvíztűrő tükörfúrógép\r\n")
    }
    super.onActivityResult(requestCode, resultCode, data)
}

Things I've tried:

  • uri.toString() instead of uri.path
  • flags = Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION when creating the intent
  • giving different directories on the Android device in the Downloads, Music, DCIM folders, when prompted by Android

Despite my best efforts, the tmpFile.createNewFile() line throws an exception with "No such file or directory" message.

    Process: hu.ibcs.android.scan, PID: 17309
    java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1, result=-1, data=Intent { dat=content://com.android.externalstorage.documents/tree/primary:Music/GVtest flg=0xc3 }} to activity {hu.ibcs.android.scan/hu.ibcs.android.scan.MainActivity}: java.io.IOException: No such file or directory
        at android.app.ActivityThread.deliverResults(ActivityThread.java:4846)
        at android.app.ActivityThread.handleSendResult(ActivityThread.java:4887)
        at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:51)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2017)
        at android.os.Handler.dispatchMessage(Handler.java:107)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7397)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935)
     Caused by: java.io.IOException: No such file or directory
        at java.io.UnixFileSystem.createFileExclusively0(Native Method)
        at java.io.UnixFileSystem.createFileExclusively(UnixFileSystem.java:317)
        at java.io.File.createNewFile(File.java:1008)
        at hu.ibcs.android.scan.MainActivity.onActivityResult(MainActivity.kt:106)
        at android.app.Activity.dispatchActivityResult(Activity.java:8135)
        at android.app.ActivityThread.deliverResults(ActivityThread.java:4839)

Solution

  • Big thanks to @blackapps for the suggestions!

    I changed the code in MainActivity.onCreate to:

    startActivityForResult(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE), GET_PERSISTABLE_PERMISSION)
    

    and the code in MainActivity to:

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        if (requestCode == GET_PERSISTABLE_PERMISSION && resultCode == Activity.RESULT_OK) {
            val uri = data?.data!!
            val contentResolver = applicationContext.contentResolver
            val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
            contentResolver.takePersistableUriPermission(uri, takeFlags)
    
            val pickedDir = DocumentFile.fromTreeUri(this, uri)
            val tmpFile = pickedDir.createFile("text/csv", "debugTestFile")
            val out: OutputStream? = getContentResolver().openOutputStream(tmpFile!!.uri)
            out?.write(("\uFEFF" + "árvíztűrő tükörfúrógép\r\n").toByteArray()) // adding BOM to the start for Excel to recognize utf8
            out?.close()
        }
        super.onActivityResult(requestCode, resultCode, data)
    }
    

    It's working and it looks like I can move on from here.