tauri_plugin_android_fs/api/picker.rs
1use sync_async::sync_async;
2use crate::*;
3use super::*;
4
5
6/// API of File/Directory Picker.
7///
8/// # Examples
9/// ```no_run
10/// use tauri_plugin_android_fs::{AndroidFsExt, Entry, ImageFormat, PublicImageDir, Result, Size};
11///
12/// async fn file_picker_example(app: &tauri::AppHandle<impl tauri::Runtime>) -> Result<()> {
13/// let api = app.android_fs_async();
14///
15/// let selected_files = api
16/// .picker()
17/// .pick_files(
18/// None, // Initial location
19/// &["*/*"], // Target MIME types
20/// false, // If true, only local files can be selected
21/// )
22/// .await?;
23///
24/// if !selected_files.is_empty() {
25/// for uri in selected_files {
26/// let file: std::fs::File = api.open_file_readable(&uri).await?;
27/// let file_path: tauri_plugin_fs::FilePath = uri.clone().into();
28///
29/// let file_type = api.get_mime_type(&uri).await?;
30/// let file_name = api.get_name(&uri).await?;
31/// let file_thumbnail = api.get_thumbnail(
32/// &uri,
33/// Size { width: 200, height: 200 },
34/// ImageFormat::Jpeg,
35/// ).await?;
36/// }
37/// }
38/// else {
39/// // User cancelled the picker.
40/// }
41///
42/// Ok(())
43/// }
44///
45/// async fn file_saver_example(app: &tauri::AppHandle<impl tauri::Runtime>) -> Result<()> {
46/// let api = app.android_fs_async();
47///
48/// // Initial directory when the file saver opens.
49/// // Resolves to `~/Pictures/MyApp/2025-10-22/`.
50/// let initial_location = api
51/// .picker()
52/// .resolve_public_storage_initial_location(
53/// None, // Storage volume (e.g. internal storage or SD card). If `None`, uses the primary volume
54/// PublicImageDir::Pictures, // Base directory
55/// "MyApp/2025-10-22", // Relative path
56/// true, // Creates missing directories
57/// )
58/// .await?;
59///
60/// let selected_file = api
61/// .picker()
62/// .save_file(
63/// Some(&initial_location), // Initial location
64/// "my-image.jpg", // Initial file name
65/// Some("image/jpeg"), // MIME type
66/// false, // If true, only local files can be selected
67/// )
68/// .await?;
69///
70/// if let Some(uri) = selected_file {
71/// // Open the file for writing.
72/// //
73/// // NOTE:
74/// // Existing contents will be truncated.
75/// let file: std::fs::File = api.open_file_writable(&uri).await?;
76/// }
77/// else {
78/// // User cancelled the picker.
79/// }
80///
81/// Ok(())
82/// }
83///
84/// async fn dir_picker_example(app: &tauri::AppHandle<impl tauri::Runtime>) -> Result<()> {
85/// let api = app.android_fs_async();
86///
87/// let selected = api
88/// .picker()
89/// .pick_dir(
90/// None, // Initial location
91/// false, // If true, restricts selection to directories on the local device
92/// )
93/// .await?;
94///
95/// if let Some(dir_uri) = selected {
96/// // Persist access permission across app/device restarts.
97/// api.picker().persist_uri_permission(&dir_uri).await?;
98///
99/// // Read the directory.
100/// for entry in api.read_dir(&dir_uri).await? {
101/// match entry {
102/// Entry::File { uri, name, .. } => {
103/// // Handle a file.
104/// }
105/// Entry::Dir { uri, name, .. } => {
106/// // Handle a directory.
107/// }
108/// }
109/// }
110///
111/// // Create a new file.
112/// // Parent directories are created recursively if necessary.
113/// let file_uri = api
114/// .create_new_file(
115/// &dir_uri,
116/// "MyApp/2025-1021/file.txt",
117/// Some("text/plain"),
118/// )
119/// .await?;
120/// }
121/// else {
122/// // User cancelled the picker.
123/// }
124///
125/// Ok(())
126/// }
127/// ```
128#[sync_async]
129pub struct Picker<'a, R: tauri::Runtime> {
130 #[cfg(target_os = "android")]
131 pub(crate) handle: &'a tauri::plugin::PluginHandle<R>,
132
133 #[cfg(not(target_os = "android"))]
134 #[allow(unused)]
135 pub(crate) handle: &'a std::marker::PhantomData<fn() -> R>,
136}
137
138#[cfg(target_os = "android")]
139#[sync_async(
140 use(if_sync) impls::SyncImpls as Impls;
141 use(if_async) impls::AsyncImpls as Impls;
142)]
143impl<'a, R: tauri::Runtime> Picker<'a, R> {
144
145 #[always_sync]
146 fn impls(&self) -> Impls<'_, R> {
147 Impls { handle: &self.handle }
148 }
149}
150
151#[sync_async(
152 use(if_async) api_async::{AndroidFs, Opener, PrivateStorage, PublicStorage};
153 use(if_sync) api_sync::{AndroidFs, Opener, PrivateStorage, PublicStorage};
154)]
155impl<'a, R: tauri::Runtime> Picker<'a, R> {
156
157 /// Opens a system file picker and returns a **read-write** URIs.
158 /// If no file is selected or the user cancels, an empty vec is returned.
159 ///
160 /// By default, returned URI is valid until the app or device is terminated.
161 /// If you want to persist it across app or device restarts, use [`Picker::persist_uri_permission`].
162 ///
163 /// This provides a standardized file explorer-style interface,
164 /// and also allows file selection from part of third-party apps or cloud storage.
165 ///
166 /// Removing the returned files is also supported in most cases,
167 /// but note that files provided by third-party apps may not be removable.
168 ///
169 /// # Args
170 /// - ***initial_location*** :
171 /// Indicate the initial location of dialog.
172 /// This URI works even without any permissions.
173 /// There is no need to use this if there is no special reason.
174 /// System will do its best to launch the dialog in the specified entry
175 /// if it's a directory, or the directory that contains the specified file if not.
176 /// If this is missing or failed to resolve the desired initial location, the initial location is system specific.
177 /// This must be a URI taken from following or it's derivative :
178 /// - [`Picker::resolve_public_storage_initial_location`]
179 /// - [`Picker::resolve_initial_location`]
180 /// - [`Picker::pick_files`]
181 /// - [`Picker::pick_file`]
182 /// - [`Picker::pick_dir`]
183 /// - [`Picker::save_file`]
184 ///
185 /// - ***mime_types*** :
186 /// The MIME types of the file to be selected.
187 /// However, there is no guarantee that the returned file will match the specified types.
188 /// If left empty, all file types will be available (equivalent to `["*/*"]`).
189 ///
190 /// - ***local_only*** :
191 /// Indicates whether only entry located on the local device should be selectable, without requiring it to be downloaded from a remote service when opened.
192 ///
193 /// # Support
194 /// All Android versions supported by Tauri.
195 ///
196 /// # References
197 /// - <https://developer.android.com/reference/android/content/Intent#ACTION_OPEN_DOCUMENT>
198 #[maybe_async]
199 pub fn pick_files(
200 &self,
201 initial_location: Option<&FsUri>,
202 mime_types: &[&str],
203 local_only: bool,
204 ) -> Result<Vec<FsUri>> {
205
206 #[cfg(not(target_os = "android"))] {
207 Err(Error::NOT_ANDROID)
208 }
209 #[cfg(target_os = "android")] {
210 self.impls().show_pick_file_dialog(initial_location, mime_types, true, local_only).await
211 }
212 }
213
214 /// Opens a system file picker and returns a **read-write** URI.
215 /// If no file is selected or the user cancels, None is returned.
216 ///
217 /// By default, returned URI is valid until the app or device is terminated.
218 /// If you want to persist it across app or device restarts, use [`Picker::persist_uri_permission`].
219 ///
220 /// This provides a standardized file explorer-style interface,
221 /// and also allows file selection from part of third-party apps or cloud storage.
222 ///
223 /// Removing the returned files is also supported in most cases,
224 /// but note that files provided by third-party apps may not be removable.
225 ///
226 /// # Args
227 /// - ***initial_location*** :
228 /// Indicate the initial location of dialog.
229 /// This URI works even without any permissions.
230 /// There is no need to use this if there is no special reason.
231 /// System will do its best to launch the dialog in the specified entry
232 /// if it's a directory, or the directory that contains the specified file if not.
233 /// If this is missing or failed to resolve the desired initial location, the initial location is system specific.
234 /// This must be a URI taken from following or it's derivative :
235 /// - [`Picker::resolve_public_storage_initial_location`]
236 /// - [`Picker::resolve_initial_location`]
237 /// - [`Picker::pick_files`]
238 /// - [`Picker::pick_file`]
239 /// - [`Picker::pick_dir`]
240 /// - [`Picker::save_file`]
241 ///
242 /// - ***mime_types*** :
243 /// The MIME types of the file to be selected.
244 /// However, there is no guarantee that the returned file will match the specified types.
245 /// If left empty, all file types will be available (equivalent to `["*/*"]`).
246 ///
247 /// - ***local_only*** :
248 /// Indicates whether only entry located on the local device should be selectable, without requiring it to be downloaded from a remote service when opened.
249 ///
250 /// # Support
251 /// All Android versions supported by Tauri.
252 ///
253 /// # References
254 /// - <https://developer.android.com/reference/android/content/Intent#ACTION_OPEN_DOCUMENT>
255 #[maybe_async]
256 pub fn pick_file(
257 &self,
258 initial_location: Option<&FsUri>,
259 mime_types: &[&str],
260 local_only: bool
261 ) -> Result<Option<FsUri>> {
262
263 #[cfg(not(target_os = "android"))] {
264 Err(Error::NOT_ANDROID)
265 }
266 #[cfg(target_os = "android")] {
267 self.impls().show_pick_file_dialog(initial_location, mime_types, false, local_only)
268 .await
269 .map(|mut i| i.pop())
270 }
271 }
272
273 /// Opens a media picker and returns a **readonly** URIs.
274 /// If no file is selected or the user cancels, an empty vec is returned.
275 ///
276 /// By default, returned URI is valid until the app or device is terminated.
277 /// If you want to persist it across app or device restarts, use [`Picker::persist_uri_permission`].
278 ///
279 /// This media picker provides a gallery,
280 /// sorted by date from newest to oldest.
281 ///
282 /// # Args
283 /// - ***target*** :
284 /// The media type of the file to be selected.
285 /// Images or videos, or both.
286 ///
287 /// - ***local_only*** :
288 /// Indicates whether only entry located on the local device should be selectable, without requiring it to be downloaded from a remote service when opened.
289 ///
290 /// # Note
291 /// The file obtained from this function cannot retrieve the correct file name using [`AndroidFs::get_name`].
292 /// Instead, it will be assigned a sequential number, such as `1000091523.png`.
293 /// And this is marked intended behavior, not a bug.
294 /// - <https://issuetracker.google.com/issues/268079113>
295 ///
296 /// # Support
297 /// This feature is available on devices that meet the following criteria:
298 /// - Running Android 11 (API level 30) or higher
299 /// - Receive changes to Modular System Components through Google System Updates
300 ///
301 /// Availability on a given device can be verified by calling [`Picker::is_visual_media_picker_available`].
302 /// If not supported, this function behaves the same as [`Picker::pick_files`].
303 ///
304 /// # References
305 /// - <https://developer.android.com/training/data-storage/shared/photopicker>
306 #[maybe_async]
307 pub fn pick_visual_medias(
308 &self,
309 target: VisualMediaTarget<'_>,
310 local_only: bool
311 ) -> Result<Vec<FsUri>> {
312
313 #[cfg(not(target_os = "android"))] {
314 Err(Error::NOT_ANDROID)
315 }
316 #[cfg(target_os = "android")] {
317 self.impls().show_pick_visual_media_dialog(target, true, local_only).await
318 }
319 }
320
321 /// Opens a media picker and returns a **readonly** URI.
322 /// If no file is selected or the user cancels, None is returned.
323 ///
324 /// By default, returned URI is valid until the app or device is terminated.
325 /// If you want to persist it across app or device restarts, use [`Picker::persist_uri_permission`].
326 ///
327 /// This media picker provides a gallery,
328 /// sorted by date from newest to oldest.
329 ///
330 /// # Args
331 /// - ***target*** :
332 /// The media type of the file to be selected.
333 /// Images or videos, or both.
334 ///
335 /// - ***local_only*** :
336 /// Indicates whether only entry located on the local device should be selectable, without requiring it to be downloaded from a remote service when opened.
337 ///
338 /// # Note
339 /// The file obtained from this function cannot retrieve the correct file name using [`AndroidFs::get_name`].
340 /// Instead, it will be assigned a sequential number, such as `1000091523.png`.
341 /// And this is marked intended behavior, not a bug.
342 /// - <https://issuetracker.google.com/issues/268079113>
343 ///
344 /// # Support
345 /// This feature is available on devices that meet the following criteria:
346 /// - Running Android 11 (API level 30) or higher
347 /// - Receive changes to Modular System Components through Google System Updates
348 ///
349 /// Availability on a given device can be verified by calling [`Picker::is_visual_media_picker_available`].
350 /// If not supported, this function behaves the same as [`Picker::pick_file`].
351 ///
352 /// # References
353 /// - <https://developer.android.com/training/data-storage/shared/photopicker>
354 #[maybe_async]
355 pub fn pick_visual_media(
356 &self,
357 target: VisualMediaTarget<'_>,
358 local_only: bool
359 ) -> Result<Option<FsUri>> {
360
361 #[cfg(not(target_os = "android"))] {
362 Err(Error::NOT_ANDROID)
363 }
364 #[cfg(target_os = "android")] {
365 self.impls().show_pick_visual_media_dialog(target, false, local_only)
366 .await
367 .map(|mut i| i.pop())
368 }
369 }
370
371 /// Opens a file picker and returns a **readonly** URIs.
372 /// If no file is selected or the user cancels, an empty vec is returned.
373 ///
374 /// Returned URI is valid until the app or device is terminated. Can not persist it.
375 ///
376 /// This works differently depending on the model and version.
377 /// Recent devices often have the similar behaviour as [`Picker::pick_visual_medias`] or [`Picker::pick_files`].
378 /// In older versions, third-party apps often handle request instead.
379 ///
380 /// # Args
381 /// - ***mime_types*** :
382 /// The MIME types of the file to be selected.
383 /// However, there is no guarantee that the returned file will match the specified types.
384 /// If left empty, all file types will be available (equivalent to `["*/*"]`).
385 ///
386 /// # Support
387 /// All Android versions supported by Tauri.
388 ///
389 /// # References
390 /// - <https://developer.android.com/reference/android/content/Intent#ACTION_GET_CONTENT>
391 #[maybe_async]
392 #[deprecated = "This may not support operations other than opening files."]
393 pub fn pick_contents(
394 &self,
395 mime_types: &[&str],
396 ) -> Result<Vec<FsUri>> {
397
398 #[cfg(not(target_os = "android"))] {
399 Err(Error::NOT_ANDROID)
400 }
401 #[cfg(target_os = "android")] {
402 self.impls().show_pick_content_dialog(mime_types, true).await
403 }
404 }
405
406 /// Opens a file picker and returns a **readonly** URI.
407 /// If no file is selected or the user cancels, None is returned.
408 ///
409 /// Returned URI is valid until the app or device is terminated. Can not persist it.
410 ///
411 /// This works differently depending on the model and version.
412 /// Recent devices often have the similar behaviour as [`Picker::pick_visual_media`] or [`Picker::pick_file`].
413 /// In older versions, third-party apps often handle request instead.
414 ///
415 /// # Args
416 /// - ***mime_types*** :
417 /// The MIME types of the file to be selected.
418 /// However, there is no guarantee that the returned file will match the specified types.
419 /// If left empty, all file types will be available (equivalent to `["*/*"]`).
420 ///
421 /// # Support
422 /// All Android versions supported by Tauri.
423 ///
424 /// # References
425 /// - <https://developer.android.com/reference/android/content/Intent#ACTION_GET_CONTENT>
426 #[maybe_async]
427 #[deprecated = "This may not support operations other than opening files."]
428 pub fn pick_content(
429 &self,
430 mime_types: &[&str],
431 ) -> Result<Option<FsUri>> {
432
433 #[cfg(not(target_os = "android"))] {
434 Err(Error::NOT_ANDROID)
435 }
436 #[cfg(target_os = "android")] {
437 self.impls().show_pick_content_dialog(mime_types, false)
438 .await
439 .map(|mut i| i.pop())
440 }
441 }
442
443 /// Opens a system directory picker, allowing the creation of a new directory or the selection of an existing one,
444 /// and returns a **read-write** directory URI.
445 /// App can fully manage entries within the returned directory.
446 /// If no directory is selected or the user cancels, `None` is returned.
447 ///
448 /// By default, returned URI is valid until the app or device is terminated.
449 /// If you want to persist it across app or device restarts, use [`Picker::persist_uri_permission`].
450 ///
451 /// This provides a standardized file explorer-style interface,
452 /// and also allows directory selection from part of third-party apps or cloud storage.
453 ///
454 /// # Args
455 /// - ***initial_location*** :
456 /// Indicate the initial location of dialog.
457 /// This URI works even without any permissions.
458 /// There is no need to use this if there is no special reason.
459 /// System will do its best to launch the dialog in the specified entry
460 /// if it's a directory, or the directory that contains the specified file if not.
461 /// If this is missing or failed to resolve the desired initial location, the initial location is system specific.
462 /// This must be a URI taken from following or it's derivative :
463 /// - [`Picker::resolve_public_storage_initial_location`]
464 /// - [`Picker::resolve_initial_location`]
465 /// - [`Picker::pick_files`]
466 /// - [`Picker::pick_file`]
467 /// - [`Picker::pick_dir`]
468 /// - [`Picker::save_file`]
469 ///
470 /// - ***local_only*** :
471 /// Indicates whether only entry located on the local device should be selectable, without requiring it to be downloaded from a remote service when opened.
472 ///
473 /// # Support
474 /// All Android versions supported by Tauri.
475 ///
476 /// # References
477 /// - <https://developer.android.com/reference/android/content/Intent#ACTION_OPEN_DOCUMENT_TREE>
478 #[maybe_async]
479 pub fn pick_dir(
480 &self,
481 initial_location: Option<&FsUri>,
482 local_only: bool
483 ) -> Result<Option<FsUri>> {
484
485 #[cfg(not(target_os = "android"))] {
486 Err(Error::NOT_ANDROID)
487 }
488 #[cfg(target_os = "android")] {
489 self.impls().show_pick_dir_dialog(initial_location, local_only).await
490 }
491 }
492
493 /// Opens a system file saver and returns a **writeonly** URI.
494 /// The returned file may be a newly created file with no content,
495 /// or it may be an existing file with the requested MIME type.
496 /// If the user cancels, `None` is returned.
497 ///
498 /// By default, returned URI is valid until the app or device is terminated.
499 /// If you want to persist it across app or device restarts, use [`Picker::persist_uri_permission`].
500 ///
501 /// This provides a standardized file explorer-style interface,
502 /// and also allows file selection from part of third-party apps or cloud storage.
503 ///
504 /// Removing and reading the returned files is also supported in most cases,
505 /// but note that files provided by third-party apps may not.
506 ///
507 /// # Args
508 /// - ***initial_location*** :
509 /// Indicate the initial location of dialog.
510 /// This URI works even without any permissions.
511 /// There is no need to use this if there is no special reason.
512 /// System will do its best to launch the dialog in the specified entry
513 /// if it's a directory, or the directory that contains the specified file if not.
514 /// If this is missing or failed to resolve the desired initial location, the initial location is system specific.
515 /// This must be a URI taken from following or it's derivative :
516 /// - [`Picker::resolve_public_storage_initial_location`]
517 /// - [`Picker::resolve_initial_location`]
518 /// - [`Picker::pick_files`]
519 /// - [`Picker::pick_file`]
520 /// - [`Picker::pick_dir`]
521 /// - [`Picker::save_file`]
522 ///
523 /// - ***initial_file_name*** :
524 /// An initial file name.
525 /// The user may change this value before creating the file.
526 /// If no extension is present,
527 /// the system may infer one from ***mime_type*** and may append it to the file name.
528 /// But this append-extension operation depends on the model and version.
529 ///
530 /// - ***mime_type*** :
531 /// The MIME type of the file to be saved.
532 /// If this is None, MIME type is inferred from the extension of ***initial_file_name*** (not file name by user input)
533 /// and if that fails, `application/octet-stream` is used.
534 ///
535 /// - ***local_only*** :
536 /// Indicates whether only entry located on the local device should be selectable.
537 ///
538 /// # Support
539 /// All Android versions supported by Tauri.
540 ///
541 /// # References
542 /// - <https://developer.android.com/reference/android/content/Intent#ACTION_CREATE_DOCUMENT>
543 #[maybe_async]
544 pub fn save_file(
545 &self,
546 initial_location: Option<&FsUri>,
547 initial_file_name: impl AsRef<str>,
548 mime_type: Option<&str>,
549 local_only: bool
550 ) -> Result<Option<FsUri>> {
551
552 #[cfg(not(target_os = "android"))] {
553 Err(Error::NOT_ANDROID)
554 }
555 #[cfg(target_os = "android")] {
556 self.impls().show_save_file_dialog(initial_location, initial_file_name, mime_type, local_only).await
557 }
558 }
559
560 /// Builds the specified directory URI.
561 ///
562 /// This should only be used as `initial_location` in the file picker, such as [`Picker::pick_files`].
563 /// It must not be used for any other purpose.
564 ///
565 /// This is useful when selecting save location,
566 /// but when selecting existing entries, `initial_location` is often better with None.
567 ///
568 /// # Args
569 /// - ***volume_id*** :
570 /// ID of the storage volume, such as internal storage, SD card, etc.
571 /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.
572 ///
573 /// - ***base_dir*** :
574 /// The base directory.
575 ///
576 /// - ***relative_path*** :
577 /// The directory path relative to the base directory.
578 ///
579 /// - ***create_dir_all*** :
580 /// Creates directories if missing.
581 /// See [`PublicStorage::create_dir_all`].
582 /// If error occurs, it will be ignored.
583 ///
584 /// # Support
585 /// All Android versions supported by Tauri.
586 ///
587 /// Note :
588 /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
589 /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].
590 /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
591 /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].
592 /// - Others dirs are available in all Android versions.
593 #[maybe_async]
594 pub fn resolve_public_storage_initial_location(
595 &self,
596 volume_id: Option<&StorageVolumeId>,
597 base_dir: impl Into<PublicDir>,
598 relative_path: impl AsRef<std::path::Path>,
599 create_dir_all: bool
600 ) -> Result<FsUri> {
601
602 #[cfg(not(target_os = "android"))] {
603 Err(Error::NOT_ANDROID)
604 }
605 #[cfg(target_os = "android")] {
606 self.impls().resolve_initial_location_in_public_storage(volume_id, base_dir, relative_path, create_dir_all).await
607 }
608 }
609
610 /// Builds the storage volume root URI.
611 ///
612 /// This should only be used as `initial_location` in the file picker, such as [`Picker::pick_files`].
613 /// It must not be used for any other purpose.
614 ///
615 /// This is useful when selecting save location,
616 /// but when selecting existing entries, `initial_location` is often better with None.
617 ///
618 /// # Args
619 /// - ***volume_id*** :
620 /// ID of the storage volume, such as internal storage, SD card, etc.
621 /// If `None` is provided, [`the primary storage volume`](AndroidFs::get_primary_volume) will be used.
622 ///
623 /// # Support
624 /// All Android versions supported by Tauri.
625 #[maybe_async]
626 pub fn resolve_initial_location(&self, volume_id: Option<&StorageVolumeId>) -> Result<FsUri> {
627 #[cfg(not(target_os = "android"))] {
628 Err(Error::NOT_ANDROID)
629 }
630 #[cfg(target_os = "android")] {
631 self.impls().resolve_root_initial_location(volume_id).await
632 }
633 }
634
635 /// Verify whether [`Picker::pick_visual_medias`] is available on a given device.
636 ///
637 /// # Support
638 /// All Android versions supported by Tauri.
639 #[maybe_async]
640 pub fn is_visual_media_picker_available(&self) -> Result<bool> {
641 #[cfg(not(target_os = "android"))] {
642 Err(Error::NOT_ANDROID)
643 }
644 #[cfg(target_os = "android")] {
645 self.impls().is_visual_media_picker_available().await
646 }
647 }
648
649 /// Check a URI permission granted by the file picker.
650 /// Returns false if there are no permissions.
651 ///
652 /// # Args
653 /// - **uri** :
654 /// URI of the target file or directory.
655 ///
656 /// - **permission** :
657 /// The permission you want to check.
658 ///
659 ///
660 /// # Support
661 /// All Android versions supported by Tauri.
662 #[maybe_async]
663 pub fn check_uri_permission(
664 &self,
665 uri: &FsUri,
666 permission: UriPermission
667 ) -> Result<bool> {
668
669 #[cfg(not(target_os = "android"))] {
670 Err(Error::NOT_ANDROID)
671 }
672 #[cfg(target_os = "android")] {
673 self.impls().check_picker_uri_permission(uri, permission).await
674 }
675 }
676
677 /// Take persistent permission to access the file, directory and its descendants.
678 /// This is a prolongation of an already acquired permission, not the acquisition of a new one.
679 ///
680 /// This works by just calling, without displaying any confirmation to the user.
681 ///
682 /// 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)
683 /// Therefore, it is recommended to relinquish the unnecessary persisted URI by [`Picker::release_persisted_uri_permission`] or [`Picker::release_all_persisted_uri_permissions`].
684 /// Persisted permissions may be relinquished by other apps, user, or by moving/removing entries.
685 /// So check by [`Picker::check_persisted_uri_permission`].
686 /// And you can retrieve the list of persisted uris using [`Picker::get_all_persisted_uri_permissions`].
687 ///
688 /// # Args
689 /// - **uri** :
690 /// URI of the target file or directory.
691 /// This must be a URI taken from following :
692 /// - [`Picker::pick_files`]
693 /// - [`Picker::pick_file`]
694 /// - [`Picker::pick_visual_medias`]
695 /// - [`Picker::pick_visual_media`]
696 /// - [`Picker::pick_dir`]
697 /// - [`Picker::save_file`]
698 /// - [`AndroidFs::resolve_file_uri`], [`AndroidFs::resolve_dir_uri`], [`AndroidFs::read_dir`], [`AndroidFs::create_new_file`], [`AndroidFs::create_dir_all`] :
699 /// If use URI from thoese fucntions, the permissions of the origin directory URI is persisted, not an entry iteself by this function.
700 /// Because the permissions and validity period of the descendant entry URIs depend on the origin directory.
701 ///
702 /// # Support
703 /// All Android versions supported by Tauri.
704 #[maybe_async]
705 pub fn persist_uri_permission(&self, uri: &FsUri) -> Result<()> {
706 #[cfg(not(target_os = "android"))] {
707 Err(Error::NOT_ANDROID)
708 }
709 #[cfg(target_os = "android")] {
710 self.impls().persist_picker_uri_permission(uri).await
711 }
712 }
713
714 /// Check a persisted URI permission grant by [`Picker::persist_uri_permission`].
715 /// Returns false if there are only non-persistent permissions or no permissions.
716 ///
717 /// # Args
718 /// - **uri** :
719 /// URI of the target file or directory.
720 /// This must be a URI taken from following :
721 /// - [`Picker::pick_files`]
722 /// - [`Picker::pick_file`]
723 /// - [`Picker::pick_visual_medias`]
724 /// - [`Picker::pick_visual_media`]
725 /// - [`Picker::pick_dir`]
726 /// - [`Picker::save_file`]
727 /// - [`AndroidFs::resolve_file_uri`], [`AndroidFs::resolve_dir_uri`], [`AndroidFs::read_dir`], [`AndroidFs::create_new_file`], [`AndroidFs::create_dir_all`] :
728 /// If use URI from those functions, the permissions of the origin directory URI is checked, not an entry iteself by this function.
729 /// Because the permissions and validity period of the descendant entry URIs depend on the origin directory.
730 ///
731 /// - **permission** :
732 /// The permission you want to check.
733 ///
734 /// # Support
735 /// All Android versions supported by Tauri.
736 #[maybe_async]
737 pub fn check_persisted_uri_permission(
738 &self,
739 uri: &FsUri,
740 permission: UriPermission
741 ) -> Result<bool> {
742
743 #[cfg(not(target_os = "android"))] {
744 Err(Error::NOT_ANDROID)
745 }
746 #[cfg(target_os = "android")] {
747 self.impls().check_persisted_picker_uri_permission(uri, permission).await
748 }
749 }
750
751 /// Return list of all persisted URIs that have been persisted by [`Picker::persist_uri_permission`] and currently valid.
752 ///
753 /// # Support
754 /// All Android versions supported by Tauri.
755 #[maybe_async]
756 pub fn get_all_persisted_uri_permissions(&self) -> Result<Vec<PersistedUriPermissionState>> {
757 #[cfg(not(target_os = "android"))] {
758 Err(Error::NOT_ANDROID)
759 }
760 #[cfg(target_os = "android")] {
761 self.impls()
762 .get_all_persisted_picker_uri_permissions().await
763 .map(|v| v.collect())
764 }
765 }
766
767 /// Relinquish a persisted URI permission grant by [`Picker::persist_uri_permission`].
768 /// Non-persistent permissions are not released.
769 ///
770 /// Returns true if a persisted permission exists for the specified URI and was successfully released;
771 /// otherwise, returns false if no persisted permission existed in the first place.
772 ///
773 /// # Args
774 /// - ***uri*** :
775 /// URI of the target file or directory.
776 ///
777 /// # Support
778 /// All Android versions supported by Tauri.
779 #[maybe_async]
780 pub fn release_persisted_uri_permission(&self, uri: &FsUri) -> Result<bool> {
781 #[cfg(not(target_os = "android"))] {
782 Err(Error::NOT_ANDROID)
783 }
784 #[cfg(target_os = "android")] {
785 self.impls().release_persisted_picker_uri_permission(uri).await
786 }
787 }
788
789 /// Relinquish a all persisted uri permission grants by [`Picker::persist_uri_permission`].
790 /// Non-persistent permissions are not released.
791 ///
792 /// # Support
793 /// All Android versions supported by Tauri.
794 #[maybe_async]
795 pub fn release_all_persisted_uri_permissions(&self) -> Result<()> {
796 #[cfg(not(target_os = "android"))] {
797 Err(Error::NOT_ANDROID)
798 }
799 #[cfg(target_os = "android")] {
800 self.impls().release_all_persisted_picker_uri_permissions().await
801 }
802 }
803}