I want to store a local path (either internal or external storage) where the user can store some data as a preference in an Android app. I want the user to select a prefered folder in the preferences. Default folder is context.externalMediaDirs which returns a File object whos absolute path I store as string in the SharedPreferences. Later when the user saves the data the data is correctly stored via FileOutputStream to a newly created file in that folder.
Now I added an intent for OPEN_DOCUMENT_TREE on the preference so the user can select a different folder. But the intent returns an URI in the format of content://com.android.externalstorage.documents/... which I store as string, but when I try to write a file via context.contentResolver.openOutputStream() I get the error that I need MANAGE_DOCUMENTS permission. But this is only for system apps.
I found solutions how to convert an URI to a local path, but it was always noted that this is hacky and should not be done. I tried contentResolver.takePersistableUriPermission(...) but nothing changed.
So what is the correct way to receive a folder from the user, store it in the preferences, and be able to create new files there?
Edit: current approach preference XML (extract):
<Preference
app:key="target_dir">
<intent android:action="android.intent.action.OPEN_DOCUMENT_TREE"/>
</Preference>
Calling the intent
val targetDirPreference: Preference? = findPreference("target_dir")
targetDirPreference?.setOnPreferenceClickListener {
startActivityForResult(it.intent, REQUEST_TARGET_FOLDER)
true
}
Result handling (changed on blackapps request to not include shared preferences but only create a file)
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
if ((requestCode == REQUEST_TARGET_FOLDER ) && resultCode == RESULT_OK && intent != null) {
//val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
//sharedPreferences.getString("target_dir", "")?.also {
// with(sharedPreferences.edit()) {
// putString("target_dir", intent.data.toString())
// commit()
// }
val newFile = Uri.parse("${intent.dataString}%2Ftest.png")
context?.contentResolver?.openOutputStream(newFile)
}
}
else {
super.onActivityResult(requestCode, resultCode, intent)
}
}
Trying to create a new file, some time (maybe hours) after the intent
val targetDir = PreferenceManager.getDefaultSharedPreferences(context).getString("target_dir", null)
val targetFile = "${System.currentTimeMillis()}.png"
val uri = Uri.parse("$targetDir%2F$targetFile")
val stream = context.contentResolver.openOutputStream(uri)
The last line crashes with the permission error.