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