Skip to main content

tauri_plugin_android_fs/api/
public_storage.rs

1use sync_async::sync_async;
2use crate::*;
3use super::*;
4
5
6/// API of file storage that is available to other applications and users.  
7/// 
8/// # Examples
9/// ```no_run
10/// use tauri_plugin_android_fs::{AndroidFsExt, Error, PublicGeneralPurposeDir, PublicImageDir, Result};
11///
12/// async fn example(app: &tauri::AppHandle<impl tauri::Runtime>) -> Result<()> {
13///     let api = app.android_fs_async();
14///
15///     // Request permission to access public storage.
16///     //
17///     // NOTE:
18///     // Enable the `legacy_storage_permission` feature
19///     // when supporting Android 9 (API level 28) or lower.
20///     if !api.public_storage().request_permission().await? {
21///         return Err(Error::with("Permission denied by user"));
22///     }
23///
24///     // Save a new file.
25///     //
26///     // Destination:
27///     // ~/Pictures/MyApp/my-image.png
28///     api.public_storage().write_new(
29///         // Storage volume (e.g. internal storage or SD card).
30///         // If `None`, uses the primary storage volume.
31///         None,
32///
33///         // Base directory.
34///         // One of:
35///         // `PublicImageDir`, `PublicVideoDir`,
36///         // `PublicAudioDir`, `PublicGeneralPurposeDir`.
37///         PublicImageDir::Pictures,
38///
39///         // Relative file path.
40///         // Parent directories will be created recursively if necessary.
41///         "MyApp/my-image.png",
42///
43///         // MIME type.
44///         Some("image/png"),
45///
46///         // File contents.
47///         &[],
48///     )
49///     .await?;
50///
51/// 
52///     // Select a writable SD card if available;
53///     // otherwise, pass `None` to use the primary storage volume.
54///     let volume = api
55///         .public_storage()
56///         .get_volumes()
57///         .await?
58///         .into_iter()
59///         .filter(|v| !v.is_readonly)
60///         .filter(|v| v.is_stable)
61///         .filter(|v| v.is_removable)
62///         .next();
63///
64///     // Create an empty file and mark it as pending,
65///     // making it invisible to other apps until writing is complete.
66///     let uri = api
67///         .public_storage()
68///         .create_new_file_with_pending(
69///             volume.as_ref().map(|v| &v.id),
70///             PublicGeneralPurposeDir::Documents,
71///             "MyApp/2025-9-14/data.txt",
72///             Some("text/plain"),
73///         )
74///         .await?;
75///
76///     // Open the file for writing.
77///     // 
78///     // NOTE:
79///     // Existing contents will be truncated.
80///     let mut file: std::fs::File = api.open_file_writable(&uri).await?;
81///
82///     // Write the file contents on a blocking thread.
83///     let result = tauri::async_runtime::spawn_blocking(move || -> Result<()> {
84///         use std::io::Write;
85///
86///         // Write the file contents.
87///         file.write_all(&[])?;
88///
89///         Ok(())
90///     })
91///     .await
92///     .map_err(Into::into)
93///     .and_then(|r| r);
94///
95///     // Delete the incomplete file if writing failed.
96///     if let Err(err) = result {
97///         api.remove_file(&uri).await.ok();
98///         return Err(err);
99///     }
100///
101///     // Make the file visible to other apps.
102///     api.public_storage().set_pending(&uri, false).await?;
103///
104///     // Notify the media scanner about the new file.
105///     api.public_storage().scan(&uri).await?;
106///
107///     Ok(())
108/// }
109/// ```
110#[sync_async]
111pub struct PublicStorage<'a, R: tauri::Runtime> {
112    #[cfg(target_os = "android")]
113    pub(crate) handle: &'a tauri::plugin::PluginHandle<R>,
114
115    #[cfg(not(target_os = "android"))]
116    #[allow(unused)]
117    pub(crate) handle: &'a std::marker::PhantomData<fn() -> R>,
118}
119
120#[cfg(target_os = "android")]
121#[sync_async(
122    use(if_sync) impls::SyncImpls as Impls;
123    use(if_async) impls::AsyncImpls as Impls;
124)]
125impl<'a, R: tauri::Runtime> PublicStorage<'a, R> {
126    
127    #[always_sync]
128    fn impls(&self) -> Impls<'_, R> {
129        Impls { handle: &self.handle }
130    }
131}
132
133#[sync_async(
134    use(if_async) api_async::{AndroidFs, Opener, Picker, AppStorage, PrivateStorage};
135    use(if_sync) api_sync::{AndroidFs, Opener, Picker, AppStorage, PrivateStorage};
136)]
137impl<'a, R: tauri::Runtime> PublicStorage<'a, R> {
138
139    /// Requests file access permission from the user if needed.
140    ///
141    /// When this function returns `true`,
142    /// the app is allowed to create files in `PublicStorage` and read/write the files it creates. 
143    /// Access to files created by other apps is not guaranteed.
144    /// Additionally, after the app is uninstalled and reinstalled, 
145    /// previously created files may become inaccessible.
146    ///
147    /// # Version behavior
148    /// ### Android 11 or higher
149    /// Requests no permission.   
150    /// This function always returns `true`.
151    /// 
152    /// ### Android 10
153    /// Selects either the behavior for Android 11 or higher or for Android 9 or lower, as needed.
154    ///
155    /// ### Android 9 or lower
156    /// Requests [`WRITE_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#WRITE_EXTERNAL_STORAGE) and [`READ_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE) permissions.   
157    /// To request the permissions, you must declare it in `AndroidManifest.xml`.
158    /// By enabling the `legacy_storage_permission` feature,
159    /// the permissions will be declared automatically only for Android 9 or lower.
160    ///
161    /// # Support
162    /// All Android versions
163    #[maybe_async]
164    pub fn request_permission(&self) -> Result<bool> {
165        #[cfg(not(target_os = "android"))] {
166            Err(Error::NOT_ANDROID)
167        }
168        #[cfg(target_os = "android")] {
169            self.impls().request_storage_permission_for_public_storage().await
170        }
171    }
172
173    /// Indicates whether the app has file access permission.
174    ///
175    /// When this function returns `true`,
176    /// the app is allowed to create files in `PublicStorage` and read/write the files it creates. 
177    /// Access to files created by other apps is not guaranteed.
178    /// Additionally, after the app is uninstalled and reinstalled, 
179    /// previously created files may become inaccessible.
180    ///
181    /// # Version behavior
182    /// ### Android 11 or higher
183    /// Always returns `true`.
184    ///
185    /// ### Android 10
186    /// Selects either the behavior for Android 11 or higher or for Android 9 or lower, as needed.
187    ///
188    /// ### Android 9 or lower
189    /// Returns `true` if the app has been granted [`WRITE_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#WRITE_EXTERNAL_STORAGE) and [`READ_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE) permissions.    
190    /// See [`PublicStorage::request_permission`] for requesting the permissions.
191    ///
192    /// # Support
193    /// All Android versions.
194    #[maybe_async]
195    pub fn check_permission(&self) -> Result<bool> {
196        #[cfg(not(target_os = "android"))] {
197            Err(Error::NOT_ANDROID)
198        }
199        #[cfg(target_os = "android")] {
200            self.impls().check_storage_permission_for_public_storage().await
201        }
202    }
203
204    /// Gets a list of currently available storage volumes (internal storage, SD card, USB drive, etc.).
205    /// Be aware of TOCTOU.
206    /// 
207    /// Since read-only SD cards and similar cases may be included, 
208    /// please use [`StorageVolume { is_readonly, .. }`](StorageVolume) for filtering as needed.
209    /// 
210    /// This typically includes [`primary storage volume`](PublicStorage::get_primary_volume),
211    /// but it may occasionally be absent if the primary volume is inaccessible 
212    /// (e.g., mounted on a computer, removed, or another issue).
213    ///
214    /// Primary storage volume is always listed first, if included. 
215    /// But the order of the others is not guaranteed.  
216    /// 
217    /// # Note
218    /// For Android 9 (API level 28) or lower, 
219    /// this does not include any storage volumes other than the primary one. 
220    /// 
221    /// The volume represents the logical view of a storage volume for an individual user:
222    /// each user may have a different view for the same physical volume.
223    /// In other words, it provides a separate area for each user in a multi-user environment.
224    /// 
225    /// # Support
226    /// All Android versions supported by Tauri.  
227    #[maybe_async]
228    pub fn get_volumes(&self) -> Result<Vec<StorageVolume>> {
229        #[cfg(not(target_os = "android"))] {
230            Err(Error::NOT_ANDROID)
231        }
232        #[cfg(target_os = "android")] {
233            self.impls().get_available_storage_volumes_for_public_storage().await
234        }
235    }
236
237    /// Gets a primary storage volume.  
238    /// This is the most common and recommended storage volume for placing files that can be accessed by other apps or user.
239    /// In many cases, it is device's built-in storage.  
240    /// 
241    /// A device always has one (and one only) primary storage volume.  
242    /// 
243    /// Primary volume may not currently be accessible 
244    /// if it has been mounted by the user on their computer, 
245    /// has been removed from the device, or some other problem has happened. 
246    /// If so, this returns `None`.
247    /// 
248    /// # Note
249    /// The volume represents the logical view of a storage volume for an individual user:
250    /// each user may have a different view for the same physical volume.
251    /// In other words, it provides a separate area for each user in a multi-user environment.
252    /// 
253    /// # Support
254    /// All Android versions supported by Tauri.
255    #[maybe_async]
256    pub fn get_primary_volume(&self) -> Result<Option<StorageVolume>> {
257        #[cfg(not(target_os = "android"))] {
258            Err(Error::NOT_ANDROID)
259        }
260        #[cfg(target_os = "android")] {
261            self.impls().get_primary_storage_volume_if_available_for_public_storage().await
262        }
263    }
264
265    /// Creates a new empty file in the specified public directory of the storage volume.  
266    /// This returns a **persistent read-write** URI.
267    ///
268    /// The app can read/write it until the app is uninstalled. 
269    /// And it is **not** removed when the app itself is uninstalled.  
270    /// 
271    /// # Note
272    /// ### Android 10 or higher. 
273    /// Files are automatically registered in the appropriate MediaStore as needed. 
274    /// Scanning is triggered when the file descriptor is closed
275    /// or as part of the [`pending`](PublicStorage::set_pending) lifecycle.
276    /// 
277    /// ### Android 9 or lower
278    /// [`WRITE_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#WRITE_EXTERNAL_STORAGE) and [`READ_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE) permissions are required.    
279    /// This needs two steps: 
280    /// 
281    /// 1. Declare :  
282    ///     By enabling the `legacy_storage_permission` feature,  
283    ///     you can declare the permissions only for Android 9 or lower automatically at build time.  
284    ///
285    /// 2. Runtime request :  
286    ///     By calling [`PublicStorage::request_permission`],
287    ///     you can request the permissions from the user at runtime.  
288    ///
289    /// After writing content to the file, call [`PublicStorage::scan`].  
290    /// Until then, the file may not appear in the gallery or other apps.
291    /// 
292    /// # Args
293    /// - ***volume_id*** :  
294    /// The ID of the storage volume, such as internal storage or an SD card.  
295    /// Usually, you don't need to specify this unless there is a special reason.  
296    /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.
297    ///
298    /// - ***base_dir*** :  
299    /// The base directory for the file.  
300    /// When using [`PublicImageDir`], only image MIME types should be used for ***mime_type***; using other types may cause errors.  
301    /// Similarly, [`PublicVideoDir`] and [`PublicAudioDir`] should only be used with their respective media types.  
302    /// Only [`PublicGeneralPurposeDir`] supports all MIME types.
303    ///
304    /// - ***relative_path*** :  
305    /// The file path relative to the base directory.  
306    /// To keep files organized, it is recommended to place your app's name directory at the top level.  
307    /// Any missing parent directories will be created automatically.  
308    /// If a file with the same name already exists, a sequential number is appended to ensure uniqueness.  
309    /// If the file has no extension, one may be inferred from ***mime_type*** and appended to the file name.  
310    /// Strings may also be sanitized as needed, so they may not be used exactly as provided.
311    /// Note that append-exntesion and sanitize-path operation may vary depending on the device model and Android version.  
312    ///
313    /// - ***mime_type*** :  
314    /// The MIME type of the file to be created.  
315    /// If `None`, the MIME type will be inferred from the extension of ***relative_path***.  
316    /// If that also fails, `application/octet-stream` will be used.
317    /// 
318    /// # Support
319    /// All Android versions supported by Tauri.
320    ///
321    /// Note :  
322    /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
323    /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].  
324    /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
325    /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].  
326    /// - Others dirs are available in all Android versions.
327    #[maybe_async]
328    pub fn create_new_file(
329        &self,
330        volume_id: Option<&StorageVolumeId>,
331        base_dir: impl Into<PublicDir>,
332        relative_path: impl AsRef<std::path::Path>, 
333        mime_type: Option<&str>
334    ) -> Result<FsUri> {
335
336        #[cfg(not(target_os = "android"))] {
337            Err(Error::NOT_ANDROID)
338        }
339        #[cfg(target_os = "android")] {
340            self.impls().create_new_file_in_public_storage(
341                volume_id, 
342                base_dir, 
343                relative_path, 
344                mime_type, 
345                false
346            ).await
347        }
348    }
349
350    /// Creates a new empty file in the specified public directory of the storage volume.  
351    /// This returns a **persistent read-write** URI.
352    ///
353    /// The app can read/write it until the app is uninstalled. 
354    /// And it is **not** removed when the app itself is uninstalled.  
355    /// 
356    /// # Note
357    /// ### Android 10 or higher
358    /// Files are automatically registered in the appropriate MediaStore as needed. 
359    /// Scanning is triggered when the file descriptor is closed
360    /// or as part of the [`pending`](PublicStorage::set_pending) lifecycle.
361    /// 
362    /// Diffrences from [`PublicStorage::create_new_file`] are that
363    /// files are marked as pending and will not be visible to other apps until 
364    /// [`PublicStorage::set_pending(..., false)`](PublicStorage::set_pending) is called. 
365    ///
366    /// ### Android 9 or lower
367    /// This behavior is equal to [`PublicStorage::create_new_file`]. 
368    /// So `pending` is ignored.  
369    /// 
370    /// [`WRITE_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#WRITE_EXTERNAL_STORAGE) and [`READ_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE) permissions are required.    
371    /// This needs two steps: 
372    /// 
373    /// 1. Declare :  
374    ///     By enabling the `legacy_storage_permission` feature,  
375    ///     you can declare the permissions only for Android 9 or lower automatically at build time.  
376    ///
377    /// 2. Runtime request :  
378    ///     By calling [`PublicStorage::request_permission`],
379    ///     you can request the permissions from the user at runtime.  
380    ///
381    /// After writing content to the file, call [`PublicStorage::scan`].  
382    /// Until then, the file may not appear in the gallery or other apps.
383    /// 
384    /// # Args
385    /// - ***volume_id*** :  
386    /// The ID of the storage volume, such as internal storage or an SD card.  
387    /// Usually, you don't need to specify this unless there is a special reason.  
388    /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.
389    ///
390    /// - ***base_dir*** :  
391    /// The base directory for the file.  
392    /// When using [`PublicImageDir`], only image MIME types should be used for ***mime_type***; using other types may cause errors.  
393    /// Similarly, [`PublicVideoDir`] and [`PublicAudioDir`] should only be used with their respective media types.  
394    /// Only [`PublicGeneralPurposeDir`] supports all MIME types.
395    ///
396    /// - ***relative_path*** :  
397    /// The file path relative to the base directory.  
398    /// To keep files organized, it is recommended to place your app's name directory at the top level.  
399    /// Any missing parent directories will be created automatically.  
400    /// If a file with the same name already exists, a sequential number is appended to ensure uniqueness.  
401    /// If the file has no extension, one may be inferred from ***mime_type*** and appended to the file name.  
402    /// Strings may also be sanitized as needed, so they may not be used exactly as provided.
403    /// Note that append-exntesion and sanitize-path operation may vary depending on the device model and Android version.  
404    ///
405    /// - ***mime_type*** :  
406    /// The MIME type of the file to be created.  
407    /// If `None`, the MIME type will be inferred from the extension of ***relative_path***.  
408    /// If that also fails, `application/octet-stream` will be used.
409    /// 
410    /// # Support
411    /// All Android versions supported by Tauri.
412    ///
413    /// Note :  
414    /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
415    /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].  
416    /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
417    /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].  
418    /// - Others dirs are available in all Android versions.
419    #[maybe_async]
420    pub fn create_new_file_with_pending(
421        &self,
422        volume_id: Option<&StorageVolumeId>,
423        base_dir: impl Into<PublicDir>,
424        relative_path: impl AsRef<std::path::Path>, 
425        mime_type: Option<&str>
426    ) -> Result<FsUri> {
427
428        #[cfg(not(target_os = "android"))] {
429            Err(Error::NOT_ANDROID)
430        }
431        #[cfg(target_os = "android")] {
432            self.impls().create_new_file_in_public_storage(
433                volume_id, 
434                base_dir, 
435                relative_path, 
436                mime_type, 
437                true
438            ).await
439        }
440    }
441
442    /// Recursively create a directory and all of its parent components if they are missing.  
443    /// If it already exists, do nothing.
444    /// 
445    /// [`PublicStorage::create_new_file`] and [`PublicStorage::create_new_file_with_pending`]
446    /// do this automatically, so there is no need to use it together.
447    /// 
448    /// # Note
449    /// On Android 9 or lower,
450    /// [`WRITE_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#WRITE_EXTERNAL_STORAGE) and [`READ_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE) permissions are required.    
451    /// This needs two steps: 
452    /// 
453    /// 1. Declare :  
454    ///     By enabling the `legacy_storage_permission` feature,  
455    ///     you can declare the permissions only for Android 9 or lower automatically at build time.  
456    ///
457    /// 2. Runtime request :  
458    ///     By calling [`PublicStorage::request_permission`],
459    ///     you can request the permissions from the user at runtime.  
460    ///
461    /// # Args  
462    /// - ***volume_id*** :  
463    /// ID of the storage volume, such as internal storage, SD card, etc.  
464    /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.  
465    /// 
466    /// - ***base_dir*** :  
467    /// The base directory.  
468    ///  
469    /// - ***relative_path*** :  
470    /// The directory path relative to the base directory.    
471    /// Strings may also be sanitized as needed, so they may not be used exactly as provided.
472    /// Note that sanitize-path operation may vary depending on the device model and Android version.  
473    ///
474    /// # Support
475    /// All Android versions supported by Tauri.
476    ///
477    /// Note :  
478    /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
479    /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].  
480    /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
481    /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].  
482    /// - Others dirs are available in all Android versions.
483    #[maybe_async]
484    pub fn create_dir_all(
485        &self,
486        volume_id: Option<&StorageVolumeId>,
487        base_dir: impl Into<PublicDir>,
488        relative_path: impl AsRef<std::path::Path>, 
489    ) -> Result<()> {
490
491        #[cfg(not(target_os = "android"))] {
492            Err(Error::NOT_ANDROID)
493        }
494        #[cfg(target_os = "android")] {
495            self.impls().create_dir_all_in_public_storage(volume_id, base_dir, relative_path).await
496        }
497    }
498
499    /// Writes contents to the new file in the specified public directory of the storage volume.  
500    /// This returns a **persistent read-write** URI.
501    ///
502    /// The app can read/write it until the app is uninstalled. 
503    /// And it is **not** removed when the app itself is uninstalled.  
504    /// 
505    /// # Note
506    /// On Android 9 or lower,
507    /// [`WRITE_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#WRITE_EXTERNAL_STORAGE) and [`READ_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE) permissions are required.    
508    /// This needs two steps: 
509    /// 
510    /// 1. Declare :  
511    ///     By enabling the `legacy_storage_permission` feature,  
512    ///     you can declare the permissions only for Android 9 or lower automatically at build time.  
513    ///
514    /// 2. Runtime request :  
515    ///     By calling [`PublicStorage::request_permission`],
516    ///     you can request the permissions from the user at runtime.  
517    ///
518    /// # Args
519    /// - ***volume_id*** :  
520    /// The ID of the storage volume, such as internal storage or an SD card.  
521    /// Usually, you don't need to specify this unless there is a special reason.  
522    /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.
523    ///
524    /// - ***base_dir*** :  
525    /// The base directory for the file.  
526    /// When using [`PublicImageDir`], only image MIME types should be used for ***mime_type***; using other types may cause errors.  
527    /// Similarly, [`PublicVideoDir`] and [`PublicAudioDir`] should only be used with their respective media types.  
528    /// Only [`PublicGeneralPurposeDir`] supports all MIME types.
529    ///
530    /// - ***relative_path*** :  
531    /// The file path relative to the base directory.  
532    /// To keep files organized, it is recommended to place your app's name directory at the top level.  
533    /// Any missing parent directories will be created automatically.  
534    /// If a file with the same name already exists, a sequential number is appended to ensure uniqueness.  
535    /// If the file has no extension, one may be inferred from ***mime_type*** and appended to the file name.  
536    /// Strings may also be sanitized as needed, so they may not be used exactly as provided.
537    /// Note that append-exntesion and sanitize-path operation may vary depending on the device model and Android version.  
538    ///
539    /// - ***mime_type*** :  
540    /// The MIME type of the file to be created.  
541    /// If `None`, the MIME type will be inferred from the extension of ***relative_path***.  
542    /// If that also fails, `application/octet-stream` will be used.
543    /// 
544    /// - ***contents*** :  
545    /// Contents.
546    /// 
547    /// # Support
548    /// All Android versions supported by Tauri.
549    ///
550    /// Note :  
551    /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
552    /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].  
553    /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
554    /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].  
555    /// - Others dirs are available in all Android versions.
556    #[maybe_async]
557    pub fn write_new(
558        &self,
559        volume_id: Option<&StorageVolumeId>,
560        base_dir: impl Into<PublicDir>,
561        relative_path: impl AsRef<std::path::Path>,
562        mime_type: Option<&str>,
563        contents: impl AsRef<[u8]>
564    ) -> Result<FsUri> {
565
566        #[cfg(not(target_os = "android"))] {
567            Err(Error::NOT_ANDROID)
568        }
569        #[cfg(target_os = "android")] {
570            self.impls().write_new_file_in_public_storage(volume_id, base_dir, relative_path, mime_type, contents).await
571        }
572    }
573
574    /// Scans the specified file in MediaStore.   
575    /// By doing this, the file will be visible with corrent metadata in the Gallery and etc.
576    ///
577    /// You don’t need to call this after [`PublicStorage::write_new`].   
578    /// 
579    /// # Version behavior
580    /// ### Android 10 or higher
581    /// This function does nothing, 
582    /// because files are automatically registered in the appropriate MediaStore as needed. 
583    /// Scanning is triggered when the file descriptor is closed
584    /// or as part of the [`pending`](PublicStorage::set_pending) lifecycle.  
585    /// 
586    /// If you really need to perform this operation in this version, 
587    /// please use [`PublicStorage::_scan`].
588    ///
589    /// ### Android 9 or lower
590    /// Requests the specified file to be scanned by MediaStore.  
591    /// This function returns when the scan request has been initiated.   
592    /// 
593    /// # Args
594    /// - ***uri*** :  
595    /// The target file URI.
596    /// This must be a URI obtained from one of the following:  
597    ///     - [`PublicStorage::write_new`]
598    ///     - [`PublicStorage::create_new_file`]
599    ///     - [`PublicStorage::create_new_file_with_pending`]
600    ///     - [`PublicStorage::scan_by_path`]
601    ///
602    /// # Support
603    /// All Android versions.
604    #[maybe_async]
605    pub fn scan(
606        &self, 
607        uri: &FsUri,
608    ) -> Result<()> {
609
610        #[cfg(not(target_os = "android"))] {
611            Err(Error::NOT_ANDROID)
612        }
613        #[cfg(target_os = "android")] {
614            self.impls().scan_file_in_public_storage(uri, false).await
615        }
616    }
617
618    /// Scans the specified file in MediaStore and returns it's URI if success.   
619    /// By doing this, the file will be visible in the Gallery and etc.
620    ///
621    /// # Note
622    /// Unlike [`PublicStorage::scan`], 
623    /// this function waits until the scan is complete and then returns either success or an error.
624    /// 
625    /// # Args
626    /// - ***uri*** :  
627    /// Absolute path of the target file.
628    /// This must be a path obtained from one of the following:  
629    ///     - [`PublicStorage::resolve_path`] and it's descendants path.
630    ///     - [`PublicStorage::get_path`]
631    /// 
632    /// - ***mime_type*** :  
633    /// The MIME type of the file.  
634    /// If `None`, the MIME type will be inferred from the extension of the path.  
635    /// If that also fails, `application/octet-stream` will be used.
636    /// 
637    /// # Support
638    /// All Android versions supported by Tauri.
639    #[maybe_async]
640    pub fn scan_by_path(
641        &self, 
642        path: impl AsRef<std::path::Path>,
643        mime_type: Option<&str>
644    ) -> Result<FsUri> {
645
646        #[cfg(not(target_os = "android"))] {
647            Err(Error::NOT_ANDROID)
648        }
649        #[cfg(target_os = "android")] {
650            self.impls().scan_file_by_path_in_public_storage(path, mime_type).await
651        }
652    }
653
654    /// Specifies whether the specified file on PublicStorage is marked as pending.   
655    /// When set to `true`, the app has exclusive access to the file, and it becomes invisible to other apps.
656    /// 
657    /// If it remains `true` for more than seven days, 
658    /// the system will automatically delete the file.
659    /// 
660    /// # Note
661    /// This is available for Android 10 or higher.  
662    /// On Android 9 or lower, this does nothing. 
663    /// 
664    /// # Args
665    /// - ***uri*** :  
666    /// Target file URI.
667    /// This must be a URI obtained from one of the following:  
668    ///     - [`PublicStorage::write_new`]
669    ///     - [`PublicStorage::create_new_file`]
670    ///     - [`PublicStorage::create_new_file_with_pending`]
671    ///     - [`PublicStorage::scan_by_path`]
672    /// 
673    /// # Support
674    /// All Android versions supported by Tauri.
675    /// 
676    /// # References
677    /// - <https://developer.android.com/reference/android/provider/MediaStore.MediaColumns#IS_PENDING>
678    /// - <https://developer.android.com/training/data-storage/shared/media?hl=en#toggle-pending-status>
679    #[maybe_async]
680    pub fn set_pending(&self, uri: &FsUri, is_pending: bool) -> Result<()> {
681        #[cfg(not(target_os = "android"))] {
682            Err(Error::NOT_ANDROID)
683        }
684        #[cfg(target_os = "android")] {
685            self.impls().set_file_pending_in_public_storage(uri, is_pending).await
686        }
687    }
688
689    /// Gets the absolute path of the specified file.
690    /// 
691    /// # Note
692    /// For description and notes on path permissions and handling, 
693    /// see [`PublicStorage::resolve_path`].
694    /// It involves important constraints and required settings. 
695    /// Therefore, **operate files via paths only when it is truly necessary**.
696    /// 
697    /// # Args
698    /// - ***uri*** :   
699    /// Target file URI.
700    /// This must be a URI obtained from one of the following:  
701    ///     - [`PublicStorage::write_new`]
702    ///     - [`PublicStorage::create_new_file`]
703    ///     - [`PublicStorage::create_new_file_with_pending`]
704    ///     - [`PublicStorage::scan_by_path`]
705    /// 
706    /// # Support
707    /// All Android versions supported by Tauri.
708    #[deprecated = "File operations via paths may result in unstable behaviour and inconsistent outcomes."]
709    #[maybe_async]
710    pub fn get_path(
711        &self,
712        uri: &FsUri,
713    ) -> Result<std::path::PathBuf> {
714
715        #[cfg(not(target_os = "android"))] {
716            Err(Error::NOT_ANDROID)
717        }
718        #[cfg(target_os = "android")] {
719            self.impls().get_file_path_in_public_storage(uri).await
720        }
721    }
722
723    /// Retrieves the absolute path for a specified public directory within the given storage volume.   
724    /// This function does **not** create any directories; it only constructs the path.
725    /// 
726    /// The app is allowed to create files in the directory and read/write the files it creates. 
727    /// Access to files created by other apps is not guaranteed.
728    /// Additionally, after the app is uninstalled and reinstalled, 
729    /// previously created files may become inaccessible.
730    /// 
731    /// The directory is **not** removed when the app itself is uninstalled.  
732    /// 
733    /// It is strongly recommended to call [`PublicStorage::scan_by_path`] 
734    /// after writing to the file to request registration in MediaStore.  
735    /// 
736    /// # Note
737    /// As shown below, this involves important constraints and required settings.
738    /// Therefore, **operate files via paths only when it is truly necessary**.
739    /// 
740    /// Do not use operations such as rename or remove that rely on paths 
741    /// (including URIs obtained via [`FileUri::from_path`] with this paths), 
742    /// as they may break consistency with the MediaStore on old version.
743    /// Instead, use the URI obtained through [`PublicStorage::scan_by_path`] together with methods 
744    /// such as [`AndroidFs::rename`] or [`AndroidFs::remove_file`].
745    /// 
746    /// ### Android 11 or higher
747    /// When using [`PublicImageDir`], use only image type for file name extension, 
748    /// using other type extension or none may cause errors.
749    /// Similarly, use only the corresponding extesions for [`PublicVideoDir`] and [`PublicAudioDir`].
750    /// Only [`PublicGeneralPurposeDir`] supports all extensions and no extension. 
751    /// 
752    /// ### Android 10 or lower
753    /// [`WRITE_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#WRITE_EXTERNAL_STORAGE) and [`READ_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE) permissions are required.    
754    /// This needs two steps: 
755    /// 
756    /// 1. Declare :  
757    ///     By enabling the `legacy_storage_permission_include_android_10` feature,  
758    ///     you can declare the permissions only for Android 10 or lower automatically at build time.  
759    /// 
760    /// 2. Runtime request :  
761    ///     By calling [`PublicStorage::request_permission`],
762    ///     you can request the permissions from the user at runtime.  
763    ///
764    /// ### Android 10
765    /// Files within PublicStorage cannot be accessed via file paths, 
766    /// so it is necessary to declare that the app need access `PublicStorage` using the method of Android 9 or lower.   
767    /// 
768    /// For it, please [set `android:requestLegacyExternalStorage="true"`](https://developer.android.com/training/data-storage/use-cases#opt-out-in-production-app).
769    /// 
770    /// `src-tauri/gen/android/app/src/main/AndroidManifest.xml`
771    /// ```xml
772    /// <manifest ... >
773    ///     <application 
774  	///         android:requestLegacyExternalStorage="true" 
775  	///         ...
776    ///     >
777    ///         ...
778    ///     </application>
779    /// </manifest>
780    /// ```
781    /// And it is not possible to access `PublicStorage`
782	/// on volumes other than the primary storage via paths, just like on Android 9 or lower.   
783	/// But filtering using [`PublicStorage::get_volumes`]
784	/// or [`StorageVolume::is_available_for_public_storage`] may not work correctly, 
785	/// as these are intended for access via URIs.
786    /// 
787    /// # Args
788    /// - ***volume_id*** :  
789    /// ID of the storage volume, such as internal storage, SD card, etc.  
790    /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.  
791    /// 
792    /// - ***base_dir*** :  
793    /// The base directory.  
794    /// One of [`PublicImageDir`], [`PublicVideoDir`], [`PublicAudioDir`], [`PublicGeneralPurposeDir`].  
795    ///  
796    /// # Support
797    /// All Android versions supported by Tauri.
798    /// 
799    /// Note :  
800    /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
801    /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].  
802    /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
803    /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].  
804    /// - Others dirs are available in all Android versions.
805    #[deprecated = "File operations via paths may result in unstable behaviour and inconsistent outcomes."]
806    #[maybe_async]
807    pub fn resolve_path(
808        &self,
809        volume_id: Option<&StorageVolumeId>,
810        base_dir: impl Into<PublicDir>,
811    ) -> Result<std::path::PathBuf> {
812
813        #[cfg(not(target_os = "android"))] {
814            Err(Error::NOT_ANDROID)
815        }
816        #[cfg(target_os = "android")] {
817            self.impls().resolve_dir_path_in_public_storage(volume_id, base_dir).await
818        }
819    }
820
821    /// Verify whether [`PublicAudioDir::Audiobooks`] is available on a given device.   
822    /// 
823    /// If on Android 10 (API level 29) or higher, this returns true.  
824    /// If on Android 9 (API level 28) and lower, this returns false.  
825    /// 
826    /// # Support
827    /// All Android versions supported by Tauri.
828    #[always_sync]
829    pub fn is_audiobooks_dir_available(&self) -> Result<bool> {
830        #[cfg(not(target_os = "android"))] {
831            Err(Error::NOT_ANDROID)
832        }
833        #[cfg(target_os = "android")] {
834            Ok(self.impls().consts()?.env_dir_audiobooks.is_some())
835        }
836    }
837
838    /// Verify whether [`PublicAudioDir::Recordings`] is available on a given device.   
839    /// 
840    /// If on Android 12 (API level 31) or higher, this returns true.  
841    /// If on Android 11 (API level 30) and lower, this returns false.  
842    /// 
843    /// # Support
844    /// All Android versions supported by Tauri.
845    #[always_sync]
846    pub fn is_recordings_dir_available(&self) -> Result<bool> {
847        #[cfg(not(target_os = "android"))] {
848            Err(Error::NOT_ANDROID)
849        }
850        #[cfg(target_os = "android")] {
851            Ok(self.impls().consts()?.env_dir_recordings.is_some())
852        }
853    }
854
855    /// For details, see [`PublicStorage::scan`].
856    /// 
857    /// The difference is that this method scans the file even on Android 10 or higher.
858    /// 
859    /// On Android 10 or higher, files are automatically registered in the appropriate MediaStore as needed. 
860    /// Scanning is triggered when the file descriptor is closed
861    /// or as part of the [`pending`](PublicStorage::set_pending) lifecycle.  
862    /// Therefore, please consider carefully whether this is truly necessary. 
863    #[maybe_async]
864    pub fn _scan(
865        &self, 
866        uri: &FsUri,
867    ) -> Result<()> {
868
869        #[cfg(not(target_os = "android"))] {
870            Err(Error::NOT_ANDROID)
871        }
872        #[cfg(target_os = "android")] {
873            self.impls().scan_file_in_public_storage(uri, true).await
874        }
875    }
876
877    /// For details, see [`PublicStorage::_scan`].
878    /// 
879    /// The difference is that this function waits until the scan is complete and then returns either success or an error.
880    #[maybe_async]
881    pub fn _scan_for_result(
882        &self, 
883        uri: &FsUri,
884    ) -> Result<()> {
885
886        #[cfg(not(target_os = "android"))] {
887            Err(Error::NOT_ANDROID)
888        }
889        #[cfg(target_os = "android")] {
890            self.impls().scan_file_in_public_storage_for_result(uri, true).await
891        }
892    }
893
894
895
896    #[deprecated = "Use `Picker::resolve_public_storage_initial_location` instead."]
897    #[maybe_async]
898    pub fn resolve_initial_location(
899        &self,
900        volume_id: Option<&StorageVolumeId>,
901        base_dir: impl Into<PublicDir>,
902        relative_path: impl AsRef<std::path::Path>,
903        create_dir_all: bool
904    ) -> Result<FsUri> {
905
906        #[cfg(not(target_os = "android"))] {
907            Err(Error::NOT_ANDROID)
908        }
909        #[cfg(target_os = "android")] {
910            self.impls().resolve_initial_location_in_public_storage(volume_id, base_dir, relative_path, create_dir_all).await
911        }
912    }
913}