tauri_plugin_android_fs/api/
android_fs.rs

1use sync_async::sync_async;
2use crate::*;
3use super::*;
4
5
6/// ***Root API***  
7/// 
8/// # Examples
9/// ```no_run
10/// fn example(app: &tauri::AppHandle) {
11///     use tauri_plugin_android_fs::AndroidFsExt as _;
12/// 
13///     let api = app.android_fs();
14/// }
15/// ```
16
17#[sync_async]
18pub struct AndroidFs<R: tauri::Runtime> {
19    #[cfg(target_os = "android")]
20    pub(crate) handle: tauri::plugin::PluginHandle<R>,
21
22    #[cfg(not(target_os = "android"))]
23    #[allow(unused)]
24    pub(crate) handle: std::marker::PhantomData<fn() -> R>
25}
26
27#[cfg(target_os = "android")]
28#[sync_async(
29    use(if_sync) impls::SyncImpls as Impls;
30    use(if_async) impls::AsyncImpls as Impls;
31)]
32impl<R: tauri::Runtime> AndroidFs<R> {
33    
34    #[always_sync]
35    pub(crate) fn impls(&self) -> Impls<'_, R> {
36        Impls { handle: &self.handle }
37    }
38}
39
40#[sync_async(
41    use(if_async) api_async::{FileOpener, FilePicker, AppStorage, PrivateStorage, PublicStorage};
42    use(if_sync) api_sync::{FileOpener, FilePicker, AppStorage, PrivateStorage, PublicStorage};
43)]
44impl<R: tauri::Runtime> AndroidFs<R> {
45
46    /// API of file storage that is available to other applications and users.
47    #[always_sync]
48    pub fn public_storage(&self) -> PublicStorage<'_, R> {
49        PublicStorage { handle: &self.handle }
50    }
51
52    /// API of file storage intended for the app's use only.
53    #[always_sync]
54    pub fn private_storage(&self) -> PrivateStorage<'_, R> {
55        PrivateStorage { handle: &self.handle }
56    }
57
58    /// API of file storage intended for the app's use.  
59    #[always_sync]
60    pub fn app_storage(&self) -> AppStorage<'_, R> {
61        AppStorage { handle: &self.handle }
62    }
63
64    /// API of file/dir picker.
65    #[always_sync]
66    pub fn file_picker(&self) -> FilePicker<'_, R> {
67        FilePicker { handle: &self.handle }
68    }
69
70    /// API of opening file/dir with other apps.
71    #[always_sync]
72    pub fn file_opener(&self) -> FileOpener<'_, R> {
73        FileOpener { handle: &self.handle }
74    }
75
76    /// Get the file or directory name.  
77    /// 
78    /// # Args
79    /// - ***uri*** :  
80    /// Target URI.  
81    /// Must be **readable**.
82    /// 
83    /// # Support
84    /// All Android version.
85    #[maybe_async]
86    pub fn get_name(&self, uri: &FileUri) -> Result<String> {
87        #[cfg(not(target_os = "android"))] {
88            Err(Error::NOT_ANDROID)
89        }
90        #[cfg(target_os = "android")] {
91            self.impls().get_entry_name(uri).await
92        }
93    }
94
95    /// Queries the provider to get the MIME type.
96    ///
97    /// For file URIs via [`FileUri::from_path`], the MIME type is determined from the file extension.  
98    /// In most other cases, it uses the MIME type that was associated with the file when it was created.  
99    /// If the MIME type is unknown or unset, it falls back to `"application/octet-stream"`.  
100    /// 
101    /// If the target is a directory, an error will occur.  
102    /// To check whether the target is a file or a directory, use [`AndroidFs::get_type`].  
103    /// 
104    /// # Args
105    /// - ***uri*** :  
106    /// Target file URI.  
107    /// Must be **readable**.
108    /// 
109    /// # Support
110    /// All Android version.
111    #[maybe_async]
112    pub fn get_mime_type(&self, uri: &FileUri) -> Result<String> {
113        #[cfg(not(target_os = "android"))] {
114            Err(Error::NOT_ANDROID)
115        }
116        #[cfg(target_os = "android")] {
117            self.impls().get_file_mime_type(uri).await
118        }
119    }
120
121    /// Gets the entry type.
122    ///
123    /// If the target is a directory, returns [`EntryType::Dir`].
124    ///
125    /// If the target is a file, returns [`EntryType::File { mime_type }`](EntryType::File).  
126    /// For file URIs via [`FileUri::from_path`], the MIME type is determined from the file extension.  
127    /// In most other cases, it uses the MIME type that was associated with the file when it was created.  
128    /// If the MIME type is unknown or unset, it falls back to `"application/octet-stream"`.  
129    /// 
130    /// # Args
131    /// - ***uri*** :  
132    /// Target URI.  
133    /// Must be **readable**.
134    /// 
135    /// # Support
136    /// All Android version.
137    #[maybe_async]
138    pub fn get_type(&self, uri: &FileUri) -> Result<EntryType> {
139        #[cfg(not(target_os = "android"))] {
140            Err(Error::NOT_ANDROID)
141        }
142        #[cfg(target_os = "android")] {
143            self.impls().get_entry_type(uri).await
144        }
145    }
146
147    /// Gets the entry information.
148    ///
149    /// # Args
150    /// - ***uri*** :  
151    /// Target URI.  
152    /// Must be **readable**.
153    /// 
154    /// # Support
155    /// All Android version.
156    #[maybe_async]
157    pub fn get_info(&self, uri: &FileUri) -> Result<Entry> {
158        #[cfg(not(target_os = "android"))] {
159            Err(Error::NOT_ANDROID)
160        }
161        #[cfg(target_os = "android")] {
162            self.impls().get_entry_info(uri).await
163        }
164    }
165
166    /// Queries the file system to get information about a file, directory.
167    /// 
168    /// # Args
169    /// - ***uri*** :  
170    /// Target URI.  
171    /// Must be **readable**.
172    /// 
173    /// # Note
174    /// This uses [`AndroidFs::open_file`] internally.
175    /// 
176    /// # Support
177    /// All Android version.
178    #[maybe_async]
179    pub fn get_metadata(&self, uri: &FileUri) -> Result<std::fs::Metadata> {
180        #[cfg(not(target_os = "android"))] {
181            Err(Error::NOT_ANDROID)
182        }
183        #[cfg(target_os = "android")] {
184            self.impls().get_entry_metadata(uri).await
185        }
186    }
187
188    /// Open the file in **readable** mode. 
189    /// 
190    /// # Note
191    /// If the target is a file on cloud storage or otherwise not physically present on the device,
192    /// the file provider may downloads the entire contents, and then opens it. 
193    /// As a result, this processing may take longer than with regular local files.
194    /// And files might be a pair of pipe or socket for streaming data.
195    /// 
196    /// # Args
197    /// - ***uri*** :  
198    /// Target file URI.  
199    /// This need to be **readable**.
200    /// 
201    /// # Support
202    /// All Android version.
203    #[maybe_async]
204    pub fn open_file_readable(&self, uri: &FileUri) -> Result<std::fs::File> {
205        #[cfg(not(target_os = "android"))] {
206            Err(Error::NOT_ANDROID)
207        }
208        #[cfg(target_os = "android")] {
209            self.impls().open_file_readable(uri).await
210        }
211    }
212
213    /// Open the file in **writable** mode.  
214    /// This truncates the existing contents.  
215    /// 
216    /// # Args
217    /// - ***uri*** :  
218    /// Target file URI.  
219    /// This need to be **writable**.
220    /// 
221    /// # Support
222    /// All Android version.
223    #[maybe_async]
224    pub fn open_file_writable(
225        &self, 
226        uri: &FileUri, 
227    ) -> Result<std::fs::File> {
228
229        #[cfg(not(target_os = "android"))] {
230            Err(Error::NOT_ANDROID)
231        }
232        #[cfg(target_os = "android")] {
233            self.impls().open_file_writable(uri).await
234        }
235    }
236
237    /// Open the file in the specified mode.  
238    /// 
239    /// # Note
240    /// 1. **Delay**:   
241    /// If the target is a file on cloud storage or otherwise not physically present on the device,
242    /// the file provider may downloads the entire contents, and then opens it. 
243    /// As a result, this processing may take longer than with regular local files.
244    /// And files might be a pair of pipe or socket for streaming data.
245    /// 
246    /// 2. **File mode restrictions**:  
247    /// Files provided by third-party apps may not support modes other than
248    /// [`FileAccessMode::Write`] or [`FileAccessMode::Read`]. 
249    /// However, [`FileAccessMode::Write`] does not guarantee
250    /// that existing contents will always be truncated.  
251    /// As a result, if the new contents are shorter than the original, the file may
252    /// become corrupted. To avoid this, consider using
253    /// [`AndroidFs::open_file_writable`], which
254    /// ensure that existing contents are truncated and also automatically apply the
255    /// maximum possible fallbacks.  
256    /// - <https://issuetracker.google.com/issues/180526528>
257    /// 
258    /// # Args
259    /// - ***uri*** :  
260    /// Target file URI.  
261    /// This must have corresponding permissions (read, write, or both) for the specified ***mode***.
262    /// 
263    /// - ***mode*** :  
264    /// Indicates how the file is opened and the permissions granted. 
265    /// 
266    /// # Support
267    /// All Android version.
268    #[maybe_async]
269    pub fn open_file(&self, uri: &FileUri, mode: FileAccessMode) -> Result<std::fs::File> {
270        #[cfg(not(target_os = "android"))] {
271            Err(Error::NOT_ANDROID)
272        }
273        #[cfg(target_os = "android")] {
274            self.impls().open_file(uri, mode).await
275        }
276    }
277 
278    /// For detailed documentation and notes, see [`AndroidFs::open_file`].  
279    ///
280    /// The modes specified in ***candidate_modes*** are tried in order.  
281    /// If the file can be opened, this returns the file along with the mode used.  
282    /// If all attempts fail, an error is returned.  
283    #[maybe_async]
284    pub fn open_file_with_fallback(
285        &self, 
286        uri: &FileUri, 
287        candidate_modes: impl IntoIterator<Item = FileAccessMode>
288    ) -> Result<(std::fs::File, FileAccessMode)> {
289
290        #[cfg(not(target_os = "android"))] {
291            Err(Error::NOT_ANDROID)
292        }
293        #[cfg(target_os = "android")] {
294            self.impls().open_file_with_fallback(uri, candidate_modes).await
295        }
296    }
297
298    /// Reads the entire contents of a file into a bytes vector.  
299    /// 
300    /// # Args
301    /// - ***uri*** :  
302    /// Target file URI.    
303    /// Must be **readable**.
304    /// 
305    /// # Support
306    /// All Android version.
307    #[maybe_async]
308    pub fn read(&self, uri: &FileUri) -> Result<Vec<u8>> {
309        #[cfg(not(target_os = "android"))] {
310            Err(Error::NOT_ANDROID)
311        }
312        #[cfg(target_os = "android")] {
313            self.impls().read_file(uri).await
314        }
315    }
316
317    /// Reads the entire contents of a file into a string.  
318    /// 
319    /// # Args
320    /// - ***uri*** :  
321    /// Target file URI.  
322    /// Must be **readable**.
323    /// 
324    /// # Support
325    /// All Android version.
326    #[maybe_async]
327    pub fn read_to_string(&self, uri: &FileUri) -> Result<String> {
328        #[cfg(not(target_os = "android"))] {
329            Err(Error::NOT_ANDROID)
330        }
331        #[cfg(target_os = "android")] {
332            self.impls().read_file_to_string(uri).await
333        }
334    }
335
336    /// Writes a slice as the entire contents of a file.  
337    /// This function will entirely replace its contents if it does exist.    
338    /// 
339    /// # Args
340    /// - ***uri*** :  
341    /// Target file URI.  
342    /// Must be **writable**.
343    /// 
344    /// # Support
345    /// All Android version.
346    #[maybe_async]
347    pub fn write(&self, uri: &FileUri, contents: impl AsRef<[u8]>) -> Result<()> {
348        #[cfg(not(target_os = "android"))] {
349            Err(Error::NOT_ANDROID)
350        }
351        #[cfg(target_os = "android")] {
352            self.impls().write_file(uri, contents).await
353        }
354    }
355
356    /// Copies the contents of the source file to the destination.  
357    /// If the destination already has contents, they are truncated before writing the source contents.  
358    /// 
359    /// # Args
360    /// - ***src*** :  
361    /// The URI of source file.   
362    /// Must be **readable**.
363    /// 
364    /// - ***dest*** :  
365    /// The URI of destination file.  
366    /// Must be **writable**.
367    /// 
368    /// # Support
369    /// All Android version.
370    #[maybe_async]
371    pub fn copy(&self, src: &FileUri, dest: &FileUri) -> Result<()> {
372        #[cfg(not(target_os = "android"))] {
373            Err(Error::NOT_ANDROID)
374        }
375        #[cfg(target_os = "android")] {
376            self.impls().copy_file(src, dest).await
377        }
378    }
379
380    /// Renames a file or directory to a new name, and return new URI.  
381    /// Even if the names conflict, the existing file will not be overwritten.  
382    /// 
383    /// Note that when files or folders (and their descendants) are renamed, their URIs will change, and any previously granted permissions will be lost.
384    /// In other words, this function returns a new URI without any permissions.
385    /// However, for files created in PublicStorage, the URI remains unchanged even after such operations, and all permissions are retained.
386    /// In this, this function returns the same URI as original URI.
387    ///
388    /// # Args
389    /// - ***uri*** :  
390    /// URI of target entry.  
391    /// 
392    /// - ***new_name*** :  
393    /// New name of target entry. 
394    /// This include extension if use.  
395    /// The behaviour in the same name already exists depends on the file provider.  
396    /// In the case of e.g. [`PublicStorage`], the suffix (e.g. `(1)`) is added to this name.  
397    /// In the case of files hosted by other applications, errors may occur.  
398    /// But at least, the existing file will not be overwritten.  
399    /// The system may sanitize these strings as needed, so those strings may not be used as it is.
400    /// 
401    /// # Support
402    /// All Android version.
403    #[maybe_async]
404    pub fn rename(&self, uri: &FileUri, new_name: impl AsRef<str>) -> Result<FileUri> {
405        #[cfg(not(target_os = "android"))] {
406            Err(Error::NOT_ANDROID)
407        }
408        #[cfg(target_os = "android")] {
409            self.impls().rename_entry(uri, new_name).await
410        }
411    }
412
413    /// Remove the file.
414    /// 
415    /// # Args
416    /// - ***uri*** :  
417    /// Target file URI.  
418    /// Must be **read-writable**.   
419    /// If not file, an error will occur.
420    /// 
421    /// # Support
422    /// All Android version.
423    #[maybe_async]
424    pub fn remove_file(&self, uri: &FileUri) -> Result<()> {
425        #[cfg(not(target_os = "android"))] {
426            Err(Error::NOT_ANDROID)
427        }
428        #[cfg(target_os = "android")] {
429            self.impls().remove_file(uri).await
430        }
431    }
432
433    /// Remove the **empty** directory.
434    /// 
435    /// # Args
436    /// - ***uri*** :  
437    /// Target directory URI.  
438    /// Must be **read-writable**.  
439    /// If not empty directory, an error will occur.
440    /// 
441    /// # Support
442    /// All Android version.
443    #[maybe_async]
444    pub fn remove_dir(&self, uri: &FileUri) -> Result<()> {
445        #[cfg(not(target_os = "android"))] {
446            Err(Error::NOT_ANDROID)
447        }
448        #[cfg(target_os = "android")] {
449            self.impls().remove_dir_if_empty(uri).await
450        }
451    }
452
453    /// Removes a directory and all its contents. Use carefully!
454    /// 
455    /// # Args
456    /// - ***uri*** :  
457    /// Target directory URI.  
458    /// Must be **read-writable**.  
459    /// If not directory, an error will occur.
460    /// 
461    /// # Support
462    /// All Android version.
463    #[maybe_async]
464    pub fn remove_dir_all(&self, uri: &FileUri) -> Result<()> {
465        #[cfg(not(target_os = "android"))] {
466            Err(Error::NOT_ANDROID)
467        }
468        #[cfg(target_os = "android")] {
469            self.impls().remove_dir_all(uri).await
470        }
471    }
472
473    /// Build a URI of an **existing** file located at the relative path from the specified directory.   
474    /// Error occurs, if the file does not exist.  
475    /// 
476    /// The permissions and validity period of the returned URI depend on the origin directory 
477    /// (e.g., the top directory selected by [`FilePicker::pick_dir`]) 
478    /// 
479    /// # Note
480    /// For [`AndroidFs::create_new_file`] and etc, the system may sanitize path strings as needed, so those strings may not be used as it is.
481    /// However, this function does not perform any sanitization, so the same ***relative_path*** may still fail.  
482    /// So consider using [`AndroidFs::create_new_file_and_return_relative_path`].
483    /// 
484    /// # Args
485    /// - ***uri*** :  
486    /// Base directory URI.  
487    /// Must be **readable**.  
488    /// 
489    /// - ***relative_path*** :
490    /// Relative path from base directory.
491    /// 
492    /// # Support
493    /// All Android version.
494    #[maybe_async]
495    pub fn resolve_file_uri(
496        &self, 
497        dir: &FileUri, 
498        relative_path: impl AsRef<std::path::Path>
499    ) -> Result<FileUri> {
500
501        #[cfg(not(target_os = "android"))] {
502            Err(Error::NOT_ANDROID)
503        }
504        #[cfg(target_os = "android")] {
505            self.impls().resolve_file_uri(dir, relative_path, false).await
506        }
507    }
508
509    /// Build a URI of an **existing** directory located at the relative path from the specified directory.   
510    /// Error occurs, if the directory does not exist.  
511    /// 
512    /// The permissions and validity period of the returned URI depend on the origin directory 
513    /// (e.g., the top directory selected by [`FilePicker::pick_dir`]) 
514    /// 
515    /// # Note
516    /// For [`AndroidFs::create_dir_all`] and etc, the system may sanitize path strings as needed, so those strings may not be used as it is.
517    /// However, this function does not perform any sanitization, so the same ***relative_path*** may still fail.  
518    /// So consider using [`AndroidFs::create_dir_all_and_return_relative_path`].
519    /// 
520    /// # Args
521    /// - ***uri*** :  
522    /// Base directory URI.  
523    /// Must be **readable**.  
524    /// 
525    /// - ***relative_path*** :
526    /// Relative path from base directory.
527    /// 
528    /// # Support
529    /// All Android version.
530    #[maybe_async]
531    pub fn resolve_dir_uri(
532        &self,
533        dir: &FileUri, 
534        relative_path: impl AsRef<std::path::Path>
535    ) -> Result<FileUri> {
536
537        #[cfg(not(target_os = "android"))] {
538            Err(Error::NOT_ANDROID)
539        }
540        #[cfg(target_os = "android")] {
541            self.impls().resolve_dir_uri(dir, relative_path, false).await
542        }
543    }
544
545    /// See [`AndroidFs::get_thumbnail`] for descriptions.  
546    /// 
547    /// If thumbnail does not wrote to dest, return false.
548    #[maybe_async]
549    pub fn get_thumbnail_to(
550        &self, 
551        src: &FileUri,
552        dest: &FileUri,
553        preferred_size: Size,
554        format: ImageFormat,
555    ) -> Result<bool> {
556
557        #[cfg(not(target_os = "android"))] {
558            Err(Error::NOT_ANDROID)
559        }
560        #[cfg(target_os = "android")] {
561            self.impls().get_file_thumbnail_to_file(src, dest, preferred_size, format).await
562        }
563    }
564
565    /// Get a file thumbnail.  
566    /// If thumbnail does not exist it, return None.
567    /// 
568    /// Note this does not cache. Please do it in your part if need.  
569    /// 
570    /// # Args
571    /// - ***uri*** :  
572    /// Targe file uri.  
573    /// Thumbnail availablty depends on the file provider.  
574    /// In general, images and videos are available.  
575    /// For file URIs via [`FileUri::from_path`], 
576    /// the file type must match the filename extension. 
577    /// In this case, the type is determined by the extension and generate thumbnails.  
578    /// Otherwise, thumbnails are provided through MediaStore, file provider, and etc.
579    /// 
580    /// - ***preferred_size*** :  
581    /// Optimal thumbnail size desired.  
582    /// This may return a thumbnail of a different size, 
583    /// but never more than about double the requested size. 
584    /// In any case, the aspect ratio is maintained.
585    /// 
586    /// - ***format*** :  
587    /// Thumbnail image format.   
588    /// If you’re not sure which one to use, [`ImageFormat::Jpeg`] is recommended.   
589    /// If you need transparency, use others.   
590    /// 
591    /// # Support
592    /// All Android version.
593    #[maybe_async]
594    pub fn get_thumbnail(
595        &self,
596        uri: &FileUri,
597        preferred_size: Size,
598        format: ImageFormat,
599    ) -> Result<Option<Vec<u8>>> {
600
601        #[cfg(not(target_os = "android"))] {
602            Err(Error::NOT_ANDROID)
603        }
604        #[cfg(target_os = "android")] {
605            self.impls().get_file_thumbnail(uri, preferred_size, format).await
606        }
607    }
608
609    /// Get a file thumbnail that encoded to base64 string.  
610    /// If thumbnail does not exist it, return None.
611    /// 
612    /// Note this does not cache. Please do it in your part if need.  
613    /// 
614    /// # Inner
615    /// This uses Kotlin's [`android.util.Base64.encodeToString(.., android.util.Base64.NO_WRAP)`](https://developer.android.com/reference/android/util/Base64#encodeToString(byte[],%20int)) internally. 
616    /// It is the same as [`base64::engine::general_purpose::STANDARD`](https://docs.rs/base64/0.22.1/base64/engine/general_purpose/constant.STANDARD.html) in `base64` crate.
617    /// 
618    /// # Args
619    /// - ***uri*** :  
620    /// Targe file uri.  
621    /// Thumbnail availablty depends on the file provider.  
622    /// In general, images and videos are available.  
623    /// For file URIs via [`FileUri::from_path`], 
624    /// the file type must match the filename extension. 
625    /// In this case, the type is determined by the extension and generate thumbnails.  
626    /// Otherwise, thumbnails are provided through MediaStore, file provider, and etc.
627    /// 
628    /// - ***preferred_size*** :  
629    /// Optimal thumbnail size desired.  
630    /// This may return a thumbnail of a different size, 
631    /// but never more than about double the requested size. 
632    /// In any case, the aspect ratio is maintained.
633    /// 
634    /// - ***format*** :  
635    /// Thumbnail image format.   
636    /// If you’re not sure which one to use, [`ImageFormat::Jpeg`] is recommended.   
637    /// If you need transparency, use others.   
638    /// 
639    /// # Support
640    /// All Android version.
641    #[maybe_async]
642    pub fn get_thumbnail_base64(
643        &self,
644        uri: &FileUri,
645        preferred_size: Size,
646        format: ImageFormat,
647    ) -> Result<Option<String>> {
648
649        #[cfg(not(target_os = "android"))] {
650            Err(Error::NOT_ANDROID)
651        }
652        #[cfg(target_os = "android")] {
653            self.impls().get_file_thumbnail_base64(uri, preferred_size, format).await
654        }
655    }
656
657    /// Creates a new empty file in the specified location and returns a URI.   
658    /// 
659    /// The permissions and validity period of the returned URIs depend on the origin directory 
660    /// (e.g., the top directory selected by [`FilePicker::pick_dir`]) 
661    /// 
662    /// # Args  
663    /// - ***dir*** :  
664    /// The URI of the base directory.  
665    /// Must be **read-write**.
666    ///  
667    /// - ***relative_path*** :  
668    /// The file path relative to the base directory.  
669    /// Any missing parent directories will be created automatically.  
670    /// If a file with the same name already exists, a sequential number may be appended to ensure uniqueness.  
671    /// If the file has no extension, one may be inferred from ***mime_type*** and appended to the file name.  
672    /// Strings may also be sanitized as needed, so they may not be used exactly as provided.
673    /// Note those operation may vary depending on the file provider.  
674    /// 
675    /// - ***mime_type*** :  
676    /// The MIME type of the file to be created.  
677    /// If this is None, MIME type is inferred from the extension of ***relative_path***
678    /// and if that fails, `application/octet-stream` is used.  
679    ///  
680    /// # Support
681    /// All Android version.
682    #[maybe_async]
683    pub fn create_new_file(
684        &self,
685        dir: &FileUri, 
686        relative_path: impl AsRef<std::path::Path>, 
687        mime_type: Option<&str>
688    ) -> Result<FileUri> {
689
690        #[cfg(not(target_os = "android"))] {
691            Err(Error::NOT_ANDROID)
692        }
693        #[cfg(target_os = "android")] {
694            self.impls().create_new_file(dir, relative_path, mime_type).await
695        }
696    }
697
698    /// Creates a new empty file in the specified location and returns a URI and relative path.   
699    /// 
700    /// The returned relative path may be sanitized and have a suffix appended to the file name, 
701    /// so it may differ from the input relative path.
702    /// And it is a logical path within the file provider and 
703    /// available for [`AndroidFs::resolve_file_uri`].
704    /// 
705    /// The permissions and validity period of the returned URIs depend on the origin directory 
706    /// (e.g., the top directory selected by [`FilePicker::pick_dir`]) 
707    /// 
708    /// # Args  
709    /// - ***dir*** :  
710    /// The URI of the base directory.  
711    /// Must be **read-write**.
712    ///  
713    /// - ***relative_path*** :  
714    /// The file path relative to the base directory.  
715    /// Any missing parent directories will be created automatically.  
716    /// If a file with the same name already exists, a sequential number may be appended to ensure uniqueness.  
717    /// If the file has no extension, one may be inferred from ***mime_type*** and appended to the file name.  
718    /// Strings may also be sanitized as needed, so they may not be used exactly as provided.
719    /// Note those operation may vary depending on the file provider.  
720    ///  
721    /// - ***mime_type*** :  
722    /// The MIME type of the file to be created.  
723    /// If this is None, MIME type is inferred from the extension of ***relative_path***
724    /// and if that fails, `application/octet-stream` is used.  
725    ///  
726    /// # Support
727    /// All Android version.
728    #[maybe_async]
729    pub fn create_new_file_and_return_relative_path(
730        &self,
731        dir: &FileUri, 
732        relative_path: impl AsRef<std::path::Path>, 
733        mime_type: Option<&str>
734    ) -> Result<(FileUri, std::path::PathBuf)> {
735
736        #[cfg(not(target_os = "android"))] {
737            Err(Error::NOT_ANDROID)
738        }
739        #[cfg(target_os = "android")] {
740            self.impls().create_new_file_and_retrun_relative_path(dir, relative_path, mime_type).await
741        }
742    }
743
744    /// Creates a directory and it's parents at the specified location if they are missing,
745    /// then return the URI.  
746    /// If it already exists, do nothing and just return the direcotry uri.
747    /// 
748    /// [`AndroidFs::create_new_file`] does this automatically, so there is no need to use it together.
749    /// 
750    /// # Args  
751    /// - ***dir*** :  
752    /// The URI of the base directory.  
753    /// Must be **read-write**.
754    ///  
755    /// - ***relative_path*** :  
756    /// The directory path relative to the base directory.    
757    /// Any missing parent directories will be created automatically.  
758    /// Strings may also be sanitized as needed, so they may not be used exactly as provided.
759    /// Note this sanitization may vary depending on the file provider.  
760    ///  
761    /// # Support
762    /// All Android version.
763    #[maybe_async]
764    pub fn create_dir_all(
765        &self,
766        dir: &FileUri, 
767        relative_path: impl AsRef<std::path::Path>, 
768    ) -> Result<FileUri> {
769
770        #[cfg(not(target_os = "android"))] {
771            Err(Error::NOT_ANDROID)
772        }
773        #[cfg(target_os = "android")] {
774            self.impls().create_dir_all(dir, relative_path).await
775        }
776    }
777
778    /// Recursively create a directory and all of its parent components if they are missing,
779    /// then return the URI and relative path.  
780    /// 
781    /// The returned relative path may be sanitized, 
782    /// so it may differ from the input relative path.
783    /// And it is a logical path within the file provider and 
784    /// available for [`AndroidFs::resolve_dir_uri`].
785    /// 
786    /// [`AndroidFs::create_new_file`] does this automatically, so there is no need to use it together.
787    /// 
788    /// # Args  
789    /// - ***dir*** :  
790    /// The URI of the base directory.  
791    /// Must be **read-write**.
792    ///  
793    /// - ***relative_path*** :  
794    /// The directory path relative to the base directory.    
795    /// Any missing parent directories will be created automatically.  
796    /// Strings may also be sanitized as needed, so they may not be used exactly as provided.
797    /// Note this sanitization may vary depending on the file provider.  
798    ///  
799    /// # Support
800    /// All Android version.
801    #[maybe_async]
802    pub fn create_dir_all_and_return_relative_path(
803        &self,
804        dir: &FileUri, 
805        relative_path: impl AsRef<std::path::Path>, 
806    ) -> Result<(FileUri, std::path::PathBuf)> {
807
808        #[cfg(not(target_os = "android"))] {
809            Err(Error::NOT_ANDROID)
810        }
811        #[cfg(target_os = "android")] {
812            self.impls().create_dir_all_and_return_relative_path(dir, relative_path).await
813        }
814    }
815
816    /// Returns the child files and directories of the specified directory.  
817    /// The order of the entries is not guaranteed.  
818    /// 
819    /// The permissions and validity period of the returned URIs depend on the origin directory 
820    /// (e.g., the top directory selected by [`FilePicker::pick_dir`])  
821    /// 
822    /// This retrieves all metadata including `uri`, `name`, `last_modified`, `len`, and `mime_type`. 
823    /// If only specific information is needed, 
824    /// using [`AndroidFs::read_dir_with_options`] will improve performance.
825    /// 
826    /// # Note
827    /// The returned type is an iterator, but the file system call is not executed lazily.  
828    /// Instead, all data is retrieved at once.  
829    /// For directories containing thousands or even tens of thousands of entries,  
830    /// this function may take several seconds to complete.  
831    /// The returned iterator itself is low-cost, as it only performs lightweight data formatting.
832    /// 
833    /// # Args
834    /// - ***uri*** :  
835    /// Target directory URI.  
836    /// Must be **readable**.
837    /// 
838    /// # Support
839    /// All Android version.
840    #[maybe_async]
841    pub fn read_dir(&self, uri: &FileUri) -> Result<impl Iterator<Item = Entry>> {
842        let entries = self.read_dir_with_options(uri, EntryOptions::ALL).await?
843            .map(Entry::try_from)
844            .filter_map(Result::ok);
845        
846        Ok(entries)
847    }
848
849    /// Returns the child files and directories of the specified directory.  
850    /// The order of the entries is not guaranteed.  
851    /// 
852    /// The permissions and validity period of the returned URIs depend on the origin directory 
853    /// (e.g., the top directory selected by [`FilePicker::pick_dir`])  
854    /// 
855    /// # Note
856    /// The returned type is an iterator, but the file system call is not executed lazily.  
857    /// Instead, all data is retrieved at once.  
858    /// For directories containing thousands or even tens of thousands of entries,  
859    /// this function may take several seconds to complete.  
860    /// The returned iterator itself is low-cost, as it only performs lightweight data formatting.
861    /// 
862    /// # Args
863    /// - ***uri*** :  
864    /// Target directory URI.  
865    /// Must be **readable**.
866    /// 
867    /// # Support
868    /// All Android version.
869    #[maybe_async]
870    pub fn read_dir_with_options(
871        &self, 
872        uri: &FileUri, 
873        options: EntryOptions
874    ) -> Result<impl Iterator<Item = OptionalEntry>> {
875        
876        #[cfg(not(target_os = "android"))] {
877            Err::<std::iter::Empty<_>, _>(Error::NOT_ANDROID)
878        }
879        #[cfg(target_os = "android")] {
880            self.impls().read_dir_with_options(uri, options).await
881        }
882    }
883
884    /// Take persistent permission to access the file, directory and its descendants.  
885    /// This is a prolongation of an already acquired permission, not the acquisition of a new one.  
886    /// 
887    /// This works by just calling, without displaying any confirmation to the user.
888    /// 
889    /// Note that [there is a limit to the total number of URI that can be made persistent by this function.](https://stackoverflow.com/questions/71099575/should-i-release-persistableuripermission-when-a-new-storage-location-is-chosen/71100621#71100621)  
890    /// Therefore, it is recommended to relinquish the unnecessary persisted URI by [`AndroidFs::release_persisted_uri_permission`] or [`AndroidFs::release_all_persisted_uri_permissions`].  
891    /// Persisted permissions may be relinquished by other apps, user, or by moving/removing entries.
892    /// So check by [`AndroidFs::check_persisted_uri_permission`].  
893    /// And you can retrieve the list of persisted uris using [`AndroidFs::get_all_persisted_uri_permissions`].
894    /// 
895    /// # Args
896    /// - **uri** :  
897    /// URI of the target file or directory.   
898    /// This must be a URI taken from following :  
899    ///     - [`FilePicker::pick_files`]  
900    ///     - [`FilePicker::pick_file`]  
901    ///     - [`FilePicker::pick_visual_medias`]  
902    ///     - [`FilePicker::pick_visual_media`]  
903    ///     - [`FilePicker::pick_dir`]  
904    ///     - [`FilePicker::save_file`]  
905    ///     - [`AndroidFs::resolve_file_uri`], [`AndroidFs::resolve_dir_uri`], [`AndroidFs::read_dir`], [`AndroidFs::create_new_file`], [`AndroidFs::create_dir_all`] :  
906    ///     If use URI from thoese fucntions, the permissions of the origin directory URI is persisted, not a entry iteself by this function. 
907    ///     Because the permissions and validity period of the descendant entry URIs depend on the origin directory.   
908    /// 
909    /// # Support
910    /// All Android version. 
911    #[maybe_async]
912    pub fn take_persistable_uri_permission(&self, uri: &FileUri) -> Result<()> {
913        #[cfg(not(target_os = "android"))] {
914            Err(Error::NOT_ANDROID)
915        }
916        #[cfg(target_os = "android")] {
917            self.impls().take_persistable_uri_permission(uri).await
918        }
919    }
920
921    /// Check a persisted URI permission grant by [`AndroidFs::take_persistable_uri_permission`].  
922    /// Returns false if there are only non-persistent permissions or no permissions.
923    /// 
924    /// # Args
925    /// - **uri** :  
926    /// URI of the target file or directory.  
927    /// This must be a URI taken from following :  
928    ///     - [`FilePicker::pick_files`]  
929    ///     - [`FilePicker::pick_file`]  
930    ///     - [`FilePicker::pick_visual_medias`]  
931    ///     - [`FilePicker::pick_visual_media`]  
932    ///     - [`FilePicker::pick_dir`]  
933    ///     - [`FilePicker::save_file`]  
934    ///     - [`AndroidFs::resolve_file_uri`], [`AndroidFs::resolve_dir_uri`], [`AndroidFs::read_dir`], [`AndroidFs::create_new_file`], [`AndroidFs::create_dir_all`] :  
935    ///     If use URI from thoese fucntions, the permissions of the origin directory URI is checked, not a entry iteself by this function. 
936    ///     Because the permissions and validity period of the descendant entry URIs depend on the origin directory.   
937    /// 
938    /// - **permission** :  
939    /// The permission you want to check.  
940    /// 
941    /// # Support
942    /// All Android version.
943    #[maybe_async]
944    pub fn check_persisted_uri_permission(
945        &self, 
946        uri: &FileUri, 
947        permission: UriPermission
948    ) -> Result<bool> {
949
950        #[cfg(not(target_os = "android"))] {
951            Err(Error::NOT_ANDROID)
952        }
953        #[cfg(target_os = "android")] {
954            self.impls().check_persisted_uri_permission(uri, permission).await
955        }
956    }
957
958    /// Return list of all persisted URIs that have been persisted by [`AndroidFs::take_persistable_uri_permission`] and currently valid.   
959    /// 
960    /// # Support
961    /// All Android version.
962    #[maybe_async]
963    pub fn get_all_persisted_uri_permissions(&self) -> Result<impl Iterator<Item = PersistedUriPermissionState>> {
964        #[cfg(not(target_os = "android"))] {
965            Err::<std::iter::Empty<_>, _>(Error::NOT_ANDROID)
966        }
967        #[cfg(target_os = "android")] {
968            self.impls().get_all_persisted_uri_permissions().await
969        }
970    }
971
972    /// Relinquish a persisted URI permission grant by [`AndroidFs::take_persistable_uri_permission`].   
973    /// 
974    /// # Args
975    /// - ***uri*** :  
976    /// URI of the target file or directory.  
977    ///
978    /// # Support
979    /// All Android version.
980    #[maybe_async]
981    pub fn release_persisted_uri_permission(&self, uri: &FileUri) -> Result<()> {
982        #[cfg(not(target_os = "android"))] {
983            Err(Error::NOT_ANDROID)
984        }
985        #[cfg(target_os = "android")] {
986            self.impls().release_persisted_uri_permission(uri).await
987        }
988    }
989
990    /// Relinquish a all persisted uri permission grants by [`AndroidFs::take_persistable_uri_permission`].  
991    /// 
992    /// # Support
993    /// All Android version.
994    #[maybe_async]
995    pub fn release_all_persisted_uri_permissions(&self) -> Result<()> {
996        #[cfg(not(target_os = "android"))] {
997            Err(Error::NOT_ANDROID)
998        }
999        #[cfg(target_os = "android")] {
1000            self.impls().release_all_persisted_uri_permissions().await
1001        }
1002    }
1003
1004    /// See [`AppStorage::get_volumes`] or [`PublicStorage::get_volumes`] for details.
1005    /// 
1006    /// The difference is that this does not perform any filtering.
1007    /// You can it by [`StorageVolume { is_available_for_app_storage, is_available_for_public_storage, .. } `](StorageVolume).
1008    #[maybe_async]
1009    pub fn get_volumes(&self) -> Result<Vec<StorageVolume>> {
1010        #[cfg(not(target_os = "android"))] {
1011            Err(Error::NOT_ANDROID)
1012        }
1013        #[cfg(target_os = "android")] {
1014            self.impls().get_available_storage_volumes().await
1015        }
1016    }
1017
1018    /// See [`AppStorage::get_primary_volume`] or [`PublicStorage::get_primary_volume`] for details.
1019    /// 
1020    /// The difference is that this does not perform any filtering.
1021    /// You can it by [`StorageVolume { is_available_for_app_storage, is_available_for_public_storage, .. } `](StorageVolume).
1022    #[maybe_async]
1023    pub fn get_primary_volume(&self) -> Result<Option<StorageVolume>> {
1024        #[cfg(not(target_os = "android"))] {
1025            Err(Error::NOT_ANDROID)
1026        }
1027        #[cfg(target_os = "android")] {
1028            self.impls().get_primary_storage_volume_if_available().await
1029        }
1030    }
1031
1032    /// Builds the storage volume root URI.  
1033    /// 
1034    /// This should only be used as `initial_location` in the file picker, such as [`FilePicker::pick_files`]. 
1035    /// It must not be used for any other purpose.  
1036    /// 
1037    /// This is useful when selecting save location, 
1038    /// but when selecting existing entries, `initial_location` is often better with None.
1039    /// 
1040    /// # Args  
1041    /// - ***volume_id*** :  
1042    /// ID of the storage volume, such as internal storage, SD card, etc.  
1043    /// If `None` is provided, [`the primary storage volume`](AndroidFs::get_primary_volume) will be used.  
1044    /// 
1045    /// # Support
1046    /// All Android version.
1047    #[maybe_async]
1048    pub fn resolve_root_initial_location(&self, volume_id: Option<&StorageVolumeId>) -> Result<FileUri> {
1049        #[cfg(not(target_os = "android"))] {
1050            Err(Error::NOT_ANDROID)
1051        }
1052        #[cfg(target_os = "android")] {
1053            self.impls().resolve_root_initial_location(volume_id).await
1054        }
1055    }
1056
1057    /// Verify whether this plugin is available.  
1058    /// 
1059    /// On Android, this returns true.  
1060    /// On other platforms, this returns false.  
1061    #[always_sync]
1062    pub fn is_available(&self) -> bool {
1063        cfg!(target_os = "android")
1064    }
1065
1066    /// Get the api level of this Android device.
1067    /// 
1068    /// The correspondence table between API levels and Android versions can be found following.  
1069    /// <https://developer.android.com/guide/topics/manifest/uses-sdk-element#api-level-table>
1070    /// 
1071    /// If you want the constant value of the API level from an Android version, there is the [`api_level`] module.
1072    /// 
1073    /// # Table
1074    /// | Android version  | API Level |
1075    /// |------------------|-----------|
1076    /// | 16.0             | 36        |
1077    /// | 15.0             | 35        |
1078    /// | 14.0             | 34        |
1079    /// | 13.0             | 33        |
1080    /// | 12L              | 32        |
1081    /// | 12.0             | 31        |
1082    /// | 11.0             | 30        |
1083    /// | 10.0             | 29        |
1084    /// | 9.0              | 28        |
1085    /// | 8.1              | 27        |
1086    /// | 8.0              | 26        |
1087    /// | 7.1 - 7.1.2      | 25        |
1088    /// | 7.0              | 24        |
1089    /// 
1090    /// Tauri does not support Android versions below 7.
1091    #[always_sync]
1092    pub fn api_level(&self) -> Result<i32> {
1093        #[cfg(not(target_os = "android"))] {
1094            Err(Error::NOT_ANDROID)
1095        }
1096        #[cfg(target_os = "android")] {
1097            self.impls().api_level()
1098        }
1099    }
1100
1101    /// See [`AndroidFs::resolve_file_uri`] for details.
1102    /// 
1103    /// The difference is that this may skip checking whether the target exists and is a file.  
1104    /// As a result, in many cases it avoids the delay (from a few to several tens of milliseconds) caused by calling a Kotlin-side function.
1105    /// 
1106    /// Note that, depending on the situation, 
1107    /// the Kotlin-side function may be called or a check may be performed, 
1108    /// which could result in an error or a delay.
1109    #[maybe_async]
1110    pub fn _resolve_file_uri(
1111        &self, 
1112        dir: &FileUri, 
1113        relative_path: impl AsRef<std::path::Path>
1114    ) -> Result<FileUri> {
1115
1116        #[cfg(not(target_os = "android"))] {
1117            Err(Error::NOT_ANDROID)
1118        }
1119        #[cfg(target_os = "android")] {
1120            self.impls().resolve_file_uri(dir, relative_path, true).await
1121        }
1122    }
1123
1124    /// See [`AndroidFs::resolve_dir_uri`] for details.
1125    /// 
1126    /// The difference is that this may skip checking whether the target exists and is a directory.  
1127    /// As a result, in many cases it avoids the delay (from a few to several tens of milliseconds) caused by calling a Kotlin-side function.
1128    ///
1129    /// Note that, depending on the situation, 
1130    /// the Kotlin-side function may be called or a check may be performed, 
1131    /// which could result in an error or a delay.
1132    #[maybe_async]
1133    pub fn _resolve_dir_uri(
1134        &self, 
1135        dir: &FileUri, 
1136        relative_path: impl AsRef<std::path::Path>
1137    ) -> Result<FileUri> {
1138
1139        #[cfg(not(target_os = "android"))] {
1140            Err(Error::NOT_ANDROID)
1141        }
1142        #[cfg(target_os = "android")] {
1143            self.impls().resolve_dir_uri(dir, relative_path, true).await
1144        }
1145    }
1146}