@@ -88,12 +88,17 @@ public static bool ShouldUseSAF
88
88
return m_shouldUseSAF . Value ;
89
89
}
90
90
}
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
+ }
91
96
#endif
92
97
93
98
public static bool FileExists ( string path )
94
99
{
95
100
#if ! UNITY_EDITOR && UNITY_ANDROID
96
- if ( ShouldUseSAF )
101
+ if ( ShouldUseSAFForPath ( path ) )
97
102
return AJC . CallStatic < bool > ( "SAFEntryExists" , Context , path , false ) ;
98
103
#endif
99
104
return File . Exists ( path ) ;
@@ -102,16 +107,28 @@ public static bool FileExists( string path )
102
107
public static bool DirectoryExists ( string path )
103
108
{
104
109
#if ! UNITY_EDITOR && UNITY_ANDROID
105
- if ( ShouldUseSAF )
110
+ if ( ShouldUseSAFForPath ( path ) )
106
111
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
+ }
107
124
#endif
108
125
return Directory . Exists ( path ) ;
109
126
}
110
127
111
128
public static bool IsDirectory ( string path )
112
129
{
113
130
#if ! UNITY_EDITOR && UNITY_ANDROID
114
- if ( ShouldUseSAF )
131
+ if ( ShouldUseSAFForPath ( path ) )
115
132
return AJC . CallStatic < bool > ( "SAFEntryDirectory" , Context , path ) ;
116
133
#endif
117
134
if ( Directory . Exists ( path ) )
@@ -126,7 +143,7 @@ public static bool IsDirectory( string path )
126
143
public static string GetDirectoryName ( string path )
127
144
{
128
145
#if ! UNITY_EDITOR && UNITY_ANDROID
129
- if ( ShouldUseSAF )
146
+ if ( ShouldUseSAFForPath ( path ) )
130
147
return AJC . CallStatic < string > ( "GetParentDirectory" , Context , path ) ;
131
148
#endif
132
149
return Path . GetDirectoryName ( path ) ;
@@ -135,7 +152,7 @@ public static string GetDirectoryName( string path )
135
152
public static FileSystemEntry [ ] GetEntriesInDirectory ( string path , bool extractOnlyLastSuffixFromExtensions )
136
153
{
137
154
#if ! UNITY_EDITOR && UNITY_ANDROID
138
- if ( ShouldUseSAF )
155
+ if ( ShouldUseSAFForPath ( path ) )
139
156
{
140
157
string resultRaw = AJC . CallStatic < string > ( "OpenSAFFolder" , Context , path ) ;
141
158
int separatorIndex = resultRaw . IndexOf ( "<>" ) ;
@@ -235,7 +252,7 @@ public static FileSystemEntry[] GetEntriesInDirectory( string path, bool extract
235
252
public static string CreateFileInDirectory ( string directoryPath , string filename )
236
253
{
237
254
#if ! UNITY_EDITOR && UNITY_ANDROID
238
- if ( ShouldUseSAF )
255
+ if ( ShouldUseSAFForPath ( directoryPath ) )
239
256
return AJC . CallStatic < string > ( "CreateSAFEntry" , Context , directoryPath , false , filename ) ;
240
257
#endif
241
258
@@ -247,7 +264,7 @@ public static string CreateFileInDirectory( string directoryPath, string filenam
247
264
public static string CreateFolderInDirectory ( string directoryPath , string folderName )
248
265
{
249
266
#if ! UNITY_EDITOR && UNITY_ANDROID
250
- if ( ShouldUseSAF )
267
+ if ( ShouldUseSAFForPath ( directoryPath ) )
251
268
return AJC . CallStatic < string > ( "CreateSAFEntry" , Context , directoryPath , true , folderName ) ;
252
269
#endif
253
270
@@ -259,7 +276,7 @@ public static string CreateFolderInDirectory( string directoryPath, string folde
259
276
public static void WriteBytesToFile ( string targetPath , byte [ ] bytes )
260
277
{
261
278
#if ! UNITY_EDITOR && UNITY_ANDROID
262
- if ( ShouldUseSAF )
279
+ if ( ShouldUseSAFForPath ( targetPath ) )
263
280
{
264
281
File . WriteAllBytes ( TemporaryFilePath , bytes ) ;
265
282
AJC . CallStatic ( "WriteToSAFEntry" , Context , targetPath , TemporaryFilePath , false ) ;
@@ -274,7 +291,7 @@ public static void WriteBytesToFile( string targetPath, byte[] bytes )
274
291
public static void WriteTextToFile ( string targetPath , string text )
275
292
{
276
293
#if ! UNITY_EDITOR && UNITY_ANDROID
277
- if ( ShouldUseSAF )
294
+ if ( ShouldUseSAFForPath ( targetPath ) )
278
295
{
279
296
File . WriteAllText ( TemporaryFilePath , text ) ;
280
297
AJC . CallStatic ( "WriteToSAFEntry" , Context , targetPath , TemporaryFilePath , false ) ;
@@ -289,7 +306,7 @@ public static void WriteTextToFile( string targetPath, string text )
289
306
public static void AppendBytesToFile ( string targetPath , byte [ ] bytes )
290
307
{
291
308
#if ! UNITY_EDITOR && UNITY_ANDROID
292
- if ( ShouldUseSAF )
309
+ if ( ShouldUseSAFForPath ( targetPath ) )
293
310
{
294
311
File . WriteAllBytes ( TemporaryFilePath , bytes ) ;
295
312
AJC . CallStatic ( "WriteToSAFEntry" , Context , targetPath , TemporaryFilePath , true ) ;
@@ -307,7 +324,7 @@ public static void AppendBytesToFile( string targetPath, byte[] bytes )
307
324
public static void AppendTextToFile ( string targetPath , string text )
308
325
{
309
326
#if ! UNITY_EDITOR && UNITY_ANDROID
310
- if ( ShouldUseSAF )
327
+ if ( ShouldUseSAFForPath ( targetPath ) )
311
328
{
312
329
File . WriteAllText ( TemporaryFilePath , text ) ;
313
330
AJC . CallStatic ( "WriteToSAFEntry" , Context , targetPath , TemporaryFilePath , true ) ;
@@ -322,7 +339,7 @@ public static void AppendTextToFile( string targetPath, string text )
322
339
private static void AppendFileToFile ( string targetPath , string sourceFileToAppend )
323
340
{
324
341
#if ! UNITY_EDITOR && UNITY_ANDROID
325
- if ( ShouldUseSAF )
342
+ if ( ShouldUseSAFForPath ( targetPath ) )
326
343
{
327
344
AJC . CallStatic ( "WriteToSAFEntry" , Context , targetPath , sourceFileToAppend , true ) ;
328
345
return ;
@@ -341,7 +358,7 @@ private static void AppendFileToFile( string targetPath, string sourceFileToAppe
341
358
public static byte [ ] ReadBytesFromFile ( string sourcePath )
342
359
{
343
360
#if ! UNITY_EDITOR && UNITY_ANDROID
344
- if ( ShouldUseSAF )
361
+ if ( ShouldUseSAFForPath ( sourcePath ) )
345
362
{
346
363
AJC . CallStatic ( "ReadFromSAFEntry" , Context , sourcePath , TemporaryFilePath ) ;
347
364
byte [ ] result = File . ReadAllBytes ( TemporaryFilePath ) ;
@@ -355,7 +372,7 @@ public static byte[] ReadBytesFromFile( string sourcePath )
355
372
public static string ReadTextFromFile ( string sourcePath )
356
373
{
357
374
#if ! UNITY_EDITOR && UNITY_ANDROID
358
- if ( ShouldUseSAF )
375
+ if ( ShouldUseSAFForPath ( sourcePath ) )
359
376
{
360
377
AJC . CallStatic ( "ReadFromSAFEntry" , Context , sourcePath , TemporaryFilePath ) ;
361
378
string result = File . ReadAllText ( TemporaryFilePath ) ;
@@ -369,7 +386,7 @@ public static string ReadTextFromFile( string sourcePath )
369
386
public static void CopyFile ( string sourcePath , string destinationPath )
370
387
{
371
388
#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
373
390
{
374
391
AJC . CallStatic ( "CopyFile" , Context , sourcePath , destinationPath , false ) ;
375
392
return ;
@@ -381,7 +398,7 @@ public static void CopyFile( string sourcePath, string destinationPath )
381
398
public static void CopyDirectory ( string sourcePath , string destinationPath )
382
399
{
383
400
#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
385
402
{
386
403
AJC . CallStatic ( "CopyDirectory" , Context , sourcePath , destinationPath , false ) ;
387
404
return ;
@@ -406,7 +423,7 @@ private static void CopyDirectoryRecursively( DirectoryInfo sourceDirectory, str
406
423
public static void MoveFile ( string sourcePath , string destinationPath )
407
424
{
408
425
#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
410
427
{
411
428
AJC . CallStatic ( "CopyFile" , Context , sourcePath , destinationPath , true ) ;
412
429
return ;
@@ -418,7 +435,7 @@ public static void MoveFile( string sourcePath, string destinationPath )
418
435
public static void MoveDirectory ( string sourcePath , string destinationPath )
419
436
{
420
437
#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
422
439
{
423
440
AJC . CallStatic ( "CopyDirectory" , Context , sourcePath , destinationPath , true ) ;
424
441
return ;
@@ -430,7 +447,7 @@ public static void MoveDirectory( string sourcePath, string destinationPath )
430
447
public static string RenameFile ( string path , string newName )
431
448
{
432
449
#if ! UNITY_EDITOR && UNITY_ANDROID
433
- if ( ShouldUseSAF )
450
+ if ( ShouldUseSAFForPath ( path ) )
434
451
return AJC . CallStatic < string > ( "RenameSAFEntry" , Context , path , newName ) ;
435
452
#endif
436
453
string newPath = Path . Combine ( Path . GetDirectoryName ( path ) , newName ) ;
@@ -442,7 +459,7 @@ public static string RenameFile( string path, string newName )
442
459
public static string RenameDirectory ( string path , string newName )
443
460
{
444
461
#if ! UNITY_EDITOR && UNITY_ANDROID
445
- if ( ShouldUseSAF )
462
+ if ( ShouldUseSAFForPath ( path ) )
446
463
return AJC . CallStatic < string > ( "RenameSAFEntry" , Context , path , newName ) ;
447
464
#endif
448
465
string newPath = Path . Combine ( new DirectoryInfo ( path ) . Parent . FullName , newName ) ;
@@ -454,7 +471,7 @@ public static string RenameDirectory( string path, string newName )
454
471
public static void DeleteFile ( string path )
455
472
{
456
473
#if ! UNITY_EDITOR && UNITY_ANDROID
457
- if ( ShouldUseSAF )
474
+ if ( ShouldUseSAFForPath ( path ) )
458
475
{
459
476
AJC . CallStatic < bool > ( "DeleteSAFEntry" , Context , path ) ;
460
477
return ;
@@ -466,7 +483,7 @@ public static void DeleteFile( string path )
466
483
public static void DeleteDirectory ( string path )
467
484
{
468
485
#if ! UNITY_EDITOR && UNITY_ANDROID
469
- if ( ShouldUseSAF )
486
+ if ( ShouldUseSAFForPath ( path ) )
470
487
{
471
488
AJC . CallStatic < bool > ( "DeleteSAFEntry" , Context , path ) ;
472
489
return ;
@@ -478,7 +495,7 @@ public static void DeleteDirectory( string path )
478
495
public static string GetFilename ( string path )
479
496
{
480
497
#if ! UNITY_EDITOR && UNITY_ANDROID
481
- if ( ShouldUseSAF )
498
+ if ( ShouldUseSAFForPath ( path ) )
482
499
return AJC . CallStatic < string > ( "SAFEntryName" , Context , path ) ;
483
500
#endif
484
501
return Path . GetFileName ( path ) ;
@@ -487,7 +504,7 @@ public static string GetFilename( string path )
487
504
public static long GetFilesize ( string path )
488
505
{
489
506
#if ! UNITY_EDITOR && UNITY_ANDROID
490
- if ( ShouldUseSAF )
507
+ if ( ShouldUseSAFForPath ( path ) )
491
508
return AJC . CallStatic < long > ( "SAFEntrySize" , Context , path ) ;
492
509
#endif
493
510
return new FileInfo ( path ) . Length ;
@@ -497,7 +514,7 @@ public static System.DateTime GetLastModifiedDate( string path )
497
514
{
498
515
#if ! UNITY_EDITOR && UNITY_ANDROID
499
516
// Credit: https://stackoverflow.com/a/28504416/2373034
500
- if ( ShouldUseSAF )
517
+ if ( ShouldUseSAFForPath ( path ) )
501
518
return new System . DateTime ( 1970 , 1 , 1 , 0 , 0 , 0 ) . AddMilliseconds ( AJC . CallStatic < long > ( "SAFEntryLastModified" , Context , path ) ) ;
502
519
#endif
503
520
return new FileInfo ( path ) . LastWriteTime ;
0 commit comments