Ever since Google and Android made the migration to scoped storage with Android 10, things have seemed to me to continue to get increasingly more complicated, for using Xamarin Forms/.NET MAUI to get a list of files from an individual folder on an Android device. For one of my software titles, even though all required permissions have been granted (StorageRead, StorageWrite), when my processing calls Directory.GetFiles for a particular path on the device, the resulting array has 0 entries.
The frustrating thing is, for an app I wrote back on Android 10, that used to work, I'm at a loss as to what I can/should change to make file information appear. I remember struggling with this before, and am going through it all over again. This instruction, for example...
String[] backupFileNames = Directory.GetFiles(backupSubfolderPath);
...should produce a non-null array of entries which I can then loop through using code such as the following:
for (int n = 0; n < backupFileNames.Length; n++)
{
FileInfo fi = new FileInfo(backupFileNames[n]);
BackupFileInfo bfi = new BackupFileInfo
{
Name = fi.Name,
Length = fi.Length
};
System.Diagnostics.Debug.WriteLine(">>> fi.Name = " + fi.Name);
System.Diagnostics.Debug.WriteLine(">>> fi.Length = " + fi.Length);
System.Diagnostics.Debug.WriteLine(">>> ---------------------------");
resultList.Add(bfi);
}
At the moment, of course, the loop above doesn't even begin, since the Length property is 0 as a result of the (failed) GetFiles call.
Google has decreed that any apps submitted for August 2023 and beyond MUST run with Android 13, as that is now their latest release of the OS. For "CRUD" apps requiring the ability to export and/or import files - perhaps files supplied by the user (in my case, backup files) - I don't see how this will ever work again.
I have seen references to Storage Access Framework, but haven't seen any code samples online - even here on StackOverflow - that strike me as a sufficiently usable solution.
Why would Xamarin Forms calling Android's file system return null data for an "external" file - that is, a file outside the "data" sandbox which holds all private application data?
Are there any decent NuGet solutions to this issue at this time?
On the Android tab in Xamarin Forms for the Android sub-project, I tried changing the Target SDK several times (Android 10, 11, 12 and 13 all produced differing results).
I also tried several other methods:
Directory.EnumerateFiles
Directory.GetFileSystemEntries
Some Android-specific code is required, to get the necessary access. Once you have access, should be able to pass the directory path to your cross-platform code, which can then use Directory and File actions. That is, your cross-platform code calls a method
string GetPlatformPath()(or whatever) in your Android project, that does the needed Android-specific work, then returns directory path.c# "partial class" / "partial method" is a convenient way to connect cross-platform code in Maui. Make sure the Android class file (in the Android folder) is in the same cross-platform namespace as the cross-platform class file.
Xamarin.Forms has to use a different mechanism, because the Android code is in a separate project. One technique is to define a
Func<string> GetPlatformPath;in cross-platform code, then have Android project set thatFuncduring app startup. Add other parameters to theFunc, as needed. [This can be used in Maui also.] Or create a "Service", that each platform implements.For a shared directory, your app has to create an intent that asks user for permission to access the needed directory. Once that permission is granted, the URI to that folder gets added to your app's SAF permissions.
For details, see SAF / Grant access to a directory's contents in Android docs.
UPDATE See also: Update other apps' media files / specify directory "Optionally, you can specify the URI of the directory that the file picker should display when it first loads by using the EXTRA_INITIAL_URI intent extra."
IMPORTANT: Read "Access Restrictions" section a bit later in that doc. Starting with Android 13, it is impossible to get at
Android/dataorAndroid/obb, or their subfolders. Prior to 13, there was a loophole that made this possible.AFAIK, after that, file and directory actions should work as expected.
If you (or anyone) gets this working from Maui/Xamarin.Forms, please edit this Community wiki answer with the c# code (or add your own answer).
It is Android-specific, so belongs in a file in the
Androiddirectory of your solution/project.TODO: C# code here
For media files in a shared media folder, see also:
Update other apps' media files:
For the app-private directory in external storage, see Microsoft's doc Android External Storage / Private:
Android.Content.Context.GetExternalFilesDir(string type)This directory does not require special permissions or interaction with user. It is similar to the app's internal directory, but in external storage [And thus might not be secure, even though it is considered "private" to the app. Especially if it is removeable media.]
OR if the files can be downloaded again from your server when needed, to allow OS (or user) to manage storage, use app's cache dir:
Internal storage: Maui / File System Helpers / Cache Directory:
FileSystem.Current.CacheDirectoryCache in External storage:
getExternalCacheDir()in Android docs; not sure what c# equivalent is.Cached files are part of app's storage, so there is no problem accessing them by path. (Though they are not guaranteed to persist; either user or system can delete them if space is needed.)
Maui's FilePicker
FilePicker.