0

I have read about and think I understand the essentials of changes in Android 10 and 11. Gone are the days of accessing folders and files outside of the Android app sandbox willy nilly. That's fine. Just need a way forward and that's become difficult.

I have 2+ apps that share a local Sqlite database and related files in a folder. One or more of the apps in the group might be installed - no guarantee on which of the apps are present. On iOS and Windows (UWP) there is a nice "app group" (iOS name for it) style concept that supports this kind of arrangement formally in the platform. First one installed/run will create the local storage files. Last app in the group uninstalled and the OS cleans up the shared storage location. Android never had this concept so a common location was created outside of the app specific sandbox.

After studying the options available going forward, seems like the "Best" option was to use the Storage Access Framework (SAF) to get permission from the user for some common folder to use. Note that although there are many different "sharing" options in Android, none of them are great for this use case, and most are not friendly to cross platform Xamarin C# without wrapping them somehow. This "Best" option using SAF still requires the user to independently pick the SAME folder from each app that wants to share the local db/files. You know users are going to mess that up, but that's beside the point at the moment.

In testing this approach, I have been able to use the SAF picker to get the user to choose a folder. The Documents folder is what I've been choosing to test with as a folder. From there the app attempts to create a subfolder where all this shared "app group" content would go. Unfortunately simply doing a Directory.CreateDirectory(path) gives a System.IO.IOException: 'Read-only file system'. I checked am I am still able to do Directory.CreateDirectory(path) in the app sandbox (GetExternalFilesDir), just not the SAF chosen location.

I am also able to create a directory in the SAF location if I stick to the SAF API, such as illustrated in the Xamarin Android sample here: https://github.com/xamarin/monodroid-samples/blob/master/android5.0/DirectorySelection/DirectorySelectionFragment.cs#L169-L188.

Is there any way to treat the SAF location chosen by the user just like a normal file system and use System.IO operation to manipulate it? The app has been given permission but those ops don't seem to work in that location. Or is there a better overall approach to this problem that I've totally missed?

DennisWelu
  • 788
  • 13
  • 26
  • All has been asked before. https://stackoverflow.com/questions/65653993/android-11-how-where-to-write-mixed-media-files-that-should-survive-uninstall/65654430?noredirect=1#comment116127388_65654430 – blackapps Jan 12 '21 at 17:15
  • @blackapps Yeah no. Not the same thing. – DennisWelu Jan 12 '21 at 17:22
  • 1
    `Is there any way to treat the SAF location chosen by the user just like a normal file system and use System.IO operation to manipulate it?` No. – blackapps Jan 12 '21 at 17:29
  • Although you can mostly convert a SAF content scheme to a classic file system path it will not help you much as you will not have read/write permission in that way. – blackapps Jan 12 '21 at 17:32
  • You might be right that there is no way, I was hoping to get that confirmed. Is there docs on that? I had previously watched this video https://www.youtube.com/watch?v=RjyYCUW-9tY&t=1s where it talks about the ability to work with SAF just like a file system... Starting at 4:20 in the video...at 5:10 the speaker talks about still allowing file path APIs for compatibility reasons. So I had hopes it could be done. – DennisWelu Jan 12 '21 at 17:55
  • She does not tell how one can use file path api. I dont know what she means. And for the rest she is hardly understandable. – blackapps Jan 12 '21 at 18:14

2 Answers2

2

Normal Java File I/O does not work with Scoped Storage. File paths and File or Directory objects do not worked in Storage Access Framework, you have to do everything through the DocumentFile API. DocumentFile has the ability to create files and directories in locations that the user has granted your app access to through the File-picker dialog.

Debrugger
  • 92
  • 1
  • 9
0

There IS a way for normal/traditional System.IO file I/O to work after converting the SAF content to a classic file system path. Using the FileUtil logic in this answer https://stackoverflow.com/a/36162691/1735721 I was first able to get permission to a folder from the user:

var intent = new Intent(Intent.ActionOpenDocumentTree);
StartActivityForResult(intent, 1);

The in OnActivityResult(_, _, Intent resultData) use the file util logic:

var folderPath = FileUtil.GetFullPathFromTreeUri(resultData.Data, this);
var filePath = Path.Combine(folderPath, "test.txt");

At that point filePath represents the path and filename in the chosen directory tree, and normal C# System.IO operations are available to the app for that file e.g. StreamWriter and StreamReader.

NOTE: I was creating "test.txt" directly in the chosen folder. This worked to create the file in "A" but then "B" couldn't read that same file (Unauthorized exception). At some point I created a subfolder and "test.txt" was created there instead...then both "A" and "B" could read and write the same file. Unfortunately, a couple days later, I couldn't repeat that. So as it stands this is only a partial solution.

DennisWelu
  • 788
  • 13
  • 26