tauri_plugin_android_fs/api/
public_storage.rs

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