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