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