Skip to content

Commit

Permalink
- Changed folderMode parameter to pickMode that supports picking file…
Browse files Browse the repository at this point in the history
…s 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)
  • Loading branch information
yasirkula committed Nov 29, 2020
1 parent d21f702 commit 6980dfd
Show file tree
Hide file tree
Showing 19 changed files with 218 additions and 139 deletions.
5 changes: 5 additions & 0 deletions .github/AAR Source (Android)/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.yasirkula.unity">
<uses-sdk android:targetSdkVersion="4" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="replace" />
</manifest>
39 changes: 20 additions & 19 deletions .github/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
- Supports runtime permissions on Android M+ and *Storage Access Framework* on Android Q+
- Optimized using a recycled list view (makes *Instantiate* calls sparingly)

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

## INSTALLATION

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

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

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

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.
File browser uses *Storage Access Framework* on these Android versions and users must first click the *Pick Folder* button in the quick links section

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

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).

## HOW TO

*for Android*: set **Write Permission** to **External (SDCard)** in **Player Settings**

**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`

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

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:

```csharp
public static bool ShowSaveDialog( OnSuccess onSuccess, OnCancel onCancel, bool folderMode = false, bool allowMultiSelection = false, string initialPath = null, string title = "Save", string saveButtonText = "Save" );
public static bool ShowLoadDialog( OnSuccess onSuccess, OnCancel onCancel, bool folderMode = false, bool allowMultiSelection = false, string initialPath = null, string title = "Load", string loadButtonText = "Select" );
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" );
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" );

