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