using Android.Content; using Tesses.VirtualFilesystem; using Android.App; using Android.Content.PM; using AndroidX.DocumentFile.Provider; using Tesses.VirtualFilesystem.Extensions; namespace Tesses.VirtualFilesystem.Filesystems; public class SAFFileSystem : SyncFileSystem { public const int RequestCode = 1447775022; public const string SharedPreferencesFile = "tesess_vfs"; public static void RequestDirectory(Activity app,bool canwrite=true,bool presistant=false,int reqCode=RequestCode) { Intent intent = new Intent(Intent.ActionOpenDocumentTree); intent.AddFlags(ActivityFlags.GrantReadUriPermission|(canwrite? ActivityFlags.GrantWriteUriPermission : 0)|(presistant?ActivityFlags.GrantPersistableUriPermission:0)); app.StartActivityForResult(intent,reqCode); } public static SAFFileSystem? GetSAFFromResponse(Context? app,Intent? intent) { if(app == null || intent == null) return null; var uri=intent.Data; if(uri != null) { return new SAFFileSystem(uri,app); } return null; } public static SAFFileSystem? GetSAFFromResponsePresistant(Context? app,Intent? intent,string key) { if(app == null || intent == null) return null; var uri=intent.Data; if(uri != null) { var r = app?.ContentResolver; if(r == null) return null; var rw = intent.Flags & (ActivityFlags.GrantReadUriPermission | ActivityFlags.GrantWriteUriPermission); r.TakePersistableUriPermission(uri,rw); var app2=app?.CreatePackageContext(app.PackageName,0); var sharedPrefs=app2?.GetSharedPreferences(SharedPreferencesFile,FileCreationMode.Private); sharedPrefs?.Edit()?.PutString(key,uri.ToString())?.Apply(); return new SAFFileSystem(uri,app); } return null; } public static void Revoke(Context? app,string key) { var app2=app?.CreatePackageContext(app.PackageName,0); var sharedPrefs=app2?.GetSharedPreferences(SharedPreferencesFile,FileCreationMode.Private); var res=sharedPrefs?.GetString(key,null); var r = app?.ContentResolver; if(r == null) return; if(string.IsNullOrWhiteSpace(res)) return; var uri = Android.Net.Uri.Parse(res); if(uri == null) return; r.ReleasePersistableUriPermission(uri,ActivityFlags.GrantReadUriPermission); sharedPrefs?.Edit()?.Remove(key)?.Apply(); } public static SAFFileSystem? GetSAFFromSharedStorage(Context? app,string key) { if(app == null) return null; var app2=app?.CreatePackageContext(app.PackageName,0); var sharedPrefs=app2?.GetSharedPreferences(SharedPreferencesFile,FileCreationMode.Private); var res=sharedPrefs?.GetString(key,null); if(string.IsNullOrWhiteSpace(res)) return null; var uri = Android.Net.Uri.Parse(res); if(uri == null) return null; return new SAFFileSystem(uri,app); } public SAFFileSystem(global::Android.Net.Uri uri,Context? ctx) { Uri = uri; Context = ctx; } public global::Android.Net.Uri Uri {get;set;} public Context? Context {get;set;} public override void CreateDirectory(UnixPath directory) { if(Context != null && Uri != null) { var dir = DocumentFile.FromTreeUri(Context,Uri); if(dir == null) return; foreach(var item in directory.Parts) { var dir2= dir?.FindFile(item); if(dir2 != null) { if(!dir2.IsDirectory && !dir2.IsFile && !dir2.IsVirtual) { dir = dir2.CreateDirectory(item); } else if(dir2.IsDirectory) { dir = dir2; } } } } } public override void DeleteDirectory(UnixPath path) { if(Context != null && Uri != null) { var dir = DocumentFile.FromTreeUri(Context,Uri); if(dir == null) return; for(int i = 0;i EnumerateFileSystemEntries(UnixPath path) { if(Context != null && Uri != null) { var dir = DocumentFile.FromTreeUri(Context,Uri); if(dir == null) yield break; if(path.IsRoot) { foreach(var _item in dir.ListFiles()) { yield return path / _item.Name; } yield break; } for(int i = 0;i "a" //FileMode.Create -> "w" //FileMode.CreateNew -> "w" //FileMode.Open -> "r or w" //FileMode.OpenOrCreate -> "r or w" //FileMode.Truncate -> "t" //"r", "w", "wt", "wa", "rw" or "rwt" Stream? strm = null; if(access == FileAccess.Read) strm = Context?.ContentResolver?.OpenInputStream(uri) ?? null; if(access == FileAccess.Write) if(mode == FileMode.Truncate) strm = Context?.ContentResolver?.OpenOutputStream(uri,"wt"); else if(mode == FileMode.Append) strm = Context?.ContentResolver?.OpenOutputStream(uri,"wa"); else strm = Context?.ContentResolver?.OpenOutputStream(uri,"w"); if(access == FileAccess.ReadWrite) if(mode == FileMode.Truncate) strm = Context?.ContentResolver?.OpenOutputStream(uri,"rwt"); else strm = Context?.ContentResolver?.OpenOutputStream(uri,"rw"); if(strm == null) throw new IOException("Failed to open stream"); return strm; } public override void SetCreationTime(UnixPath path, DateTime time) { } public override void SetLastAccessTime(UnixPath path, DateTime time) { } public override void SetLastWriteTime(UnixPath path, DateTime time) { } }