public delegate void OnSuccess( string[] paths );
public delegate void OnCancel();
```

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.

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.
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.

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

```csharp
public static IEnumerator WaitForSaveDialog( bool folderMode = false, bool allowMultiSelection = false, string initialPath = null, string title = "Save", string saveButtonText = "Save" );
public static IEnumerator WaitForLoadDialog( bool folderMode = false, bool allowMultiSelection = false, string initialPath = null, string title = "Load", string loadButtonText = "Select" );
public static IEnumerator WaitForSaveDialog( PickMode pickMode, bool allowMultiSelection = false, string initialPath = null, string initialFilename = null, string title = "Save", string saveButtonText = "Save" );
public static IEnumerator WaitForLoadDialog( PickMode pickMode, bool allowMultiSelection = false, string initialPath = null, string initialFilename = null, string title = "Load", string loadButtonText = "Select" );
```

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.
Expand Down Expand Up @@ -205,17 +203,19 @@ public class FileBrowserTest : MonoBehaviour
// onSuccess event: not registered (which means this dialog is pretty useless)
// onCancel event: not registered
// Save file/folder: file, Allow multiple selection: false
// Initial path: "C:\", Title: "Save As", submit button text: "Save"
// FileBrowser.ShowSaveDialog( null, null, false, false, "C:\\", "Save As", "Save" );
// Initial path: "C:\", Initial filename: "Screenshot.png"
// Title: "Save As", Submit button text: "Save"
// FileBrowser.ShowSaveDialog( null, null, FileBrowser.PickMode.Files, false, "C:\\", "Screenshot.png", "Save As", "Save" );
// Show a select folder dialog
// onSuccess event: print the selected folder's path
// onCancel event: print "Canceled"
// Load file/folder: folder, Allow multiple selection: false
// Initial path: default (Documents), Title: "Select Folder", submit button text: "Select"
// Initial path: default (Documents), Initial filename: empty
// Title: "Select Folder", Submit button text: "Select"
// FileBrowser.ShowLoadDialog( ( paths ) => { Debug.Log( "Selected: " + paths[0] ); },
// () => { Debug.Log( "Canceled" ); },
// true, false, null, "Select Folder", "Select" );
// () => { Debug.Log( "Canceled" ); },
// FileBrowser.PickMode.Folders, false, null, null, "Select Folder", "Select" );
// Coroutine example
StartCoroutine( ShowLoadDialogCoroutine() );
Expand All @@ -224,9 +224,10 @@ public class FileBrowserTest : MonoBehaviour
IEnumerator ShowLoadDialogCoroutine()
{
// Show a load file dialog and wait for a response from user
// Load file/folder: file, Allow multiple selection: true
// Initial path: default (Documents), Title: "Load File", submit button text: "Load"
yield return FileBrowser.WaitForLoadDialog( false, true, null, "Load File", "Load" );
// Load file/folder: both, Allow multiple selection: true
// Initial path: default (Documents), Initial filename: empty
// Title: "Load File", Submit button text: "Load"
yield return FileBrowser.WaitForLoadDialog( FileBrowser.PickMode.FilesAndFolders, true, null, null, "Load Files and Folders", "Load" );

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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.
9 changes: 9 additions & 0 deletions Plugins/SimpleFileBrowser/Editor.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions Plugins/SimpleFileBrowser/Editor/SFBPostProcessBuild.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.IO;
using UnityEditor;
using UnityEngine;

public class SFBPostProcessBuild
{
[InitializeOnLoadMethod]
public static void ValidatePlugin()
{
string jarPath = "Assets/Plugins/SimpleFileBrowser/Android/SimpleFileBrowser.jar";
if( File.Exists( jarPath ) )
{
Debug.Log( "Deleting obsolete " + jarPath );
AssetDatabase.DeleteAsset( jarPath );
}
}
}
12 changes: 12 additions & 0 deletions Plugins/SimpleFileBrowser/Editor/SFBPostProcessBuild.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions Plugins/SimpleFileBrowser/Editor/SimpleFileBrowser.Editor.asmdef
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "SimpleFileBrowser.Editor",
"references": [],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 20 additions & 7 deletions Plugins/SimpleFileBrowser/README.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,24 @@ E-mail: [email protected]
1. ABOUT
This plugin helps you show save/load dialogs during gameplay with its uGUI based file browser.

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

2. HOW TO
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.

3. SCRIPTING API

3. FAQ
- Can't show the file browser on Android, it says "java.lang.ClassNotFoundException: com.yasirkula.unity.FileBrowserPermissionReceiver" in Logcat
If your project uses ProGuard, try adding the following line to ProGuard filters: -keep class com.yasirkula.unity.* { *; }

- File browser doesn't show any files on Android 10+
File browser uses Storage Access Framework on these Android versions and users must first click the "Pick Folder" button in the quick links section

- RequestPermission returns Permission.Denied on Android
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"/>
You'll need to add the following attribute to the '<manifest ...>' element: xmlns:tools="http://schemas.android.com/tools"


4. SCRIPTING API
Please see the online documentation for a more in-depth documentation of the Scripting API: https://github.com/yasirkula/UnitySimpleFileBrowser

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
Expand All @@ -20,16 +32,17 @@ NOTE: On Android Q (10) or later, it is impossible to work with File APIs. On th
using SimpleFileBrowser;

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

public delegate void OnSuccess( string path );
public delegate void OnCancel();

// Showing dialog
bool ShowSaveDialog( OnSuccess onSuccess, OnCancel onCancel, bool folderMode = false, bool allowMultiSelection = false, string initialPath = null, string title = "Save", string saveButtonText = "Save" );
bool ShowLoadDialog( OnSuccess onSuccess, OnCancel onCancel, bool folderMode = false, bool allowMultiSelection = false, string initialPath = null, string title = "Load", string loadButtonText = "Select" );
bool ShowSaveDialog( OnSuccess onSuccess, OnCancel onCancel, PickMode pickMode, bool allowMultiSelection = false, string initialPath = null, string initialFilename = null, string title = "Save", string saveButtonText = "Save" );
bool ShowLoadDialog( OnSuccess onSuccess, OnCancel onCancel, PickMode pickMode, bool allowMultiSelection = false, string initialPath = null, string initialFilename = null, string title = "Load", string loadButtonText = "Select" );

IEnumerator WaitForSaveDialog( bool folderMode = false, bool allowMultiSelection = false, string initialPath = null, string title = "Save", string saveButtonText = "Save" );
IEnumerator WaitForLoadDialog( bool folderMode = false, bool allowMultiSelection = false, string initialPath = null, string title = "Load", string loadButtonText = "Select" );
IEnumerator WaitForSaveDialog( PickMode pickMode, bool allowMultiSelection = false, string initialPath = null, string initialFilename = null, string title = "Save", string saveButtonText = "Save" );
IEnumerator WaitForLoadDialog( PickMode pickMode, bool allowMultiSelection = false, string initialPath = null, string initialFilename = null, string title = "Load", string loadButtonText = "Select" );

// Force closing an open dialog
void HideDialog( bool invokeCancelCallback = false );
Expand Down
Loading

0 comments on commit 6980dfd

Please sign in to comment.