tauri_plugin_android_fs/api/
android_fs.rs

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