admin管理员组

文章数量:1401651

I am writing an Android app in Kotlin and I am using Jetpack Compose.

In my app, I ask the user to select a directory where I can create a file and write to it. This is the relevant code extracted from my app:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            TestApplicationTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    DirectorySelectionScreen(
                        modifier = Modifier.padding(innerPadding)
                    )
                }
            }
        }
    }
}

@Composable
fun DirectorySelectionScreen(modifier: Modifier = Modifier) {
    val context = LocalContext.current

    // Launcher for directory selection
    val openDirectoryLauncher = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri ->
        uri?.let {
            // Handle the selected directory URI
            createFileInDirectory(context, it)
        }
    }

    Column(modifier = Modifier.padding(16.dp)) {
        Text(
            text = "Select a Directory to Create File",
            style = MaterialTheme.typography.headlineSmall,
            modifier = Modifier.padding(bottom = 16.dp),
            fontWeight = FontWeight.Bold
        )

        Button(onClick = { openDirectoryLauncher.launch(null) }) {
            Text(text = "Select Directory")
        }
    }
}

fun createFileInDirectory(context: Context, parentUri: Uri) {
    val contentResolver = context.contentResolver
    val mimeType = "text/plain"
    val displayName = "example.txt"

    try {
        // Create a new document (file) in the selected directory
        val newDocumentUri = DocumentsContract.createDocument(
            contentResolver,
            parentUri,  // URI of the selected directory
            mimeType,
            displayName
        )

        newDocumentUri?.let {
            // Now write to the file
            writeToFile(context, it)
        }
    } catch (e: Exception) {
        Toast.makeText(context, "Error creating file: ${e.message}", Toast.LENGTH_LONG).show()
    }
}

fun writeToFile(context: Context, fileUri: Uri) {
    try {
        // Open an OutputStream to the created file and write content
        context.contentResolver.openOutputStream(fileUri)?.use { outputStream ->
            val content = "Hello, world!"
            outputStream.write(content.toByteArray())
        }
    } catch (e: Exception) {
        Toast.makeText(context, "Error writing to file: ${e.message}", Toast.LENGTH_LONG).show()
    }
}

I am getting following exception after I select a directory:

Failed to create document
java.lang.IllegalArgumentException: Invalid URI: content://com.android.externalstorage.documents/tree/primary%3ADownload%2FTest
    at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:172)
    at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:142)
    at android.content.ContentProviderProxy.call(ContentProviderNative.java:764)
    at android.content.ContentResolver.call(ContentResolver.java:2466)
    at android.provider.DocumentsContract.createDocument(DocumentsContract.java:1380)

I am not able to understand what am I missing here.

I am writing an Android app in Kotlin and I am using Jetpack Compose.

In my app, I ask the user to select a directory where I can create a file and write to it. This is the relevant code extracted from my app:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            TestApplicationTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    DirectorySelectionScreen(
                        modifier = Modifier.padding(innerPadding)
                    )
                }
            }
        }
    }
}

@Composable
fun DirectorySelectionScreen(modifier: Modifier = Modifier) {
    val context = LocalContext.current

    // Launcher for directory selection
    val openDirectoryLauncher = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri ->
        uri?.let {
            // Handle the selected directory URI
            createFileInDirectory(context, it)
        }
    }

    Column(modifier = Modifier.padding(16.dp)) {
        Text(
            text = "Select a Directory to Create File",
            style = MaterialTheme.typography.headlineSmall,
            modifier = Modifier.padding(bottom = 16.dp),
            fontWeight = FontWeight.Bold
        )

        Button(onClick = { openDirectoryLauncher.launch(null) }) {
            Text(text = "Select Directory")
        }
    }
}

fun createFileInDirectory(context: Context, parentUri: Uri) {
    val contentResolver = context.contentResolver
    val mimeType = "text/plain"
    val displayName = "example.txt"

    try {
        // Create a new document (file) in the selected directory
        val newDocumentUri = DocumentsContract.createDocument(
            contentResolver,
            parentUri,  // URI of the selected directory
            mimeType,
            displayName
        )

        newDocumentUri?.let {
            // Now write to the file
            writeToFile(context, it)
        }
    } catch (e: Exception) {
        Toast.makeText(context, "Error creating file: ${e.message}", Toast.LENGTH_LONG).show()
    }
}

fun writeToFile(context: Context, fileUri: Uri) {
    try {
        // Open an OutputStream to the created file and write content
        context.contentResolver.openOutputStream(fileUri)?.use { outputStream ->
            val content = "Hello, world!"
            outputStream.write(content.toByteArray())
        }
    } catch (e: Exception) {
        Toast.makeText(context, "Error writing to file: ${e.message}", Toast.LENGTH_LONG).show()
    }
}

I am getting following exception after I select a directory:

Failed to create document
java.lang.IllegalArgumentException: Invalid URI: content://com.android.externalstorage.documents/tree/primary%3ADownload%2FTest
    at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:172)
    at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:142)
    at android.content.ContentProviderProxy.call(ContentProviderNative.java:764)
    at android.content.ContentResolver.call(ContentResolver.java:2466)
    at android.provider.DocumentsContract.createDocument(DocumentsContract.java:1380)

I am not able to understand what am I missing here.

Share Improve this question asked Mar 23 at 0:54 jyoteshjyotesh 4507 silver badges19 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

I figured it out. You have to first convert the URI that is obtained from ACTION_OPEN_DOCUMENT_TREE by calling DocumentsContract.buildDocumentUriUsingTree and then use the obtained URI in the call to DocumentsContract.createDocument like this

val parentDocumentUri = DocumentsContract.buildDocumentUriUsingTree(
    parentUri,
    DocumentsContract.getTreeDocumentId(parentUri)
)
val newDocumentUri = DocumentsContract.createDocument(
    contentResolver,
    parentDocumentUri,  // Tree URI of the selected directory
    mimeType,
    displayName
)

The call to DocumentsContract.buildDocumentUriUsingTree converts the URI from

content://com.android.externalstorage.documents/tree/primary%3ADownload%2FTest

to

content://com.android.externalstorage.documents/tree/primary%3ADownload%2FTest/document/primary%3ADownload%2FTest

本文标签: androidInvalid URI in DocumentsContractcreateDocumentStack Overflow