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/// fn example(app: &tauri::AppHandle) {
11///     use tauri_plugin_android_fs::AndroidFsExt as _;
12/// 
13///     let api = app.android_fs();
14///     let public_storage = api.public_storage();
15/// }
16/// ```
17#[sync_async]
18pub struct PublicStorage<'a, R: tauri::Runtime> {
19    #[cfg(target_os = "android")]
20    pub(crate) handle: &'a tauri::plugin::PluginHandle<R>,
21
22    #[cfg(not(target_os = "android"))]
23    #[allow(unused)]
24    pub(crate) handle: &'a std::marker::PhantomData<fn() -> R>,
25}
26
27#[cfg(target_os = "android")]
28#[sync_async(
29    use(if_sync) impls::SyncImpls as Impls;
30    use(if_async) impls::AsyncImpls as Impls;
31)]
32impl<'a, R: tauri::Runtime> PublicStorage<'a, R> {
33    
34    #[always_sync]
35    fn impls(&self) -> Impls<'_, R> {
36        Impls { handle: &self.handle }
37    }
38}
39
40#[sync_async(
41    use(if_async) api_async::{AndroidFs, FileOpener, FilePicker, PrivateStorage, WritableStream};
42    use(if_sync) api_sync::{AndroidFs, FileOpener, FilePicker, PrivateStorage, WritableStream};
43)]
44impl<'a, R: tauri::Runtime> PublicStorage<'a, R> {
45
46    /// Gets a list of currently available storage volumes (internal storage, SD card, USB drive, etc.).
47    /// Be aware of TOCTOU.
48    /// 
49    /// Since read-only SD cards and similar cases may be included, 
50    /// please use [`StorageVolume { is_readonly, .. }`](StorageVolume) for filtering as needed.
51    /// 
52    /// This typically includes [`primary storage volume`](PublicStorage::get_primary_volume),
53    /// but it may occasionally be absent if the primary volume is inaccessible 
54    /// (e.g., mounted on a computer, removed, or another issue).
55    ///
56    /// Primary storage volume is always listed first, if included. 
57    /// But the order of the others is not guaranteed.  
58    ///
59    /// # Note
60    /// The volume represents the logical view of a storage volume for an individual user:
61    /// each user may have a different view for the same physical volume.
62    /// In other words, it provides a separate area for each user in a multi-user environment.
63    /// 
64    /// # Support
65    /// Android 10 (API level 29) or higher.  
66    #[maybe_async]
67    pub fn get_volumes(&self) -> Result<Vec<StorageVolume>> {
68        #[cfg(not(target_os = "android"))] {
69            Err(Error::NOT_ANDROID)
70        }
71        #[cfg(target_os = "android")] {
72            self.impls().get_available_storage_volumes_for_public_storage().await
73        }
74    }
75
76    /// Gets a primary storage volume.  
77    /// This is the most common and recommended storage volume for placing files that can be accessed by other apps or user.
78    /// In many cases, it is device's built-in storage.  
79    /// 
80    /// A device always has one (and one only) primary storage volume.  
81    /// 
82    /// Primary volume may not currently be accessible 
83    /// if it has been mounted by the user on their computer, 
84    /// has been removed from the device, or some other problem has happened. 
85    /// If so, this returns `None`.
86    /// 
87    /// # Note
88    /// The volume represents the logical view of a storage volume for an individual user:
89    /// each user may have a different view for the same physical volume.
90    /// In other words, it provides a separate area for each user in a multi-user environment.
91    /// 
92    /// # Support
93    /// Android 10 (API level 29) or higher.   
94    #[maybe_async]
95    pub fn get_primary_volume(&self) -> Result<Option<StorageVolume>> {
96        #[cfg(not(target_os = "android"))] {
97            Err(Error::NOT_ANDROID)
98        }
99        #[cfg(target_os = "android")] {
100            self.impls().get_primary_storage_volume_if_available_for_public_storage().await
101        }
102    }
103
104    /// Creates a new empty file in the specified public directory of the storage volume.  
105    /// This returns a **persistent read-write** URI.
106    ///
107    /// The created file has the following features:  
108    /// - It is registered with the appropriate MediaStore as needed.  
109    /// - The app can fully manage it until the app is uninstalled.  
110    /// - It is **not** removed when the app itself is uninstalled.  
111    /// 
112    /// # Args
113    /// - ***volume_id*** :  
114    /// ID of the storage volume, such as internal storage, SD card, etc.  
115    /// Usually, you don't need to specify this unless there is a special reason.  
116    /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.  
117    /// 
118    /// - ***base_dir*** :  
119    /// The base directory.  
120    /// When using [`PublicImageDir`], use only image MIME types for ***mime_type***, which is discussed below.; using other types may cause errors.
121    /// Similarly, use only the corresponding media types for [`PublicVideoDir`] and [`PublicAudioDir`].
122    /// Only [`PublicGeneralPurposeDir`] supports all MIME types. 
123    /// 
124    /// - ***relative_path*** :  
125    /// The file path relative to the base directory.  
126    /// To avoid cluttering files, it is helpful to place the app name directory at the top level.   
127    /// Any missing subdirectories in the specified path will be created automatically.  
128    /// If a file with the same name already exists, 
129    /// the system append a sequential number to ensure uniqueness.  
130    /// If no extension is present, 
131    /// the system may infer one from ***mime_type*** and may append it to the file name. 
132    /// But this append-extension operation depends on the model and version.  
133    /// The system may sanitize these strings as needed, so those strings may not be used as it is.
134    ///  
135    /// - ***mime_type*** :  
136    /// The MIME type of the file to be created.  
137    /// If this is None, MIME type is inferred from the extension of ***relative_path***
138    /// and if that fails, `application/octet-stream` is used.  
139    /// 
140    /// # Support
141    /// Android 10 (API level 29) or higher.  
142    ///
143    /// Note :  
144    /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
145    /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].  
146    /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
147    /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].  
148    /// - Others dirs are available in all Android versions.
149    #[maybe_async]
150    pub fn create_new_file(
151        &self,
152        volume_id: Option<&StorageVolumeId>,
153        base_dir: impl Into<PublicDir>,
154        relative_path: impl AsRef<std::path::Path>, 
155        mime_type: Option<&str>
156    ) -> Result<FileUri> {
157
158        #[cfg(not(target_os = "android"))] {
159            Err(Error::NOT_ANDROID)
160        }
161        #[cfg(target_os = "android")] {
162            self.impls().create_new_file_in_public_storage(volume_id, base_dir, relative_path, mime_type, false).await
163        }
164    }
165
166    /// Creates a new empty file in the specified public directory of the storage volume.  
167    /// This returns a **persistent read-write** URI.
168    ///
169    /// The created file has the following features:  
170    /// - Marked as pending and will not be visible to other apps until [`PublicStorage::set_pending(..., false)`](PublicStorage::set_pending) is called.
171    /// - It is registered with the appropriate MediaStore as needed.  
172    /// - The app can fully manage it until the app is uninstalled.  
173    /// - It is **not** removed when the app itself is uninstalled.  
174    /// 
175    /// # Args
176    /// - ***volume_id*** :  
177    /// ID of the storage volume, such as internal storage, SD card, etc.  
178    /// Usually, you don't need to specify this unless there is a special reason.  
179    /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.  
180    /// 
181    /// - ***base_dir*** :  
182    /// The base directory.  
183    /// When using [`PublicImageDir`], use only image MIME types for ***mime_type***, which is discussed below.; using other types may cause errors.
184    /// Similarly, use only the corresponding media types for [`PublicVideoDir`] and [`PublicAudioDir`].
185    /// Only [`PublicGeneralPurposeDir`] supports all MIME types. 
186    /// 
187    /// - ***relative_path*** :  
188    /// The file path relative to the base directory.  
189    /// To avoid cluttering files, it is helpful to place the app name directory at the top level.   
190    /// Any missing subdirectories in the specified path will be created automatically.  
191    /// If a file with the same name already exists, 
192    /// the system append a sequential number to ensure uniqueness.  
193    /// If no extension is present, 
194    /// the system may infer one from ***mime_type*** and may append it to the file name. 
195    /// But this append-extension operation depends on the model and version.  
196    /// The system may sanitize these strings as needed, so those strings may not be used as it is.
197    ///  
198    /// - ***mime_type*** :  
199    /// The MIME type of the file to be created.  
200    /// If this is None, MIME type is inferred from the extension of ***relative_path***
201    /// and if that fails, `application/octet-stream` is used.  
202    /// 
203    /// # Support
204    /// Android 10 (API level 29) or higher.  
205    ///
206    /// Note :  
207    /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
208    /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].  
209    /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
210    /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].  
211    /// - Others dirs are available in all Android versions.
212    #[maybe_async]
213    pub fn create_new_file_with_pending(
214        &self,
215        volume_id: Option<&StorageVolumeId>,
216        base_dir: impl Into<PublicDir>,
217        relative_path: impl AsRef<std::path::Path>, 
218        mime_type: Option<&str>
219    ) -> Result<FileUri> {
220
221        #[cfg(not(target_os = "android"))] {
222            Err(Error::NOT_ANDROID)
223        }
224        #[cfg(target_os = "android")] {
225            self.impls().create_new_file_in_public_storage(volume_id, base_dir, relative_path, mime_type, true).await
226        }
227    }
228
229    /// Recursively create a directory and all of its parent components if they are missing.  
230    /// If it already exists, do nothing.
231    /// 
232    /// [`PublicStorage::create_new_file`] does this automatically, so there is no need to use it together.
233    /// 
234    /// # Args  
235    /// - ***volume_id*** :  
236    /// ID of the storage volume, such as internal storage, SD card, etc.  
237    /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.  
238    /// 
239    /// - ***base_dir*** :  
240    /// The base directory.  
241    ///  
242    /// - ***relative_path*** :  
243    /// The directory path relative to the base directory.    
244    /// The system may sanitize these strings as needed, so those strings may not be used as it is.
245    ///  
246    /// # Support
247    /// Android 10 (API level 29) or higher.  
248    ///
249    /// Note :  
250    /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
251    /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].  
252    /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
253    /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].  
254    /// - Others dirs are available in all Android versions.
255    #[maybe_async]
256    pub fn create_dir_all(
257        &self,
258        volume_id: Option<&StorageVolumeId>,
259        base_dir: impl Into<PublicDir>,
260        relative_path: impl AsRef<std::path::Path>, 
261    ) -> Result<()> {
262
263        #[cfg(not(target_os = "android"))] {
264            Err(Error::NOT_ANDROID)
265        }
266        #[cfg(target_os = "android")] {
267            self.impls().create_dir_all_in_public_storage(volume_id, base_dir, relative_path).await
268        }
269    }
270
271    /// Specifies whether the specified file on PublicStorage is marked as pending.   
272    /// When set to `true`, the app has exclusive access to the file, and it becomes invisible to other apps.
273    /// 
274    /// If it remains `true` for more than seven days, 
275    /// the system will automatically delete the file.
276    /// 
277    /// # Args
278    /// - ***uri*** :  
279    /// Target file URI on PublicStorage.
280    /// This must be **read-writable**.
281    /// 
282    /// # Support
283    /// Android 10 (API level 29) or higher.  
284    /// 
285    /// # References
286    /// <https://developer.android.com/reference/android/provider/MediaStore.MediaColumns#IS_PENDING>
287    /// <https://developer.android.com/training/data-storage/shared/media?hl=en#toggle-pending-status>
288    #[maybe_async]
289    pub fn set_pending(&self, uri: &FileUri, is_pending: bool) -> Result<()> {
290        #[cfg(not(target_os = "android"))] {
291            Err(Error::NOT_ANDROID)
292        }
293        #[cfg(target_os = "android")] {
294            self.impls().set_file_pending_in_public_storage(uri, is_pending).await
295        }
296    }
297
298    /// Retrieves the absolute path for a specified public directory within the given storage volume.   
299    /// This function does **not** create any directories; it only constructs the path.
300    /// 
301    /// **Please avoid using this whenever possible.**    
302    /// Use it only in cases that cannot be handled by [`PublicStorage::create_new_file`] or [`PrivateStorage::resolve_path`], 
303    /// such as when you need to pass the absolute path of a user-accessible file as an argument to any database library, debug logger, and etc.  
304    ///
305    /// Since **Android 11** (not Android 10),
306    /// you can create files and folders under this directory and read or write **only** them.  
307    /// If not, you can do nothing with this path.
308    /// 
309    /// When using [`PublicImageDir`], use only image type for file name extension, 
310    /// using other type extension or none may cause errors.
311    /// Similarly, use only the corresponding extesions for [`PublicVideoDir`] and [`PublicAudioDir`].
312    /// Only [`PublicGeneralPurposeDir`] supports all extensions and no extension. 
313    /// 
314    /// # Note
315    /// Filesystem access via this path may be heavily impacted by emulation overhead.
316    /// And those files will not be registered in MediaStore. 
317    /// It might eventually be registered over time, but this should not be expected.
318    /// As a result, it may not appear in gallery apps or photo picker tools.
319    /// 
320    /// You cannot access files created by other apps. 
321    /// Additionally, if the app is uninstalled, 
322    /// you will no longer be able to access the files you created, 
323    /// even if the app is reinstalled.  
324    /// Android tends to restrict public file access using paths, so this may stop working in the future.
325    /// 
326    /// # Args
327    /// - ***volume_id*** :  
328    /// ID of the storage volume, such as internal storage, SD card, etc.  
329    /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.  
330    /// 
331    /// - ***base_dir*** :  
332    /// The base directory.  
333    ///  
334    /// # Support
335    /// Android 10 (API level 29) or higher.  
336    /// 
337    /// Note :  
338    /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
339    /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].  
340    /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
341    /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].  
342    /// - Others dirs are available in all Android versions.
343    #[maybe_async]
344    pub fn resolve_path(
345        &self,
346        volume_id: Option<&StorageVolumeId>,
347        base_dir: impl Into<PublicDir>,
348    ) -> Result<std::path::PathBuf> {
349
350        #[cfg(not(target_os = "android"))] {
351            Err(Error::NOT_ANDROID)
352        }
353        #[cfg(target_os = "android")] {
354            self.impls().resolve_path(volume_id, base_dir).await
355        }
356    }
357
358    /// Create the specified directory URI that has **no permissions**.  
359    /// 
360    /// This should only be used as `initial_location` in the file picker. 
361    /// It must not be used for any other purpose.  
362    /// 
363    /// This is useful when selecting save location, 
364    /// but when selecting existing entries, `initial_location` is often better with None.
365    /// 
366    /// # Args  
367    /// - ***volume_id*** :  
368    /// ID of the storage volume, such as internal storage, SD card, etc.  
369    /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.  
370    /// 
371    /// - ***base_dir*** :  
372    /// The base directory.  
373    ///  
374    /// - ***relative_path*** :  
375    /// The directory path relative to the base directory.    
376    ///  
377    /// # Support
378    /// All Android version.
379    ///
380    /// Note :  
381    /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
382    /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].  
383    /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
384    /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].  
385    /// - Others dirs are available in all Android versions.
386    #[maybe_async]
387    pub fn resolve_initial_location(
388        &self,
389        volume_id: Option<&StorageVolumeId>,
390        base_dir: impl Into<PublicDir>,
391        relative_path: impl AsRef<std::path::Path>,
392        create_dir_all: bool
393    ) -> Result<FileUri> {
394
395        #[cfg(not(target_os = "android"))] {
396            Err(Error::NOT_ANDROID)
397        }
398        #[cfg(target_os = "android")] {
399            self.impls().resolve_public_storage_initial_location(volume_id, base_dir, relative_path, create_dir_all).await
400        }
401    }
402
403    /// Create the specified directory URI that has **no permissions**.  
404    /// 
405    /// This should only be used as `initial_location` in the file picker. 
406    /// It must not be used for any other purpose.  
407    /// 
408    /// This is useful when selecting save location, 
409    /// but when selecting existing entries, `initial_location` is often better with None.
410    /// 
411    /// # Args  
412    /// - ***volume_id*** :  
413    /// ID of the storage volume, such as internal storage, SD card, etc.  
414    /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.  
415    /// 
416    /// # Support
417    /// All Android version.
418    ///
419    /// Note :  
420    /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
421    /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].  
422    /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
423    /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].  
424    /// - Others dirs are available in all Android versions.
425    #[maybe_async]
426    pub fn resolve_initial_location_top(
427        &self,
428        volume_id: Option<&StorageVolumeId>
429    ) -> Result<FileUri> {
430
431        #[cfg(not(target_os = "android"))] {
432            Err(Error::NOT_ANDROID)
433        }
434        #[cfg(target_os = "android")] {
435            self.impls().resolve_public_storage_initial_location_top(volume_id).await
436        }
437    }
438
439    /// Verify whether the basic functions of PublicStorage 
440    /// (such as [`PublicStorage::create_new_file`]) can be performed.
441    /// 
442    /// If on Android 9 (API level 28) and lower, this returns false.  
443    /// If on Android 10 (API level 29) or higher, this returns true.  
444    /// 
445    /// # Support
446    /// All Android version.
447    #[always_sync]
448    pub fn is_available(&self) -> Result<bool> {
449        #[cfg(not(target_os = "android"))] {
450            Err(Error::NOT_ANDROID)
451        }
452        #[cfg(target_os = "android")] {
453            Ok(api_level::ANDROID_10 <= self.impls().api_level()?)
454        }
455    }
456
457    /// Verify whether [`PublicAudioDir::Audiobooks`] is available on a given device.   
458    /// 
459    /// If on Android 9 (API level 28) and lower, this returns false.  
460    /// If on Android 10 (API level 29) or higher, this returns true.  
461    /// 
462    /// # Support
463    /// All Android version.
464    #[always_sync]
465    pub fn is_audiobooks_dir_available(&self) -> Result<bool> {
466        #[cfg(not(target_os = "android"))] {
467            Err(Error::NOT_ANDROID)
468        }
469        #[cfg(target_os = "android")] {
470            Ok(self.impls().consts()?.env_dir_audiobooks.is_some())
471        }
472    }
473
474    /// Verify whether [`PublicAudioDir::Recordings`] is available on a given device.   
475    /// 
476    /// If on Android 11 (API level 30) and lower, this returns false.  
477    /// If on Android 12 (API level 31) or higher, this returns true.  
478    /// 
479    /// # Support
480    /// All Android version.
481    #[always_sync]
482    pub fn is_recordings_dir_available(&self) -> Result<bool> {
483        #[cfg(not(target_os = "android"))] {
484            Err(Error::NOT_ANDROID)
485        }
486        #[cfg(target_os = "android")] {
487            Ok(self.impls().consts()?.env_dir_recordings.is_some())
488        }
489    }
490}