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