tauri_plugin_android_fs/api/
app_storage.rs

1use sync_async::sync_async;
2use crate::*;
3use super::*;
4
5
6/// API of file storage intended for the app's use.  
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 app_storage = api.app_storage();
15/// }
16/// ```
17#[sync_async]
18pub struct AppStorage<'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> AppStorage<'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, PublicStorage, PrivateStorage};
42    use(if_sync) api_sync::{AndroidFs, FileOpener, FilePicker, PublicStorage, PrivateStorage};
43)]
44impl<'a, R: tauri::Runtime> AppStorage<'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 function returns only storage volume that is considered stable by system. 
53    /// It includes device’s built-in storage and physical media slots under protective covers,
54    /// but does not include storage volume considered temporary, 
55    /// such as USB flash drives connected to handheld devices.
56    /// 
57    /// This typically includes [`primary storage volume`](AppStorage::get_primary_volume),
58    /// but it may occasionally be absent if primary torage volume is inaccessible 
59    /// (e.g., mounted on a computer, removed, or another issue).
60    ///
61    /// Primary storage volume is always listed first, if included. 
62    /// But the order of the others is not guaranteed.  
63    /// 
64    /// # Note
65    /// The volume represents the logical view of a storage volume for an individual user:
66    /// each user may have a different view for the same physical volume.
67    /// In other words, it provides a separate area for each user in a multi-user environment.
68    /// 
69    /// # Support
70    /// All Android version.
71    #[maybe_async]
72    pub fn get_volumes(&self) -> Result<Vec<StorageVolume>> {
73        #[cfg(not(target_os = "android"))] {
74            Err(Error::NOT_ANDROID)
75        }
76        #[cfg(target_os = "android")] {
77            self.impls().get_available_storage_volumes_for_app_storage().await
78        }
79    }
80
81    /// Gets a primary storage volume.  
82    /// In many cases, it is device's built-in storage. 
83    /// 
84    /// A device always has one (and one only) primary storage volume.  
85    /// 
86    /// Primary volume may not currently be accessible 
87    /// if it has been mounted by the user on their computer, 
88    /// has been removed from the device, or some other problem has happened. 
89    /// If so, this returns `None`.
90    /// 
91    /// # Note
92    /// The volume represents the logical view of a storage volume for an individual user:
93    /// each user may have a different view for the same physical volume.
94    /// In other words, it provides a separate area for each user in a multi-user environment.
95    /// 
96    /// # Support
97    /// All Android version.
98    #[maybe_async]
99    pub fn get_primary_volume(&self) -> Result<Option<StorageVolume>> {
100        #[cfg(not(target_os = "android"))] {
101            Err(Error::NOT_ANDROID)
102        }
103        #[cfg(target_os = "android")] {
104            self.impls().get_primary_storage_volume_if_available_for_app_storage().await
105        }
106    }
107
108    /// Gets the absolute path of the app directory on the specified storage volume.  
109    /// App can fully manage entries within this directory via [`std::fs`] and etc.   
110    /// 
111    /// This function does **not** create any directories; it only constructs the path.
112    ///    
113    /// These entries will be deleted when the app is uninstalled 
114    /// and may also be deleted at the user’s initialising request.   
115    /// 
116    /// Since storage volume id and returned paths can change,
117	/// only relative paths should be stored.
118    /// 
119    /// # Note
120    /// ### About AppDir::Data and AppDir::Cache
121    /// Since these locations may contain files created by other Tauri plugins or webview systems, 
122    /// it is recommended to add a subdirectory with a unique name.
123    ///
124    /// If you are unsure between this function and [`PrivateStorage::resolve_path`] with [`PrivateDir::Data`] or [`PrivateDir::Cache`],
125    /// you don’t need to use this one.  
126    /// The difference from it is that these files may be accessed by user or other apps that have permissions,
127    /// and it cannot always be available since removable storage can be ejected.  
128    /// 
129    /// One advantage of using this is that it allows storing large app-specific data/cache on SD cards or other supplementary storage, 
130    /// which can be useful on older devices with limited built-in storage capacity. 
131    /// However on modern devices, the built-in storage capacity is relatively large,
132    /// and there is little advantage in using this.  
133    /// 
134    /// By using [`StorageVolume { is_emulated, .. }`](StorageVolume), 
135    /// you can determine whether this belongs to the same storage volume as [`PrivateStorage::resolve_path`]. 
136    /// If it is `true`, there is no advantage in using this instead of [`PrivateStorage::resolve_path`]. 
137    /// It only reduces security.
138    /// 
139    /// ### About AppDir::PublicMedia
140    /// This is a location for storing media files shared with other apps nad user on older versions of Android. 
141    /// For Android 11 (API level 30) or higher, 
142    /// it has been marked as deprecated. 
143    /// It still works, but you should consider migrating to [`PublicStorage`].
144    /// 
145    /// This is a location that is unfamiliar to the user, 
146    /// but calling [`AppStorage::scan_public_media_by_path`] will make it 
147    /// displayed in a more user-friendly way in gallery apps and file managers.  
148    /// 
149    /// For file in this directory, do not use operations such as rename or remove that rely on paths 
150    /// (including URIs obtained via [`FileUri::from_path`] with this paths), 
151    /// as they may break consistency with the MediaStore on old version.
152    /// Instead, use the URI obtained through [`AppStorage::scan_public_media_by_path`] together with methods 
153    /// such as [`AndroidFs::rename`] or [`AndroidFs::remove_file`].
154    /// 
155    /// # Args
156    /// - ***volume_id*** :  
157    /// ID of the storage volume, such as internal storage, SD card, etc.  
158    /// If `None` is provided, [`the primary storage volume`](AppStorage::get_primary_volume) will be used.  
159    /// 
160    /// # Support
161    /// All Android version. 
162    #[maybe_async]
163    pub fn resolve_path(
164        &self, 
165        volume_id: Option<&StorageVolumeId>,
166        dir: AppDir
167    ) -> Result<std::path::PathBuf> {
168
169        #[cfg(not(target_os = "android"))] {
170            Err(Error::NOT_ANDROID)
171        }
172        #[cfg(target_os = "android")] {
173            self.impls().resolve_dir_path_in_app_storage(volume_id, dir).await
174        }
175    }
176
177    /// See [`AppStorage::resolve_path`] and [`FileUri::from_path`].
178    #[maybe_async]
179    pub fn resolve_uri(
180        &self, 
181        volume_id: Option<&StorageVolumeId>,
182        dir: AppDir,
183        relative_path: impl AsRef<std::path::Path>
184    ) -> Result<FileUri> {
185
186        #[cfg(not(target_os = "android"))] {
187            Err(Error::NOT_ANDROID)
188        }
189        #[cfg(target_os = "android")] {
190            let mut path = self.resolve_path(volume_id, dir).await?;
191            path.push(relative_path.as_ref());
192            Ok(path.into())
193        }
194    }
195
196    /// Scans the specified file in MediaStore and returns it's URI if success.   
197    /// By doing this, the file will be visible in the Gallery and etc.
198    ///
199    /// # Args
200    /// - ***uri*** :  
201    /// Absolute path of the target file.   
202    /// This must be a path obtained from [`AppStorage::resolve_path`] with [`AppDir::PublicMedia`]
203    /// and it's descendants path.
204    /// 
205    /// - ***mime_type*** :  
206    /// The MIME type of the file.  
207    /// If `None`, the MIME type will be inferred from the extension of the path.  
208    /// If that also fails, `application/octet-stream` will be used.
209    /// 
210    /// # Support
211    /// All Android version.
212    #[maybe_async]
213    pub fn scan_public_media_by_path(
214        &self,
215        path: impl AsRef<std::path::Path>,
216        mime_type: Option<&str>,
217    ) -> Result<FileUri> {
218
219        #[cfg(not(target_os = "android"))] {
220            Err(Error::NOT_ANDROID)
221        }
222        #[cfg(target_os = "android")] {
223            self.impls().scan_file_to_media_store_by_path(path, mime_type).await
224        }
225    }
226
227    /// Gets the absolute path of the specified file.
228    /// 
229    /// # Args
230    /// - ***uri*** :   
231    /// Target file URI.
232    /// This must be a URI obtained from [`AppStorage::scan_public_media_by_path`].
233    /// 
234    /// # Support
235    /// All Android version.
236    #[maybe_async]
237    pub fn get_public_media_path(
238        &self,
239        uri: &FileUri,
240    ) -> Result<std::path::PathBuf> {
241
242        #[cfg(not(target_os = "android"))] {
243            Err(Error::NOT_ANDROID)
244        }
245        #[cfg(target_os = "android")] {
246            self.impls().get_public_media_file_path_in_app_storage(uri).await
247        }
248    }
249}