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