Skip to content

Commit e7a0a95

Browse files
committed
On Android 10+, when Storage Access Framework is used, paths that can be accessed via System.IO (like persistentDataPath) can still be browsed using System.IO and added as quick links
1 parent e9c32ec commit e7a0a95

File tree

3 files changed

+59
-38
lines changed

3 files changed

+59
-38
lines changed

Plugins/SimpleFileBrowser/Scripts/FileBrowser.cs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -557,11 +557,14 @@ private string CurrentPath
557557
get { return m_currentPath; }
558558
set
559559
{
560+
if( value != null )
561+
{
562+
value = value.Trim();
560563
#if !UNITY_EDITOR && UNITY_ANDROID
561-
if( !FileBrowserHelpers.ShouldUseSAF )
564+
if( !FileBrowserHelpers.ShouldUseSAFForPath( value ) )
562565
#endif
563-
if( value != null )
564-
value = GetPathWithoutTrailingDirectorySeparator( value.Trim() );
566+
value = GetPathWithoutTrailingDirectorySeparator( value );
567+
}
565568

566569
if( string.IsNullOrEmpty( value ) )
567570
{
@@ -597,7 +600,10 @@ private string CurrentPath
597600
forwardButton.interactable = currentPathIndex < pathsFollowed.Count - 1;
598601
#if !UNITY_EDITOR && UNITY_ANDROID
599602
if( FileBrowserHelpers.ShouldUseSAF )
600-
upButton.interactable = !string.IsNullOrEmpty( FileBrowserHelpers.GetDirectoryName( m_currentPath ) );
603+
{
604+
string parentPath = FileBrowserHelpers.GetDirectoryName( m_currentPath );
605+
upButton.interactable = !string.IsNullOrEmpty( parentPath ) && ( FileBrowserHelpers.ShouldUseSAFForPath( parentPath ) || FileBrowserHelpers.DirectoryExists( parentPath ) ); // DirectoryExists: Directory may not be accessible on Android 10+, this function checks that
606+
}
601607
else
602608
#endif
603609
{
@@ -627,7 +633,7 @@ private string CurrentPath
627633
// If a quick link points to this directory, highlight it
628634
#if !UNITY_EDITOR && UNITY_ANDROID
629635
// Path strings aren't deterministic on Storage Access Framework but the paths' absolute parts usually are
630-
if( FileBrowserHelpers.ShouldUseSAF )
636+
if( FileBrowserHelpers.ShouldUseSAFForPath( m_currentPath ) )
631637
{
632638
int SAFAbsolutePathSeparatorIndex = m_currentPath.LastIndexOf( '/' );
633639
if( SAFAbsolutePathSeparatorIndex >= 0 )
@@ -1274,7 +1280,7 @@ public void OnUpButtonPressed()
12741280
if( FileBrowserHelpers.ShouldUseSAF )
12751281
{
12761282
string parentPath = FileBrowserHelpers.GetDirectoryName( m_currentPath );
1277-
if( !string.IsNullOrEmpty( parentPath ) )
1283+
if( !string.IsNullOrEmpty( parentPath ) && ( FileBrowserHelpers.ShouldUseSAFForPath( parentPath ) || FileBrowserHelpers.DirectoryExists( parentPath ) ) ) // DirectoryExists: Directory may not be accessible on Android 10+, this function checks that
12781284
CurrentPath = parentPath;
12791285
}
12801286
else
@@ -1462,7 +1468,7 @@ public void OnSubmitButtonClicked()
14621468
}
14631469

14641470
#if !UNITY_EDITOR && UNITY_ANDROID
1465-
if( FileBrowserHelpers.ShouldUseSAF )
1471+
if( FileBrowserHelpers.ShouldUseSAFForPath( m_currentPath ) )
14661472
{
14671473
if( m_pickerMode == PickMode.Folders )
14681474
result[fileCount++] = FileBrowserHelpers.CreateFolderInDirectory( m_currentPath, filename );
@@ -1688,7 +1694,7 @@ public void OnItemSelected( FileBrowserItem item, bool isDoubleClick )
16881694
{
16891695
// Enter the directory
16901696
#if !UNITY_EDITOR && UNITY_ANDROID
1691-
if( FileBrowserHelpers.ShouldUseSAF )
1697+
if( FileBrowserHelpers.ShouldUseSAFForPath( m_currentPath ) )
16921698
{
16931699
for( int i = 0; i < validFileEntries.Count; i++ )
16941700
{
@@ -2211,7 +2217,7 @@ private bool AddQuickLink( Sprite icon, string name, string path )
22112217
return false;
22122218

22132219
#if !UNITY_EDITOR && UNITY_ANDROID
2214-
if( !FileBrowserHelpers.ShouldUseSAF )
2220+
if( !FileBrowserHelpers.ShouldUseSAFForPath( path ) )
22152221
#endif
22162222
{
22172223
if( !Directory.Exists( path ) )
@@ -2690,10 +2696,8 @@ public static IEnumerator WaitForLoadDialog( PickMode pickMode, bool allowMultiS
26902696

26912697
public static bool AddQuickLink( string name, string path, Sprite icon = null )
26922698
{
2693-
#if !UNITY_EDITOR && UNITY_ANDROID
2694-
if( FileBrowserHelpers.ShouldUseSAF )
2699+
if( string.IsNullOrEmpty( path ) || !FileBrowserHelpers.DirectoryExists( path ) )
26952700
return false;
2696-
#endif
26972701

26982702
if( !quickLinksInitialized )
26992703
{

Plugins/SimpleFileBrowser/Scripts/FileBrowserHelpers.cs

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,17 @@ public static bool ShouldUseSAF
8888
return m_shouldUseSAF.Value;
8989
}
9090
}
91+
92+
public static bool ShouldUseSAFForPath( string path ) // true: path should be managed with AJC (native helper class for Storage Access Framework), false: path should be managed with System.IO
93+
{
94+
return ShouldUseSAF && ( string.IsNullOrEmpty( path ) || path[0] != '/' );
95+
}
9196
#endif
9297

9398
public static bool FileExists( string path )
9499
{
95100
#if !UNITY_EDITOR && UNITY_ANDROID
96-
if( ShouldUseSAF )
101+
if( ShouldUseSAFForPath( path ) )
97102
return AJC.CallStatic<bool>( "SAFEntryExists", Context, path, false );
98103
#endif
99104
return File.Exists( path );
@@ -102,16 +107,28 @@ public static bool FileExists( string path )
102107
public static bool DirectoryExists( string path )
103108
{
104109
#if !UNITY_EDITOR && UNITY_ANDROID
105-
if( ShouldUseSAF )
110+
if( ShouldUseSAFForPath( path ) )
106111
return AJC.CallStatic<bool>( "SAFEntryExists", Context, path, true );
112+
else if( ShouldUseSAF ) // Directory.Exists returns true even for inaccessible directories on Android 10+, we need to check if the directory is accessible
113+
{
114+
try
115+
{
116+
Directory.GetFiles( path, "testtesttest" );
117+
return true;
118+
}
119+
catch
120+
{
121+
return false;
122+
}
123+
}
107124
#endif
108125
return Directory.Exists( path );
109126
}
110127

111128
public static bool IsDirectory( string path )
112129
{
113130
#if !UNITY_EDITOR && UNITY_ANDROID
114-
if( ShouldUseSAF )
131+
if( ShouldUseSAFForPath( path ) )
115132
return AJC.CallStatic<bool>( "SAFEntryDirectory", Context, path );
116133
#endif
117134
if( Directory.Exists( path ) )
@@ -126,7 +143,7 @@ public static bool IsDirectory( string path )
126143
public static string GetDirectoryName( string path )
127144
{
128145
#if !UNITY_EDITOR && UNITY_ANDROID
129-
if( ShouldUseSAF )
146+
if( ShouldUseSAFForPath( path ) )
130147
return AJC.CallStatic<string>( "GetParentDirectory", Context, path );
131148
#endif
132149
return Path.GetDirectoryName( path );
@@ -135,7 +152,7 @@ public static string GetDirectoryName( string path )
135152
public static FileSystemEntry[] GetEntriesInDirectory( string path, bool extractOnlyLastSuffixFromExtensions )
136153
{
137154
#if !UNITY_EDITOR && UNITY_ANDROID
138-
if( ShouldUseSAF )
155+
if( ShouldUseSAFForPath( path ) )
139156
{
140157
string resultRaw = AJC.CallStatic<string>( "OpenSAFFolder", Context, path );
141158
int separatorIndex = resultRaw.IndexOf( "<>" );
@@ -235,7 +252,7 @@ public static FileSystemEntry[] GetEntriesInDirectory( string path, bool extract
235252
public static string CreateFileInDirectory( string directoryPath, string filename )
236253
{
237254
#if !UNITY_EDITOR && UNITY_ANDROID
238-
if( ShouldUseSAF )
255+
if( ShouldUseSAFForPath( directoryPath ) )
239256
return AJC.CallStatic<string>( "CreateSAFEntry", Context, directoryPath, false, filename );
240257
#endif
241258

@@ -247,7 +264,7 @@ public static string CreateFileInDirectory( string directoryPath, string filenam
247264
public static string CreateFolderInDirectory( string directoryPath, string folderName )
248265
{
249266
#if !UNITY_EDITOR && UNITY_ANDROID
250-
if( ShouldUseSAF )
267+
if( ShouldUseSAFForPath( directoryPath ) )
251268
return AJC.CallStatic<string>( "CreateSAFEntry", Context, directoryPath, true, folderName );
252269
#endif
253270

@@ -259,7 +276,7 @@ public static string CreateFolderInDirectory( string directoryPath, string folde
259276
public static void WriteBytesToFile( string targetPath, byte[] bytes )
260277
{
261278
#if !UNITY_EDITOR && UNITY_ANDROID
262-
if( ShouldUseSAF )
279+
if( ShouldUseSAFForPath( targetPath ) )
263280
{
264281
File.WriteAllBytes( TemporaryFilePath, bytes );
265282
AJC.CallStatic( "WriteToSAFEntry", Context, targetPath, TemporaryFilePath, false );
@@ -274,7 +291,7 @@ public static void WriteBytesToFile( string targetPath, byte[] bytes )
274291
public static void WriteTextToFile( string targetPath, string text )
275292
{
276293
#if !UNITY_EDITOR && UNITY_ANDROID
277-
if( ShouldUseSAF )
294+
if( ShouldUseSAFForPath( targetPath ) )
278295
{
279296
File.WriteAllText( TemporaryFilePath, text );
280297
AJC.CallStatic( "WriteToSAFEntry", Context, targetPath, TemporaryFilePath, false );
@@ -289,7 +306,7 @@ public static void WriteTextToFile( string targetPath, string text )
289306
public static void AppendBytesToFile( string targetPath, byte[] bytes )
290307
{
291308
#if !UNITY_EDITOR && UNITY_ANDROID
292-
if( ShouldUseSAF )
309+
if( ShouldUseSAFForPath( targetPath ) )
293310
{
294311
File.WriteAllBytes( TemporaryFilePath, bytes );
295312
AJC.CallStatic( "WriteToSAFEntry", Context, targetPath, TemporaryFilePath, true );
@@ -307,7 +324,7 @@ public static void AppendBytesToFile( string targetPath, byte[] bytes )
307324
public static void AppendTextToFile( string targetPath, string text )
308325
{
309326
#if !UNITY_EDITOR && UNITY_ANDROID
310-
if( ShouldUseSAF )
327+
if( ShouldUseSAFForPath( targetPath ) )
311328
{
312329
File.WriteAllText( TemporaryFilePath, text );
313330
AJC.CallStatic( "WriteToSAFEntry", Context, targetPath, TemporaryFilePath, true );
@@ -322,7 +339,7 @@ public static void AppendTextToFile( string targetPath, string text )
322339
private static void AppendFileToFile( string targetPath, string sourceFileToAppend )
323340
{
324341
#if !UNITY_EDITOR && UNITY_ANDROID
325-
if( ShouldUseSAF )
342+
if( ShouldUseSAFForPath( targetPath ) )
326343
{
327344
AJC.CallStatic( "WriteToSAFEntry", Context, targetPath, sourceFileToAppend, true );
328345
return;
@@ -341,7 +358,7 @@ private static void AppendFileToFile( string targetPath, string sourceFileToAppe
341358
public static byte[] ReadBytesFromFile( string sourcePath )
342359
{
343360
#if !UNITY_EDITOR && UNITY_ANDROID
344-
if( ShouldUseSAF )
361+
if( ShouldUseSAFForPath( sourcePath ) )
345362
{
346363
AJC.CallStatic( "ReadFromSAFEntry", Context, sourcePath, TemporaryFilePath );
347364
byte[] result = File.ReadAllBytes( TemporaryFilePath );
@@ -355,7 +372,7 @@ public static byte[] ReadBytesFromFile( string sourcePath )
355372
public static string ReadTextFromFile( string sourcePath )
356373
{
357374
#if !UNITY_EDITOR && UNITY_ANDROID
358-
if( ShouldUseSAF )
375+
if( ShouldUseSAFForPath( sourcePath ) )
359376
{
360377
AJC.CallStatic( "ReadFromSAFEntry", Context, sourcePath, TemporaryFilePath );
361378
string result = File.ReadAllText( TemporaryFilePath );
@@ -369,7 +386,7 @@ public static string ReadTextFromFile( string sourcePath )
369386
public static void CopyFile( string sourcePath, string destinationPath )
370387
{
371388
#if !UNITY_EDITOR && UNITY_ANDROID
372-
if( ShouldUseSAF )
389+
if( ShouldUseSAF ) // No need to use ShouldUseSAFForPath because both SAF paths and raw file paths are handled on the native-side
373390
{
374391
AJC.CallStatic( "CopyFile", Context, sourcePath, destinationPath, false );
375392
return;
@@ -381,7 +398,7 @@ public static void CopyFile( string sourcePath, string destinationPath )
381398
public static void CopyDirectory( string sourcePath, string destinationPath )
382399
{
383400
#if !UNITY_EDITOR && UNITY_ANDROID
384-
if( ShouldUseSAF )
401+
if( ShouldUseSAF ) // No need to use ShouldUseSAFForPath because both SAF paths and raw directory paths are handled on the native-side
385402
{
386403
AJC.CallStatic( "CopyDirectory", Context, sourcePath, destinationPath, false );
387404
return;
@@ -406,7 +423,7 @@ private static void CopyDirectoryRecursively( DirectoryInfo sourceDirectory, str
406423
public static void MoveFile( string sourcePath, string destinationPath )
407424
{
408425
#if !UNITY_EDITOR && UNITY_ANDROID
409-
if( ShouldUseSAF )
426+
if( ShouldUseSAF ) // No need to use ShouldUseSAFForPath because both SAF paths and raw file paths are handled on the native-side
410427
{
411428
AJC.CallStatic( "CopyFile", Context, sourcePath, destinationPath, true );
412429
return;
@@ -418,7 +435,7 @@ public static void MoveFile( string sourcePath, string destinationPath )
418435
public static void MoveDirectory( string sourcePath, string destinationPath )
419436
{
420437
#if !UNITY_EDITOR && UNITY_ANDROID
421-
if( ShouldUseSAF )
438+
if( ShouldUseSAF ) // No need to use ShouldUseSAFForPath because both SAF paths and raw directory paths are handled on the native-side
422439
{
423440
AJC.CallStatic( "CopyDirectory", Context, sourcePath, destinationPath, true );
424441
return;
@@ -430,7 +447,7 @@ public static void MoveDirectory( string sourcePath, string destinationPath )
430447
public static string RenameFile( string path, string newName )
431448
{
432449
#if !UNITY_EDITOR && UNITY_ANDROID
433-
if( ShouldUseSAF )
450+
if( ShouldUseSAFForPath( path ) )
434451
return AJC.CallStatic<string>( "RenameSAFEntry", Context, path, newName );
435452
#endif
436453
string newPath = Path.Combine( Path.GetDirectoryName( path ), newName );
@@ -442,7 +459,7 @@ public static string RenameFile( string path, string newName )
442459
public static string RenameDirectory( string path, string newName )
443460
{
444461
#if !UNITY_EDITOR && UNITY_ANDROID
445-
if( ShouldUseSAF )
462+
if( ShouldUseSAFForPath( path ) )
446463
return AJC.CallStatic<string>( "RenameSAFEntry", Context, path, newName );
447464
#endif
448465
string newPath = Path.Combine( new DirectoryInfo( path ).Parent.FullName, newName );
@@ -454,7 +471,7 @@ public static string RenameDirectory( string path, string newName )
454471
public static void DeleteFile( string path )
455472
{
456473
#if !UNITY_EDITOR && UNITY_ANDROID
457-
if( ShouldUseSAF )
474+
if( ShouldUseSAFForPath( path ) )
458475
{
459476
AJC.CallStatic<bool>( "DeleteSAFEntry", Context, path );
460477
return;
@@ -466,7 +483,7 @@ public static void DeleteFile( string path )
466483
public static void DeleteDirectory( string path )
467484
{
468485
#if !UNITY_EDITOR && UNITY_ANDROID
469-
if( ShouldUseSAF )
486+
if( ShouldUseSAFForPath( path ) )
470487
{
471488
AJC.CallStatic<bool>( "DeleteSAFEntry", Context, path );
472489
return;
@@ -478,7 +495,7 @@ public static void DeleteDirectory( string path )
478495
public static string GetFilename( string path )
479496
{
480497
#if !UNITY_EDITOR && UNITY_ANDROID
481-
if( ShouldUseSAF )
498+
if( ShouldUseSAFForPath( path ) )
482499
return AJC.CallStatic<string>( "SAFEntryName", Context, path );
483500
#endif
484501
return Path.GetFileName( path );
@@ -487,7 +504,7 @@ public static string GetFilename( string path )
487504
public static long GetFilesize( string path )
488505
{
489506
#if !UNITY_EDITOR && UNITY_ANDROID
490-
if( ShouldUseSAF )
507+
if( ShouldUseSAFForPath( path ) )
491508
return AJC.CallStatic<long>( "SAFEntrySize", Context, path );
492509
#endif
493510
return new FileInfo( path ).Length;
@@ -497,7 +514,7 @@ public static System.DateTime GetLastModifiedDate( string path )
497514
{
498515
#if !UNITY_EDITOR && UNITY_ANDROID
499516
// Credit: https://stackoverflow.com/a/28504416/2373034
500-
if( ShouldUseSAF )
517+
if( ShouldUseSAFForPath( path ) )
501518
return new System.DateTime( 1970, 1, 1, 0, 0, 0 ).AddMilliseconds( AJC.CallStatic<long>( "SAFEntryLastModified", Context, path ) );
502519
#endif
503520
return new FileInfo( path ).LastWriteTime;

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "com.yasirkula.simplefilebrowser",
33
"displayName": "Simple File Browser",
4-
"version": "1.5.1",
4+
"version": "1.5.2",
55
"documentationUrl": "https://github.com/yasirkula/UnitySimpleFileBrowser",
66
"changelogUrl": "https://github.com/yasirkula/UnitySimpleFileBrowser/releases",
77
"licensesUrl": "https://github.com/yasirkula/UnitySimpleFileBrowser/blob/master/LICENSE.txt",

0 commit comments

Comments
 (0)