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).await
163        }
164    }
165
166    /// Recursively create a directory and all of its parent components if they are missing.  
167    /// If it already exists, do nothing.
168    /// 
169    /// [`PublicStorage::create_new_file`] does this automatically, so there is no need to use it together.
170    /// 
171    /// # Args  
172    /// - ***volume_id*** :  
173    /// ID of the storage volume, such as internal storage, SD card, etc.  
174    /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.  
175    /// 
176    /// - ***base_dir*** :  
177    /// The base directory.  
178    ///  
179    /// - ***relative_path*** :  
180    /// The directory path relative to the base directory.    
181    /// The system may sanitize these strings as needed, so those strings may not be used as it is.
182    ///  
183    /// # Support
184    /// Android 10 (API level 29) or higher.  
185    ///
186    /// Note :  
187    /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
188    /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].  
189    /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
190    /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].  
191    /// - Others dirs are available in all Android versions.
192    #[maybe_async]
193    pub fn create_dir_all(
194        &self,
195        volume_id: Option<&StorageVolumeId>,
196        base_dir: impl Into<PublicDir>,
197        relative_path: impl AsRef<std::path::Path>, 
198    ) -> Result<()> {
199
200        #[cfg(not(target_os = "android"))] {
201            Err(Error::NOT_ANDROID)
202        }
203        #[cfg(target_os = "android")] {
204            self.impls().create_dir_all_in_public_storage(volume_id, base_dir, relative_path).await
205        }
206    }
207
208    /// Retrieves the absolute path for a specified public directory within the given storage volume.   
209    /// This function does **not** create any directories; it only constructs the path.
210    /// 
211    /// **Please avoid using this whenever possible.**    
212    /// Use it only in cases that cannot be handled by [`PublicStorage::create_new_file`] or [`PrivateStorage::resolve_path`], 
213    /// 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.  
214    ///
215    /// Since **Android 11** (not Android 10),
216    /// you can create files and folders under this directory and read or write **only** them.  
217    /// If not, you can do nothing with this path.
218    /// 
219    /// When using [`PublicImageDir`], use only image type for file name extension, 
220    /// using other type extension or none may cause errors.
221    /// Similarly, use only the corresponding extesions for [`PublicVideoDir`] and [`PublicAudioDir`].
222    /// Only [`PublicGeneralPurposeDir`] supports all extensions and no extension. 
223    /// 
224    /// # Note
225    /// Filesystem access via this path may be heavily impacted by emulation overhead.
226    /// And those files will not be registered in MediaStore. 
227    /// It might eventually be registered over time, but this should not be expected.
228    /// As a result, it may not appear in gallery apps or photo picker tools.
229    /// 
230    /// You cannot access files created by other apps. 
231    /// Additionally, if the app is uninstalled, 
232    /// you will no longer be able to access the files you created, 
233    /// even if the app is reinstalled.  
234    /// Android tends to restrict public file access using paths, so this may stop working in the future.
235    /// 
236    /// # Args
237    /// - ***volume_id*** :  
238    /// ID of the storage volume, such as internal storage, SD card, etc.  
239    /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.  
240    /// 
241    /// - ***base_dir*** :  
242    /// The base directory.  
243    ///  
244    /// # Support
245    /// Android 10 (API level 29) or higher.  
246    /// 
247    /// Note :  
248    /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
249    /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].  
250    /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
251    /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].  
252    /// - Others dirs are available in all Android versions.
253    #[maybe_async]
254    pub fn resolve_path(
255        &self,
256        volume_id: Option<&StorageVolumeId>,
257        base_dir: impl Into<PublicDir>,
258    ) -> Result<std::path::PathBuf> {
259
260        #[cfg(not(target_os = "android"))] {
261            Err(Error::NOT_ANDROID)
262        }
263        #[cfg(target_os = "android")] {
264            self.impls().resolve_path(volume_id, base_dir).await
265        }
266    }
267
268    /// Create the specified directory URI that has **no permissions**.  
269    /// 
270    /// This should only be used as `initial_location` in the file picker. 
271    /// It must not be used for any other purpose.  
272    /// 
273    /// This is useful when selecting save location, 
274    /// but when selecting existing entries, `initial_location` is often better with None.
275    /// 
276    /// # Args  
277    /// - ***volume_id*** :  
278    /// ID of the storage volume, such as internal storage, SD card, etc.  
279    /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.  
280    /// 
281    /// - ***base_dir*** :  
282    /// The base directory.  
283    ///  
284    /// - ***relative_path*** :  
285    /// The directory path relative to the base directory.    
286    ///  
287    /// # Support
288    /// All Android version.
289    ///
290    /// Note :  
291    /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
292    /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].  
293    /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
294    /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].  
295    /// - Others dirs are available in all Android versions.
296    #[maybe_async]
297    pub fn resolve_initial_location(
298        &self,
299        volume_id: Option<&StorageVolumeId>,
300        base_dir: impl Into<PublicDir>,
301        relative_path: impl AsRef<std::path::Path>,
302        create_dir_all: bool
303    ) -> Result<FileUri> {
304
305        #[cfg(not(target_os = "android"))] {
306            Err(Error::NOT_ANDROID)
307        }
308        #[cfg(target_os = "android")] {
309            self.impls().resolve_public_storage_initial_location(volume_id, base_dir, relative_path, create_dir_all).await
310        }
311    }
312
313    /// Create the specified directory URI that has **no permissions**.  
314    /// 
315    /// This should only be used as `initial_location` in the file picker. 
316    /// It must not be used for any other purpose.  
317    /// 
318    /// This is useful when selecting save location, 
319    /// but when selecting existing entries, `initial_location` is often better with None.
320    /// 
321    /// # Args  
322    /// - ***volume_id*** :  
323    /// ID of the storage volume, such as internal storage, SD card, etc.  
324    /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.  
325    /// 
326    /// # Support
327    /// All Android version.
328    ///
329    /// Note :  
330    /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
331    /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].  
332    /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
333    /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].  
334    /// - Others dirs are available in all Android versions.
335    #[maybe_async]
336    pub fn resolve_initial_location_top(
337        &self,
338        volume_id: Option<&StorageVolumeId>
339    ) -> Result<FileUri> {
340
341        #[cfg(not(target_os = "android"))] {
342            Err(Error::NOT_ANDROID)
343        }
344        #[cfg(target_os = "android")] {
345            self.impls().resolve_public_storage_initial_location_top(volume_id).await
346        }
347    }
348
349    /// Verify whether the basic functions of PublicStorage 
350    /// (such as [`PublicStorage::create_new_file`]) can be performed.
351    /// 
352    /// If on Android 9 (API level 28) and lower, this returns false.  
353    /// If on Android 10 (API level 29) or higher, this returns true.  
354    /// 
355    /// # Support
356    /// All Android version.
357    #[always_sync]
358    pub fn is_available(&self) -> Result<bool> {
359        #[cfg(not(target_os = "android"))] {
360            Err(Error::NOT_ANDROID)
361        }
362        #[cfg(target_os = "android")] {
363            Ok(api_level::ANDROID_10 <= self.impls().api_level()?)
364        }
365    }
366
367    /// Verify whether [`PublicAudioDir::Audiobooks`] is available on a given device.   
368    /// 
369    /// If on Android 9 (API level 28) and lower, this returns false.  
370    /// If on Android 10 (API level 29) or higher, this returns true.  
371    /// 
372    /// # Support
373    /// All Android version.
374    #[always_sync]
375    pub fn is_audiobooks_dir_available(&self) -> Result<bool> {
376        #[cfg(not(target_os = "android"))] {
377            Err(Error::NOT_ANDROID)
378        }
379        #[cfg(target_os = "android")] {
380            Ok(self.impls().consts()?.env_dir_audiobooks.is_some())
381        }
382    }
383
384    /// Verify whether [`PublicAudioDir::Recordings`] is available on a given device.   
385    /// 
386    /// If on Android 11 (API level 30) and lower, this returns false.  
387    /// If on Android 12 (API level 31) or higher, this returns true.  
388    /// 
389    /// # Support
390    /// All Android version.
391    #[always_sync]
392    pub fn is_recordings_dir_available(&self) -> Result<bool> {
393        #[cfg(not(target_os = "android"))] {
394            Err(Error::NOT_ANDROID)
395        }
396        #[cfg(target_os = "android")] {
397            Ok(self.impls().consts()?.env_dir_recordings.is_some())
398        }
399    }
400}