Skip to content

Commit 6980dfd

Browse files
committed
- Changed folderMode parameter to pickMode that supports picking files and folders simultaneously
- Added optional initialFilename parameter to prefill the filename input field - Files and folders are now sorted by their names (they weren't automatically sorted on some platforms) - Made AllFilesFilterText, FoldersFilterText and PickFolderQuickLinkText properties public static so that these labels can be localized or customized - Write External Storage permission is now added automatically on Android (no manual setup is needed)
1 parent d21f702 commit 6980dfd

19 files changed

+218
-139
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.yasirkula.unity">
3+
<uses-sdk android:targetSdkVersion="4" />
4+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="replace" />
5+
</manifest>

.github/README.md

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
- Supports runtime permissions on Android M+ and *Storage Access Framework* on Android Q+
2121
- Optimized using a recycled list view (makes *Instantiate* calls sparingly)
2222

23-
**NOTE:** Universal Windows Platform (UWP) is not supported!
23+
**NOTE:** *Universal Windows Platform (UWP)* and *WebGL* platforms aren't supported!
2424

2525
## INSTALLATION
2626

@@ -40,41 +40,39 @@ There are 5 ways to install this plugin:
4040

4141
If your project uses ProGuard, try adding the following line to ProGuard filters: `-keep class com.yasirkula.unity.* { *; }`
4242

43-
- **File browser doesn't show any files on Android**
43+
- **File browser doesn't show any files on Android 10+**
4444

45-
Make sure that you've set the **Write Permission** to **External (SDCard)** in *Player Settings*. On Android 10+, file browser uses *Storage Access Framework* and users must click the *Pick Folder* button first.
45+
File browser uses *Storage Access Framework* on these Android versions and users must first click the *Pick Folder* button in the quick links section
4646

47-
- **RequestPermission returns Permission.Denied on Android even though I've set "Write Permission" to "External (SDCard)"**
47+
- **RequestPermission returns Permission.Denied on Android**
4848

4949
Declare the `WRITE_EXTERNAL_STORAGE` permission manually in your [**Plugins/Android/AndroidManifest.xml** file](https://answers.unity.com/questions/982710/where-is-the-manifest-file-in-unity.html) with the `tools:node="replace"` attribute as follows: `<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="replace"/>` (you'll need to add the `xmlns:tools="http://schemas.android.com/tools"` attribute to the `<manifest ...>` element).
5050

5151
## HOW TO
5252

53-
*for Android*: set **Write Permission** to **External (SDCard)** in **Player Settings**
54-
5553
**NOTE:** On *Android Q (10)* or later, it is impossible to work with *File* APIs. On these devices, SimpleFileBrowser uses *Storage Access Framework (SAF)* to browse the files. However, paths returned by SAF are not File API compatible. To simulate the behaviour of the File API on all devices (including SAF), you can check out the **FileBrowserHelpers** functions. For reference, here is an example SAF path: `content://com.android.externalstorage.documents/tree/primary%3A/document/primary%3APictures`
5654

5755
First, add `using SimpleFileBrowser;` to your script.
5856

5957
The file browser can be shown either as a **save dialog** or a **load dialog**. In load mode, the returned path(s) always lead to existing files or folders. In save mode, the returned path(s) can point to non-existing files, as well. You can use the following functions to show the file browser:
6058

6159
```csharp
62-
public static bool ShowSaveDialog( OnSuccess onSuccess, OnCancel onCancel, bool folderMode = false, bool allowMultiSelection = false, string initialPath = null, string title = "Save", string saveButtonText = "Save" );
63-
public static bool ShowLoadDialog( OnSuccess onSuccess, OnCancel onCancel, bool folderMode = false, bool allowMultiSelection = false, string initialPath = null, string title = "Load", string loadButtonText = "Select" );
60+
public static bool ShowSaveDialog( OnSuccess onSuccess, OnCancel onCancel, PickMode pickMode, bool allowMultiSelection = false, string initialPath = null, string initialFilename = null, string title = "Save", string saveButtonText = "Save" );
61+
public static bool ShowLoadDialog( OnSuccess onSuccess, OnCancel onCancel, PickMode pickMode, bool allowMultiSelection = false, string initialPath = null, string initialFilename = null, string title = "Load", string loadButtonText = "Select" );
6462

6563
public delegate void OnSuccess( string[] paths );
6664
public delegate void OnCancel();
6765
```
6866

6967
There can only be one dialog active at a time. These functions will return *true* if the dialog is shown successfully (if no other dialog is active), *false* otherwise. You can query the **FileBrowser.IsOpen** property to see if there is an active dialog at the moment.
7068

71-
If user presses the *Cancel* button, **onCancel** callback is called. Otherwise, **onSuccess** callback is called with the paths of the selected files/folders as parameter. When **folderMode** is set to *true*, the file browser will show only folders and the user will pick folders instead of files. Setting **allowMultiSelection** to *true* will allow picking multiple files/folders.
69+
If user presses the *Cancel* button, **onCancel** callback is called. Otherwise, **onSuccess** callback is called with the paths of the selected files/folders as parameter. **pickMode** can be *Files*, *Folders* or *FilesAndFolders*. Setting **allowMultiSelection** to *true* will allow picking multiple files/folders.
7270

7371
There are also coroutine variants of these functions that will yield while the dialog is active:
7472

7573
```csharp
76-
public static IEnumerator WaitForSaveDialog( bool folderMode = false, bool allowMultiSelection = false, string initialPath = null, string title = "Save", string saveButtonText = "Save" );
77-
public static IEnumerator WaitForLoadDialog( bool folderMode = false, bool allowMultiSelection = false, string initialPath = null, string title = "Load", string loadButtonText = "Select" );
74+
public static IEnumerator WaitForSaveDialog( PickMode pickMode, bool allowMultiSelection = false, string initialPath = null, string initialFilename = null, string title = "Save", string saveButtonText = "Save" );
75+
public static IEnumerator WaitForLoadDialog( PickMode pickMode, bool allowMultiSelection = false, string initialPath = null, string initialFilename = null, string title = "Load", string loadButtonText = "Select" );
7876
```
7977

8078
After the dialog is closed, you can check the **FileBrowser.Success** property to see whether the user has selected some files/folders or cancelled the operation and if FileBrowser.Success is set to *true*, you can use the **FileBrowser.Result** property to get the paths of the selected files/folders.
@@ -205,17 +203,19 @@ public class FileBrowserTest : MonoBehaviour
205203
// onSuccess event: not registered (which means this dialog is pretty useless)
206204
// onCancel event: not registered
207205
// Save file/folder: file, Allow multiple selection: false
208-
// Initial path: "C:\", Title: "Save As", submit button text: "Save"
209-
// FileBrowser.ShowSaveDialog( null, null, false, false, "C:\\", "Save As", "Save" );
206+
// Initial path: "C:\", Initial filename: "Screenshot.png"
207+
// Title: "Save As", Submit button text: "Save"
208+
// FileBrowser.ShowSaveDialog( null, null, FileBrowser.PickMode.Files, false, "C:\\", "Screenshot.png", "Save As", "Save" );
210209
211210
// Show a select folder dialog
212211
// onSuccess event: print the selected folder's path
213212
// onCancel event: print "Canceled"
214213
// Load file/folder: folder, Allow multiple selection: false
215-
// Initial path: default (Documents), Title: "Select Folder", submit button text: "Select"
214+
// Initial path: default (Documents), Initial filename: empty
215+
// Title: "Select Folder", Submit button text: "Select"
216216
// FileBrowser.ShowLoadDialog( ( paths ) => { Debug.Log( "Selected: " + paths[0] ); },
217-
// () => { Debug.Log( "Canceled" ); },
218-
// true, false, null, "Select Folder", "Select" );
217+
// () => { Debug.Log( "Canceled" ); },
218+
// FileBrowser.PickMode.Folders, false, null, null, "Select Folder", "Select" );
219219
220220
// Coroutine example
221221
StartCoroutine( ShowLoadDialogCoroutine() );
@@ -224,9 +224,10 @@ public class FileBrowserTest : MonoBehaviour
224224
IEnumerator ShowLoadDialogCoroutine()
225225
{
226226
// Show a load file dialog and wait for a response from user
227-
// Load file/folder: file, Allow multiple selection: true
228-
// Initial path: default (Documents), Title: "Load File", submit button text: "Load"
229-
yield return FileBrowser.WaitForLoadDialog( false, true, null, "Load File", "Load" );
227+
// Load file/folder: both, Allow multiple selection: true
228+
// Initial path: default (Documents), Initial filename: empty
229+
// Title: "Load File", Submit button text: "Load"
230+
yield return FileBrowser.WaitForLoadDialog( FileBrowser.PickMode.FilesAndFolders, true, null, null, "Load Files and Folders", "Load" );
230231

231232
// Dialog is closed
232233
// Print whether the user has selected some files/folders or cancelled the operation (FileBrowser.Success)
Binary file not shown.

Plugins/SimpleFileBrowser/Android/SimpleFileBrowser.jar.meta renamed to Plugins/SimpleFileBrowser/Android/SimpleFileBrowser.aar.meta

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Binary file not shown.

Plugins/SimpleFileBrowser/Editor.meta

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System.IO;
2+
using UnityEditor;
3+
using UnityEngine;
4+
5+
public class SFBPostProcessBuild
6+
{
7+
[InitializeOnLoadMethod]
8+
public static void ValidatePlugin()
9+
{
10+
string jarPath = "Assets/Plugins/SimpleFileBrowser/Android/SimpleFileBrowser.jar";
11+
if( File.Exists( jarPath ) )
12+
{
13+
Debug.Log( "Deleting obsolete " + jarPath );
14+
AssetDatabase.DeleteAsset( jarPath );
15+
}
16+
}
17+
}

Plugins/SimpleFileBrowser/Editor/SFBPostProcessBuild.cs.meta

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "SimpleFileBrowser.Editor",
3+
"references": [],
4+
"includePlatforms": [
5+
"Editor"
6+
],
7+
"excludePlatforms": [],
8+
"allowUnsafeCode": false,
9+
"overrideReferences": false,
10+
"precompiledReferences": [],
11+
"autoReferenced": true,
12+
"defineConstraints": [],
13+
"versionDefines": [],
14+
"noEngineReferences": false
15+
}

Plugins/SimpleFileBrowser/Editor/SimpleFileBrowser.Editor.asmdef.meta

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Plugins/SimpleFileBrowser/README.txt

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,24 @@ E-mail: [email protected]
66
1. ABOUT
77
This plugin helps you show save/load dialogs during gameplay with its uGUI based file browser.
88

9-
2. HOW TO
10-
for Android: set Write Permission to External (SDCard) in Player Settings
119

10+
2. HOW TO
1211
The file browser can be shown either as a save dialog or a load dialog. In load mode, the returned path(s) always lead to existing files or folders. In save mode, the returned path(s) can point to non-existing files, as well.
1312

14-
3. SCRIPTING API
13+
14+
3. FAQ
15+
- Can't show the file browser on Android, it says "java.lang.ClassNotFoundException: com.yasirkula.unity.FileBrowserPermissionReceiver" in Logcat
16+
If your project uses ProGuard, try adding the following line to ProGuard filters: -keep class com.yasirkula.unity.* { *; }
17+
18+
- File browser doesn't show any files on Android 10+
19+
File browser uses Storage Access Framework on these Android versions and users must first click the "Pick Folder" button in the quick links section
20+
21+
- RequestPermission returns Permission.Denied on Android
22+
Declare the WRITE_EXTERNAL_STORAGE permission manually in your Plugins/Android/AndroidManifest.xml file as follows: <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="replace"/>
23+
You'll need to add the following attribute to the '<manifest ...>' element: xmlns:tools="http://schemas.android.com/tools"
24+
25+
26+
4. SCRIPTING API
1527
Please see the online documentation for a more in-depth documentation of the Scripting API: https://github.com/yasirkula/UnitySimpleFileBrowser
1628

1729
NOTE: On Android Q (10) or later, it is impossible to work with File APIs. On these devices, SimpleFileBrowser uses Storage Access Framework (SAF) to browse the files. However, paths returned by SAF are not File API compatible. To simulate the behaviour of the File API on all devices (including SAF), you can check out the FileBrowserHelpers functions. For reference, here is an example SAF path: content://com.android.externalstorage.documents/tree/primary%3A/document/primary%3APictures
@@ -20,16 +32,17 @@ NOTE: On Android Q (10) or later, it is impossible to work with File APIs. On th
2032
using SimpleFileBrowser;
2133

2234
public enum Permission { Denied = 0, Granted = 1, ShouldAsk = 2 };
35+
public enum PickMode { Files = 0, Folders = 1, FilesAndFolders = 2 };
2336

2437
public delegate void OnSuccess( string path );
2538
public delegate void OnCancel();
2639

2740
// Showing dialog
28-
bool ShowSaveDialog( OnSuccess onSuccess, OnCancel onCancel, bool folderMode = false, bool allowMultiSelection = false, string initialPath = null, string title = "Save", string saveButtonText = "Save" );
29-
bool ShowLoadDialog( OnSuccess onSuccess, OnCancel onCancel, bool folderMode = false, bool allowMultiSelection = false, string initialPath = null, string title = "Load", string loadButtonText = "Select" );
41+
bool ShowSaveDialog( OnSuccess onSuccess, OnCancel onCancel, PickMode pickMode, bool allowMultiSelection = false, string initialPath = null, string initialFilename = null, string title = "Save", string saveButtonText = "Save" );
42+
bool ShowLoadDialog( OnSuccess onSuccess, OnCancel onCancel, PickMode pickMode, bool allowMultiSelection = false, string initialPath = null, string initialFilename = null, string title = "Load", string loadButtonText = "Select" );
3043

31-
IEnumerator WaitForSaveDialog( bool folderMode = false, bool allowMultiSelection = false, string initialPath = null, string title = "Save", string saveButtonText = "Save" );
32-
IEnumerator WaitForLoadDialog( bool folderMode = false, bool allowMultiSelection = false, string initialPath = null, string title = "Load", string loadButtonText = "Select" );
44+
IEnumerator WaitForSaveDialog( PickMode pickMode, bool allowMultiSelection = false, string initialPath = null, string initialFilename = null, string title = "Save", string saveButtonText = "Save" );
45+
IEnumerator WaitForLoadDialog( PickMode pickMode, bool allowMultiSelection = false, string initialPath = null, string initialFilename = null, string title = "Load", string loadButtonText = "Select" );
3346

3447
// Force closing an open dialog
3548
void HideDialog( bool invokeCancelCallback = false );

0 commit comments

Comments
 (0)