tauri_plugin_android_fs/api/
file_picker.rs

1use crate::*;
2
3
4/// API of file/dir picker.
5/// 
6/// # Examples
7/// ```
8/// fn example(app: &tauri::AppHandle) {
9///     use tauri_plugin_android_fs::AndroidFsExt as _;
10/// 
11///     let api = app.android_fs();
12///     let file_picker = api.file_picker();
13/// }
14/// ```
15pub struct FilePicker<'a, R: tauri::Runtime>(pub(crate) &'a AndroidFs<R>);
16
17impl<'a, R: tauri::Runtime> FilePicker<'a, R> {
18
19    /// Opens a system file picker and returns a **read-write** URIs.  
20    /// If no file is selected or the user cancels, an empty vec is returned.  
21    /// 
22    /// By default, returned URI is valid until the app is terminated. 
23    /// If you want to persist it across app restarts, use [`AndroidFs::take_persistable_uri_permission`].
24    /// 
25    /// This provides a standardized file explorer-style interface, 
26    /// and also allows file selection from part of third-party apps or cloud storage.
27    ///
28    /// Removing the returned files is also supported in most cases, 
29    /// but note that files provided by third-party apps may not be removable.  
30    ///  
31    /// # Args  
32    /// - ***initial_location*** :  
33    /// Indicate the initial location of dialog.  
34    /// This URI works even without any permissions.  
35    /// There is no need to use this if there is no special reason.  
36    /// System will do its best to launch the dialog in the specified entry 
37    /// if it's a directory, or the directory that contains the specified file if not.  
38    /// If this is missing or failed to resolve the desired initial location, the initial location is system specific.  
39    /// This must be a URI taken from following :   
40    ///     - [`AndroidFs::resolve_initial_location`]
41    ///     - [`AndroidFs::try_resolve_file_uri`]
42    ///     - [`AndroidFs::try_resolve_dir_uri`]
43    ///     - [`AndroidFs::resolve_uri`]
44    ///     - [`AndroidFs::read_dir`]
45    ///     - [`AndroidFs::create_file`]
46    ///     - [`AndroidFs::create_dir_all`]
47    ///     - [`AndroidFs::rename`]
48    ///     - [`FilePicker::pick_files`]
49    ///     - [`FilePicker::pick_file`]
50    ///     - [`FilePicker::pick_dir`]
51    ///     - [`FilePicker::save_file`]
52    /// 
53    /// - ***mime_types*** :  
54    /// The MIME types of the file to be selected.  
55    /// However, there is no guarantee that the returned file will match the specified types.  
56    /// If left empty, all file types will be available (equivalent to `["*/*"]`).  
57    ///  
58    /// - ***multiple*** :  
59    /// Indicates whether multiple file selection is allowed.  
60    /// 
61    /// # Support
62    /// All.
63    /// 
64    /// # References
65    /// <https://developer.android.com/reference/android/content/Intent#ACTION_OPEN_DOCUMENT>
66    pub fn pick_files(
67        &self,
68        initial_location: Option<&FileUri>,
69        mime_types: &[&str],
70        multiple: bool,
71    ) -> crate::Result<Vec<FileUri>> {
72
73        on_android!({
74            impl_se!(struct Req<'a> { 
75                mime_types: &'a [&'a str],
76                multiple: bool,
77                initial_location: Option<&'a FileUri>
78            });
79            impl_de!(struct Res { uris: Vec<FileUri> });
80    
81            let _guard = self.0.intent_lock.lock();
82            self.0.api
83                .run_mobile_plugin::<Res>("showOpenFileDialog", Req { mime_types, multiple, initial_location })
84                .map(|v| v.uris)
85                .map_err(Into::into)
86        })
87    }
88
89    /// Opens a system file picker and returns a **read-write** URI.  
90    /// If no file is selected or the user cancels, None is returned.  
91    /// 
92    /// By default, returned URI is valid until the app is terminated. 
93    /// If you want to persist it across app restarts, use [`AndroidFs::take_persistable_uri_permission`].
94    /// 
95    /// This provides a standardized file explorer-style interface, 
96    /// and also allows file selection from part of third-party apps or cloud storage.
97    ///
98    /// Removing the returned files is also supported in most cases, 
99    /// but note that files provided by third-party apps may not be removable.  
100    ///  
101    /// # Args  
102    /// - ***initial_location*** :  
103    /// Indicate the initial location of dialog.  
104    /// This URI works even without any permissions.  
105    /// There is no need to use this if there is no special reason.  
106    /// System will do its best to launch the dialog in the specified entry 
107    /// if it's a directory, or the directory that contains the specified file if not.  
108    /// If this is missing or failed to resolve the desired initial location, the initial location is system specific.  
109    /// This must be a URI taken from following :   
110    ///     - [`AndroidFs::resolve_initial_location`]
111    ///     - [`AndroidFs::try_resolve_file_uri`]
112    ///     - [`AndroidFs::try_resolve_dir_uri`]
113    ///     - [`AndroidFs::resolve_uri`]
114    ///     - [`AndroidFs::read_dir`]
115    ///     - [`AndroidFs::create_file`]
116    ///     - [`AndroidFs::create_dir_all`]
117    ///     - [`AndroidFs::rename`]
118    ///     - [`FilePicker::pick_files`]
119    ///     - [`FilePicker::pick_file`]
120    ///     - [`FilePicker::pick_dir`]
121    ///     - [`FilePicker::save_file`]
122    /// 
123    /// - ***mime_types*** :  
124    /// The MIME types of the file to be selected.  
125    /// However, there is no guarantee that the returned file will match the specified types.  
126    /// If left empty, all file types will be available (equivalent to `["*/*"]`).  
127    ///  
128    /// # Support
129    /// All.
130    /// 
131    /// # References
132    /// <https://developer.android.com/reference/android/content/Intent#ACTION_OPEN_DOCUMENT>
133    pub fn pick_file(
134        &self,
135        initial_location: Option<&FileUri>,
136        mime_types: &[&str],
137    ) -> crate::Result<Option<FileUri>> {
138
139        self.pick_files(initial_location, mime_types, false).map(|mut f| f.pop())
140    }
141
142    /// Opens a media picker and returns a **readonly** URIs.  
143    /// If no file is selected or the user cancels, an empty vec is returned.  
144    ///  
145    /// By default, returned URI is valid until the app is terminated. 
146    /// If you want to persist it across app restarts, use [`AndroidFs::take_persistable_uri_permission`].
147    ///  
148    /// This media picker provides a gallery, 
149    /// sorted by date from newest to oldest. 
150    /// 
151    /// # Args  
152    /// - ***target*** :  
153    /// The media type of the file to be selected.  
154    /// Images or videos, or both.  
155    ///  
156    /// - ***multiple*** :  
157    /// Indicates whether multiple file selection is allowed.  
158    ///  
159    /// # Note
160    /// The file obtained from this function cannot retrieve the correct file name using [`AndroidFs::get_name`].  
161    /// Instead, it will be assigned a sequential number, such as `1000091523.png`. 
162    /// And this is marked intended behavior, not a bug.
163    /// - <https://issuetracker.google.com/issues/268079113>  
164    ///  
165    /// # Support
166    /// This feature is available on devices that meet the following criteria:  
167    /// - Running Android 11 (API level 30) or higher  
168    /// - Receive changes to Modular System Components through Google System Updates  
169    ///  
170    /// Availability on a given device can be verified by calling [`FilePicker::is_visual_media_picker_available`].  
171    /// If not supported, this function behaves the same as [`FilePicker::pick_files`].  
172    /// 
173    /// # References
174    /// <https://developer.android.com/training/data-storage/shared/photopicker>
175    pub fn pick_visual_medias(
176        &self,
177        target: VisualMediaTarget,
178        multiple: bool,
179    ) -> crate::Result<Vec<FileUri>> {
180
181        on_android!({
182            impl_se!(struct Req { multiple: bool, target: VisualMediaTarget });
183            impl_de!(struct Res { uris: Vec<FileUri> });
184    
185            let _guard = self.0.intent_lock.lock();
186            self.0.api
187                .run_mobile_plugin::<Res>("showOpenVisualMediaDialog", Req { multiple, target })
188                .map(|v| v.uris)
189                .map_err(Into::into)
190        })
191    }
192
193    /// Opens a media picker and returns a **readonly** URI.  
194    /// If no file is selected or the user cancels, None is returned.  
195    ///  
196    /// By default, returned URI is valid until the app is terminated. 
197    /// If you want to persist it across app restarts, use [`AndroidFs::take_persistable_uri_permission`].
198    ///  
199    /// This media picker provides a gallery, 
200    /// sorted by date from newest to oldest. 
201    /// 
202    /// # Args  
203    /// - ***target*** :  
204    /// The media type of the file to be selected.  
205    /// Images or videos, or both.  
206    ///  
207    /// # Note
208    /// The file obtained from this function cannot retrieve the correct file name using [`AndroidFs::get_name`].  
209    /// Instead, it will be assigned a sequential number, such as `1000091523.png`. 
210    /// And this is marked intended behavior, not a bug.
211    /// - <https://issuetracker.google.com/issues/268079113>  
212    ///  
213    /// # Support
214    /// This feature is available on devices that meet the following criteria:  
215    /// - Running Android 11 (API level 30) or higher  
216    /// - Receive changes to Modular System Components through Google System Updates  
217    ///  
218    /// Availability on a given device can be verified by calling [`FilePicker::is_visual_media_picker_available`].  
219    /// If not supported, this function behaves the same as [`FilePicker::pick_file`].  
220    /// 
221    /// # References
222    /// <https://developer.android.com/training/data-storage/shared/photopicker>
223    pub fn pick_visual_media(
224        &self,
225        target: VisualMediaTarget,
226    ) -> crate::Result<Option<FileUri>> {
227
228        self.pick_visual_medias(target, false).map(|mut f| f.pop())
229    }
230
231    /// Opens a file picker and returns a **readonly** URIs.  
232    /// If no file is selected or the user cancels, an empty vec is returned.  
233    ///  
234    /// Returned URI is valid until the app is terminated. Can not persist it.
235    /// 
236    /// This works differently depending on the model and version.  
237    /// Recent devices often have the similar behaviour as [`FilePicker::pick_visual_medias`] or [`FilePicker::pick_files`].  
238    /// In older versions, third-party apps often handle request instead.
239    /// 
240    /// # Args  
241    /// - ***mime_types*** :  
242    /// The MIME types of the file to be selected.  
243    /// However, there is no guarantee that the returned file will match the specified types.  
244    /// If left empty, all file types will be available (equivalent to `["*/*"]`).  
245    ///  
246    /// - ***multiple*** :  
247    /// Indicates whether multiple file selection is allowed.  
248    /// 
249    /// # Support
250    /// All.
251    /// 
252    /// # References
253    /// <https://developer.android.com/reference/android/content/Intent#ACTION_GET_CONTENT>
254    pub fn pick_contents(
255        &self,
256        mime_types: &[&str],
257        multiple: bool
258    ) -> crate::Result<Vec<FileUri>> {
259
260        on_android!({
261            impl_se!(struct Req<'a> { mime_types: &'a [&'a str], multiple: bool });
262            impl_de!(struct Res { uris: Vec<FileUri> });
263
264            let _guard = self.0.intent_lock.lock();
265            self.0.api
266                .run_mobile_plugin::<Res>("showOpenContentDialog", Req { mime_types, multiple })
267                .map(|v| v.uris)
268                .map_err(Into::into)
269        })
270    }
271
272    /// Opens a file picker and returns a **readonly** URI.  
273    /// If no file is selected or the user cancels, None is returned.  
274    ///  
275    /// Returned URI is valid until the app is terminated. Can not persist it.
276    /// 
277    /// This works differently depending on the model and version.  
278    /// Recent devices often have the similar behaviour as [`FilePicker::pick_visual_media`] or [`FilePicker::pick_file`].  
279    /// In older versions, third-party apps often handle request instead.
280    /// 
281    /// # Args  
282    /// - ***mime_types*** :  
283    /// The MIME types of the file to be selected.  
284    /// However, there is no guarantee that the returned file will match the specified types.  
285    /// If left empty, all file types will be available (equivalent to `["*/*"]`).  
286    ///  
287    /// # Support
288    /// All.
289    /// 
290    /// # References
291    /// <https://developer.android.com/reference/android/content/Intent#ACTION_GET_CONTENT>
292    pub fn pick_content(
293        &self,
294        mime_types: &[&str],
295    ) -> crate::Result<Option<FileUri>> {
296
297        self.pick_contents(mime_types, false).map(|mut f| f.pop())
298    }
299
300    /// Opens a system directory picker, allowing the creation of a new directory or the selection of an existing one, 
301    /// and returns a **read-write** directory URI. 
302    /// App can fully manage entries within the returned directory.  
303    /// If no directory is selected or the user cancels, `None` is returned. 
304    /// 
305    /// By default, returned URI is valid until the app is terminated. 
306    /// If you want to persist it across app restarts, use [`AndroidFs::take_persistable_uri_permission`].
307    /// 
308    /// This provides a standardized file explorer-style interface,
309    /// and also allows file selection from part of third-party apps or cloud storage.
310    /// 
311    /// # Args  
312    /// - ***initial_location*** :  
313    /// Indicate the initial location of dialog.    
314    /// This URI works even without any permissions.  
315    /// There is no need to use this if there is no special reason.  
316    /// System will do its best to launch the dialog in the specified entry 
317    /// if it's a directory, or the directory that contains the specified file if not.  
318    /// If this is missing or failed to resolve the desired initial location, the initial location is system specific.   
319    /// This must be a URI taken from following :   
320    ///     - [`AndroidFs::resolve_initial_location`]
321    ///     - [`AndroidFs::try_resolve_file_uri`]
322    ///     - [`AndroidFs::try_resolve_dir_uri`]
323    ///     - [`AndroidFs::resolve_uri`]
324    ///     - [`AndroidFs::read_dir`]
325    ///     - [`AndroidFs::create_file`]
326    ///     - [`AndroidFs::create_dir_all`]
327    ///     - [`AndroidFs::rename`]
328    ///     - [`FilePicker::pick_files`]
329    ///     - [`FilePicker::pick_file`]
330    ///     - [`FilePicker::pick_dir`]
331    ///     - [`FilePicker::save_file`]
332    /// 
333    /// # Support
334    /// All.
335    /// 
336    /// # References
337    /// <https://developer.android.com/reference/android/content/Intent#ACTION_OPEN_DOCUMENT_TREE>
338    pub fn pick_dir(
339        &self,
340        initial_location: Option<&FileUri>,
341    ) -> crate::Result<Option<FileUri>> {
342
343        on_android!({
344            impl_se!(struct Req<'a> { initial_location: Option<&'a FileUri> });
345            impl_de!(struct Res { uri: Option<FileUri> });
346
347            let _guard = self.0.intent_lock.lock();
348            self.0.api
349                .run_mobile_plugin::<Res>("showManageDirDialog", Req { initial_location })
350                .map(|v| v.uri)
351                .map_err(Into::into)
352        })
353    }
354
355    /// Opens a system file saver and returns a **writeonly** URI.  
356    /// The returned file may be a newly created file with no content,
357    /// or it may be an existing file with the requested MIME type.  
358    /// If the user cancels, `None` is returned. 
359    /// 
360    /// By default, returned URI is valid until the app is terminated. 
361    /// If you want to persist it across app restarts, use [`AndroidFs::take_persistable_uri_permission`].
362    /// 
363    /// This provides a standardized file explorer-style interface, 
364    /// and also allows file selection from part of third-party apps or cloud storage.
365    /// 
366    /// Removing and reading the returned files is also supported in most cases, 
367    /// but note that files provided by third-party apps may not.  
368    ///  
369    /// # Args  
370    /// - ***initial_location*** :  
371    /// Indicate the initial location of dialog.    
372    /// This URI works even without any permissions.  
373    /// There is no need to use this if there is no special reason.  
374    /// System will do its best to launch the dialog in the specified entry 
375    /// if it's a directory, or the directory that contains the specified file if not.  
376    /// If this is missing or failed to resolve the desired initial location, the initial location is system specific.   
377    /// This must be a URI taken from following :   
378    ///     - [`AndroidFs::resolve_initial_location`]
379    ///     - [`AndroidFs::try_resolve_file_uri`]
380    ///     - [`AndroidFs::try_resolve_dir_uri`]
381    ///     - [`AndroidFs::resolve_uri`]
382    ///     - [`AndroidFs::read_dir`]
383    ///     - [`AndroidFs::create_file`]
384    ///     - [`AndroidFs::create_dir_all`]
385    ///     - [`AndroidFs::rename`]
386    ///     - [`FilePicker::pick_files`]
387    ///     - [`FilePicker::pick_file`]
388    ///     - [`FilePicker::pick_dir`]
389    ///     - [`FilePicker::save_file`]
390    /// 
391    /// - ***initial_file_name*** :  
392    /// An initial file name.  
393    /// The user may change this value before creating the file.  
394    /// If no extension is present, 
395    /// the system may infer one from ***mime_type*** and may append it to the file name. 
396    /// But this append-extension operation depends on the model and version.
397    /// 
398    /// - ***mime_type*** :  
399    /// The MIME type of the file to be saved.  
400    /// If this is None, MIME type is inferred from the extension of ***initial_file_name*** (not file name by user input)
401    /// and if that fails, `application/octet-stream` is used.  
402    ///  
403    /// # Support
404    /// All.
405    /// 
406    /// # References
407    /// <https://developer.android.com/reference/android/content/Intent#ACTION_CREATE_DOCUMENT>
408    pub fn save_file(
409        &self,
410        initial_location: Option<&FileUri>,
411        initial_file_name: impl AsRef<str>,
412        mime_type: Option<&str>,
413    ) -> crate::Result<Option<FileUri>> {
414        
415        on_android!({
416            impl_se!(struct Req<'a> {
417                initial_file_name: &'a str, 
418                mime_type: Option<&'a str>, 
419                initial_location: Option<&'a FileUri> 
420            });
421            impl_de!(struct Res { uri: Option<FileUri> });
422    
423            let initial_file_name = initial_file_name.as_ref();
424        
425            let _guard = self.0.intent_lock.lock();
426            self.0.api
427                .run_mobile_plugin::<Res>("showSaveFileDialog", Req { initial_file_name, mime_type, initial_location })
428                .map(|v| v.uri)
429                .map_err(Into::into)
430        })
431    }
432
433    /// Verify whether [`FilePicker::pick_visual_medias`] is available on a given device.
434    /// 
435    /// # Support
436    /// All.
437    pub fn is_visual_media_picker_available(&self) -> crate::Result<bool> {
438        on_android!({
439            impl_de!(struct Res { value: bool });
440
441            self.0.api
442                .run_mobile_plugin::<Res>("isVisualMediaDialogAvailable", "")
443                .map(|v| v.value)
444                .map_err(Into::into)
445        })
446    }
447}