Skip to main content

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