Simple File Browser [Open Source]

I’ve done some more debugging, and i found that when i upgraded, it did not update the file browser resource that was in my project tree. I forgot that it’s not instantiating a prefab anymore, except for the item prefabs. I replaced it and now i can see images in the right pane. I’m still seeing the same error, but i guess it’s just choking on that one secure file and it’s not stopping the process.

I do still have one issue though, which is that i can’t seem to save to the sd card. The only thing i save in this app is screenshots. If i use the filebrowser to select a folder on the sd card, then attempt to create a file and write a byte array to it, i get this error:

2025/01/11 14:00:57.421 3816 3863 Error Unity UnauthorizedAccessException: Access to the path “/storage/D9CC-693A/misc/test/HTVR_2021_0728_170010_165_00.00.34.279.png” is denied.
2025/01/11 14:00:57.421 3816 3863 Error Unity at System.IO.FileStream…ctor (System.String path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share, System.Int32 bufferSize, System.Boolean anonymous, System.IO.FileOptions options) [0x00000] in <00000000000000000000000000000000>:0
2025/01/11 14:00:57.421 3816 3863 Error Unity at System.IO.File.InternalWriteAllBytes (System.String path, System.Byte bytes) [0x00000] in <00000000000000000000000000000000>:0
2025/01/11 14:00:57.421 3816 3863 Error Unity at menuScript.CaptureImage () [0x00000] in <00000000000000000000000000000000>:0
2025/01/11 14:00:57.421 3816 3863 Error Unity at UnityEngine.Events.UnityEvent.Invoke () [0x00000] in <00000000000000000000000000000000>:0
2025/01/11 14:00:57.421 3816 3863 Error Unity at UnityEngine.EventSystems.ExecuteEvents.Execute[T] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.ExecuteEvents+EventFunction`1[T1] functor) [0x00000] in <00000000000000000000000000000000>:0
2025/01/11 14:00:57.421 3816 3863 Error Unity at UnityEngine.EventSystems.StandaloneInputModule.P

The file doesn’t get created, but i don’t see an error about it, only about trying to write the bytes. I also tried adding the shouldUseSAF check that i saw in a github issue into my start method, but it didn’t have any effect. It seems to think the sd card is read only, but i’ve written to it in several other ways without issue using different apps. I’m wondering if SAF has to be used for the sd card, but i’m just guessing about that.

READ_EXTERNAL_STORAGE permission might be granted but WRITE_EXTERNAL_STORAGE permission might not be (it could have been done either manually or via another plugin). Could you try reproducing this issue on a new Unity project without modifying the AndroidManifest manually? Can you also check what FileBrowser.RequestPermission() returns?

To fix the initial permission error, could you try replacing this line as follows:

try
{
	Attributes = fileInfo.Attributes;
}
catch( Exception e )
{
	Attributes = ( fileInfo is DirectoryInfo ) ? FileAttributes.Directory : FileAttributes.Normal;
}

Both permissions exist in the manifest

I made a new test project is in unity 2022.3.54f1 and imported Simple File Browser through the package manager. FileBrowser.RequestPermission() returns “Granted”. I’m getting the same result on android 9. Access to the path on the SD card is deinied, no issue saving to internal storage.

Yes this prevents the error.

I also have an additional problem now. Someone told me that after my recent update, they can’t read from or see external USB drives in the file browser. This sounded familiar, and i realized that we talked about it before. Referring back to this post, you provided a fix that allowed all drives to appear in the left column. It still works on android 9, but on android 14 it now only shows internal storage. I checked to see if i need to add the fix again, but it looks like you added it to the asset. I tried reverting it back to the old way, but that didn’t do anything. Yeah, i feel dumb for trying that, but i would feel really smart if it worked…

This is kinda confusing me though. In the new test project, it works the same on android 9, with all drives in the left column, but on android 14, i only see the browse button, which takes me to the native picker, where i can indeed see and select a folder in a USB drive. But in my existing project, also with the same asset version as the test project, i don’t see the browse button at all, just one item, internal storage. I’m not sure why it’s different. I even tried importing again, but the package manager didn’t detect that FileBrowser.cs or any other files were different, aside from the prefabs that i edited. It seems like something didn’t update, but the package manager disagrees, and the readme in the root asset folder says version 1.6.6

And then there’s another minor thing that’s really not a problem, but it’s only adding to my confusion. One user told me he stores his content in folders like .vrSBS, .vrHOU, etc. I created a folder that starts with a period, and was able to confirm that i couldn’t see it. After some googling, and reading through your github issues (can’t remember where i finally saw this), someone said that starting in android 13, apps can’t see hidden files and folders that weren’t created by the app that tries to read it. That seemed reasonable, so i told that user to just remove the periods so the OS doesn’t think they’re meant to be hidden, and i’m fine with that result. However, in the new test project, i can see those folders.

The main thing that i care about now is the USB access. The number of users that are saving screenshots from videos on android 9 and below is probably very small, and getting smaller every day. But the USB issue on later versions will continue to generate complaints as more users update to my latest version. I’m just not sure what to check or how to approach this, since it seems that i’m fully updated.

  • Android 9 SD card problem:

There are 2 things that you could try:

  1. Firstly, you can check if the issue is actually caused by SimpleFileBrowser by calling File.WriteAllText("a hardcoded path in SD Card.txt", "Hello world"). But beforehand, request Permission.ExternalStorageWrite via Permission.RequestUserPermission function. This way, you won’t be using any SimpleFileBrowser APIs and if the issue persists, I think it’s safe to say that it’s not possible to write to SD Card using System.IO API on those devices.
  2. You’ve said that you’ve tried setting m_shouldUseSAF in Start but did you set it to true or false? If you haven’t tried setting it to true, please do so:
#if !UNITY_EDITOR && UNITY_ANDROID
typeof(SimpleFileBrowser.FileBrowserHelpers).GetField("m_shouldUseSAF", BindingFlags.NonPublic | BindingFlags.Static).SetValue(null, (bool?)true);
#endif
  • USB drives aren’t listed on Android 14 if Browse button is missing:

I think the important part here is that the Browse button is missing. It means that Storage Access Framework isn’t used. I think this can happen if your Target API Level is low. You can compare it with the empty project’s Target API Level. You can also try uninstalling the app and then reinstalling it because useLegacyExternalStorage flag is cached by Android and can cause this problem. You could also set m_shouldUseSAF to true to force usage of Storage Access Framework but I’d recommend checking the device’s API Level beforehand (API Level 29 and later can safely use Storage Access Framework).

  • Folders whose name start with a period are missing:

I don’t know about this one. Maybe some manufacturers’ devices automatically set those folders as hidden but your device doesn’t? Or it could be related to a “Show hidden files” setting in the native file browser app which then theoretically affects the file browsers in other apps including yours?

  • Additional info:

If you’re just saving and loading images to/from Gallery, then you may also consider replacing SimpleFileBrowser with NativeGallery. I recommend this plugin for Gallery interactions because it provides a very convenient user interface which is the native Gallery app’s own user interface. There is also NativeFilePicker which also uses native user interface only.

That was it. I set m_shouldUseSAF false to try to get the SD issue resolved. I thought i put it in an api level condition, but i guess i forgot to actually do that, so it was false for all versions.

Setting it to true for 30+ resolved the USB drive issue, and even the dot folder issue.
However, the SD card issue on android 9 still remained, so i started looking at how i was saving the file.
I was originally saving the screenshots like this:

FileBrowserHelpers.WriteBytesToFile(currentScreenshotFolder + "/HTVR_" + namenospaces + "_" + sstime + ".png", screenshot);

That worked on android 9 for internal storage, but not for the SD card. It also worked on android 14 without SAF, but not with it.
I figured that concatenating the path like that could be a problem with SAF, so i tried this:

string ssFileName = "HTVR_" + namenospaces + "_" + sstime + ".png";
string destinationPath = System.IO.Path.Combine(currentScreenshotFolder, ssFileName);
FileBrowserHelpers.CreateFileInDirectory(currentScreenshotFolder, ssFileName);
FileBrowserHelpers.WriteBytesToFile(destinationPath, screenshot);

That also failed on android 9 and 14.
I tried a few other things, including creating and writing the file in app storage, then copying it to the real folder. None of that worked.

Finally, i tried this:

FileBrowserHelpers.CreateFileInDirectory(currentScreenshotFolder, ssFileName);
FileSystemEntry[] ssfiles = FileBrowserHelpers.GetEntriesInDirectory(currentScreenshotFolder, false);
foreach (FileSystemEntry f in ssfiles)
{
    if (f.Name == ssFileName)
    {
        FileBrowserHelpers.WriteBytesToFile(f.Path, screenshot);
    }
}

That worked in every situation, except for the SD card on android 9.
Then i tried setting m_shouldUseSAF to true for all versions, and then it finally saved to the SD card.

So i seem to have learned the following things about storage:

On android 9
-files can be read from internal storage or the SD card without SAF
-files can be written to internal storage without SAF
-files can NOT be written to the SD card without SAF

On android 14
-files can be read from internal storage without SAF
-files cannot be read from or written to a USB drive without SAF
-the same is probably true for the SD card, but i don’t have a way to test that

So in the end, i’m forcing SAF for all versions since it makes everything work everywhere. My minimum api level is 24, which is after SAF was added, so i don’t see a problem with this, aside from folks on android 9 and below now having to use the native picker to create quick links, instead of just seeing the drives on the left. But…

SAF was added in api level 19, but i don’t have a phone below level 29 to test on. Do you believe this will be an issue on 24-28? I guess i could selectively change m_shouldUseSAF when needed for each scenario, but i’d rather not do that if i don’t need to, just to avoid unnecessary complication.

The app loads video, audio, and image files, saves image files, and i need to select a folder and save it for storing screenshots. It’s a VR app, so calling a native picker for every capture isn’t practical. It seems that i can’t load audio or pick folders with NativeGallery. I’m assuming that’s a MediaStore limitation. But i’ve been very happy with SimpleFileBrowser for this app for the last 5 years, and now that i have things working again, i see no reason to change.

I have another app that just needs to import and export sql files, and i wanted just a native picker for that, so i tried NativeFilePicker. It doesn’t seem to have the helper functions though, which are very useful. I tried using both SimpleFileBrowser and NativeFilePicker together, which surprisingly worked without conflicts, but it felt lazy, so i ended up removing NativeFilePicker, and then i dug into the SimpleFileBrowser java files and figured out how to bypass the canvas browser and just call the native picker. That worked very well, and people are using it now.

I also have a new app i’m working on, that will need to load audio and video files, and import/export sql files. With everything that i’ve learned by dealing with all this storage stuff, and given that these needs are pretty simple, i decided to try my hand at making my own plugin that does specifically what i need and nothing more. But one weird thing i noticed is that if i send specific mime types to the picker, all files will still be visible, but the types that i didn’t specify are just greyed out. Do you happen to know if there’s a way to just hide what i don’t want to see?

Anyway, thanks again for your help. As usual, you basically solved my problems with one sentence. I’m grateful for your software and your expertise.

1 Like

This was actually almost correct lol:

string destinationPath = FileBrowserHelpers.CreateFileInDirectory(currentScreenshotFolder, ssFileName);
FileBrowserHelpers.WriteBytesToFile(destinationPath, screenshot);

No need to call FileBrowserHelpers.GetEntriesInDirectory in this case. I’m also happy to hear that SAF fixed the problems on Android 9.

The following functions could crash the app:

So unless you’re using FileBrowserHelpers.IsPathDescendantOfAnother, the minimum SAF API Level shouldn’t be lower than 26 (unless I’ve missed another critical function).

I actually thought that this was the standard expected behaviour. I unfortunately don’t know if you can customize this behaviour.

Yeah i wasn’t paying enough attention to the resulting path. Now i see that i was duplicating part of the path, so of course it didn’t work.

Understood. Proceeding with SAF for 26+ and i’ll see what happens.

Okay i figured that was the case, i’m just very new to working with the native picker, so i thought i might be missing something obvious.

Thanks again

1 Like