Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing many files on Android 11

Scenario

I am trying to create many files, as a feature for the user. For example, I might write an app which creates one file for each song they listened to for the past few weeks (e.g. a text file with lyrics). I can't make the user pick the directory and filename for each file I generate, it would take them hours. The user should have access to these documents from other apps.

Solutions (not really)

In Android 11, it seems like Storage Access Framework is not going to be useful. I noticed there were 2 initially interesting options:

  1. Create a new file (which creates launches activity the user interacts with to save one file only), described here
  2. Grant access to a directory's contents (which allows read access but no way to write any documents), described here.
  3. Note: A solution already exists if you target Android 10 and request WRITE_EXTERNAL_STORAGE. Unfortunately this permission is denied on Android 11. Therefore, I cannot use the solution posted here.
  4. Get access to the directory using Storage Access Framework (specifically ACTION_OPEN_DOCUMENT_TREE) to get access to a directory, but I couldn't write files into this directory. (I get FileNotFoundException, as this is not a normal path, but a tree path.)

Verdict

I guess in my case, I need to go down the route of "manage all files on a storage device", as described here which involves launching an extra activity with ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION. I'd rather not ask permission to read the entire drive or make the user go into the settings app to give permission. Writing files shouldn't require any permission...

How would you handle saving many (non-media) files for the user?

like image 909
Ben Butterworth Avatar asked Nov 06 '20 19:11

Ben Butterworth


People also ask

Does Android 11 have file manager?

Currently system level file management app can view and edit files under data folder on Android 11. You can use system built-in file manager to open the files and assign QuickEdit to edit the file.

Why does Android 11 have restrictions file manager?

The reason while the android 11 OBB folder was restricted from being accessed was due to the fact that android wanted to stop files from interacting with each other, basically separating them in a container, while also stopping the user from manually making changes to files or app data.

How do I get write external permissions in Android 11?

On the Settings > Privacy > Permission manager > Files and media page, each app that has the permission is listed under Allowed for all files. If your app targets Android 11, keep in mind that this access to "all files" is read-only.


1 Answers

There are 2 methods, but first, I'll define the file and directory name, which I will later save inside the external (sdcard) Downloads folder

val outputFilename = "my_file"
val outputDirectory = "my_sub_directory" // The folder within the Downloads folder, because we use `DIRECTORY_DOWNLOADS`

The working, but deprecated method:

Although the Environment.getExternalStoragePublicDirectory was deprecated, it still works on apps targeting and running on Android 11.

file = File(Environment.getExternalStoragePublicDirectory(
    Environment.DIRECTORY_DOCUMENTS),
    "$outputDirectory/$outputFilename"
)
val outputStream = FileOutputStream(file)

The MediaStore way:

Alternatively, you could use MediaStore's Files collection, suggested Gaurav Mall here. I didn't know about the MediaStore files collection...

I rewrote it in kotlin and modified it for writing files here:

val resolver = context.contentResolver
val values = ContentValues()
// save to a folder
values.put(MediaStore.MediaColumns.DISPLAY_NAME, outputFilename)
values.put(MediaStore.MediaColumns.MIME_TYPE, "application/my-custom-type")
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS + "/" + outputDirectory)
val uri = resolver.insert(MediaStore.Files.getContentUri("external"), values)
// You can use this outputStream to write whatever file you want:
val outputStream = resolver.openOutputStream(uri!!)
// Or alternatively call other methods of ContentResolver, e.g. .openFile

In both cases below, you don't need WRITE_EXTERNAL_STORAGE for Android 29 and above.

like image 162
Ben Butterworth Avatar answered Oct 11 '22 18:10

Ben Butterworth