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}