tauri_plugin_android_fs/api/private_storage.rs
1use sync_async::sync_async;
2use crate::*;
3
4
5/// API of file storage intended for the app’s use only.
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 private_storage = api.private_storage();
14/// }
15/// ```
16#[sync_async]
17pub struct PrivateStorage<'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> PrivateStorage<'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, PublicStorage, WritableStream};
41 use(if_sync) super::api_sync::{AndroidFs, FileOpener, FilePicker, PublicStorage, WritableStream};
42)]
43impl<'a, R: tauri::Runtime> PrivateStorage<'a, R> {
44
45 /// Get an absolute path of the app-specific directory on the internal storage.
46 /// App can fully manage entries within this directory.
47 ///
48 /// This function does **not** create any directories; it only constructs the path.
49 ///
50 /// Since these locations may contain files created by other Tauri plugins or webview systems,
51 /// it is recommended to add a subdirectory with a unique name.
52 ///
53 /// These entries will be deleted when the app is uninstalled and may also be deleted at the user’s initialising request.
54 ///
55 /// When using [`PrivateDir::Cache`], the system will automatically delete entries as disk space is needed elsewhere on the device.
56 /// But you should not rely on this. The cache should be explicitly cleared by yourself.
57 ///
58 /// The system prevents other apps and user from accessing these locations.
59 /// In cases where the device is rooted or the user has special permissions, the user may be able to access this.
60 ///
61 /// Since the returned paths can change when the app is moved to an [adopted storage](https://source.android.com/docs/core/storage/adoptable),
62 /// only relative paths should be stored.
63 ///
64 /// # Note
65 /// This provides a separate area for each user in a multi-user environment.
66 ///
67 /// # Support
68 /// All Android version.
69 #[maybe_async]
70 pub fn resolve_path(
71 &self,
72 dir: PrivateDir
73 ) -> Result<std::path::PathBuf> {
74
75 #[cfg(not(target_os = "android"))] {
76 Err(Error::NOT_ANDROID)
77 }
78 #[cfg(target_os = "android")] {
79 self.impls().internal_private_dir_path(dir).map(Clone::clone)
80 }
81 }
82
83 /// Get an absolute path of the app-specific directory on the specified storage volume.
84 /// App can fully manage entries within this directory.
85 ///
86 /// This function does **not** create any directories; it only constructs the path.
87 ///
88 /// Since these locations may contain files created by other Tauri plugins or webview systems,
89 /// it is recommended to add a subdirectory with a unique name.
90 ///
91 /// These entries will be deleted when the app is uninstalled and may also be deleted at the user’s initialising request.
92 ///
93 /// # Note
94 /// If you are unsure between this function and [`PrivateStorage::resolve_path`],
95 /// you don’t need to use this one.
96 /// The difference from [`PrivateStorage::resolve_path`] is that these files may be accessed by other apps that have specific permissions,
97 /// and it cannot always be available since removable storage can be ejected.
98 ///
99 /// One advantage of using this is that it allows storing large app-specific data/cache on SD cards or other supplementary storage,
100 /// which can be useful on older devices with limited built-in storage capacity.
101 /// However on modern devices, the built-in storage capacity is relatively large,
102 /// and there is little advantage in using this.
103 ///
104 /// By using [`StorageVolume { is_emulated, .. }`](StorageVolume),
105 /// you can determine whether this belongs to the same storage volume as [`PrivateStorage::resolve_path`].
106 /// In this case, there is no advantage in using this instead of `PrivateStorage::resolve_path`.
107 /// It only reduces security.
108 ///
109 /// # Args
110 /// - ***volume_id*** :
111 /// ID of the storage volume, such as internal storage, SD card, etc.
112 /// If `None` is provided, [`the primary storage volume`](PrivateStorage::get_primary_volume) will be used.
113 ///
114 /// # Support
115 /// All Android version.
116 #[maybe_async]
117 pub fn resolve_outside_path(
118 &self,
119 volume_id: Option<&StorageVolumeId>,
120 dir: OutsidePrivateDir
121 ) -> Result<std::path::PathBuf> {
122
123 #[cfg(not(target_os = "android"))] {
124 Err(Error::NOT_ANDROID)
125 }
126 #[cfg(target_os = "android")] {
127 self.impls().resolve_outside_private_dir_path(volume_id, dir).await
128 }
129 }
130
131 /// Gets a list of currently available storage volumes (internal storage, SD card, USB drive, etc.).
132 /// Be aware of TOCTOU.
133 ///
134 /// Since read-only SD cards and similar cases may be included,
135 /// please use [`StorageVolume { is_readonly, .. }`](StorageVolume) for filtering as needed.
136 ///
137 /// This function returns only storage volume that is considered stable by system.
138 /// It includes device’s built-in storage and physical media slots under protective covers,
139 /// but does not include storage volume considered temporary,
140 /// such as USB flash drives connected to handheld devices.
141 ///
142 /// This typically includes [`primary storage volume`](PrivateStorage::get_primary_volume),
143 /// but it may occasionally be absent if primary torage volume is inaccessible
144 /// (e.g., mounted on a computer, removed, or another issue).
145 ///
146 /// Primary storage volume is always listed first, if included.
147 /// But the order of the others is not guaranteed.
148 ///
149 /// # Note
150 /// The volume represents the logical view of a storage volume for an individual user:
151 /// each user may have a different view for the same physical volume.
152 /// In other words, it provides a separate area for each user in a multi-user environment.
153 ///
154 /// # Support
155 /// All Android version.
156 #[maybe_async]
157 pub fn get_volumes(&self) -> Result<Vec<StorageVolume>> {
158 #[cfg(not(target_os = "android"))] {
159 Err(Error::NOT_ANDROID)
160 }
161 #[cfg(target_os = "android")] {
162 self.impls().get_available_storage_volumes_for_private_storage().await
163 }
164 }
165
166 /// Gets a primary storage volume.
167 /// In many cases, it is device's built-in storage.
168 ///
169 /// A device always has one (and one only) primary storage volume.
170 ///
171 /// Primary volume may not currently be accessible
172 /// if it has been mounted by the user on their computer,
173 /// has been removed from the device, or some other problem has happened.
174 /// If so, this returns `None`.
175 ///
176 /// # Note
177 /// The volume represents the logical view of a storage volume for an individual user:
178 /// each user may have a different view for the same physical volume.
179 /// In other words, it provides a separate area for each user in a multi-user environment.
180 ///
181 /// # Support
182 /// All Android version.
183 #[maybe_async]
184 pub fn get_primary_volume(&self) -> Result<Option<StorageVolume>> {
185 #[cfg(not(target_os = "android"))] {
186 Err(Error::NOT_ANDROID)
187 }
188 #[cfg(target_os = "android")] {
189 self.impls().get_primary_storage_volume_if_available_for_private_storage().await
190 }
191 }
192
193
194 /// This is same as [`FileUri::from_path`]
195 #[deprecated = "use `FileUri::from_path` instead"]
196 #[maybe_async]
197 pub fn resolve_uri(
198 &self,
199 dir: PrivateDir,
200 relative_path: impl AsRef<std::path::Path>
201 ) -> Result<FileUri> {
202
203 #[cfg(not(target_os = "android"))] {
204 Err(Error::NOT_ANDROID)
205 }
206 #[cfg(target_os = "android")] {
207 let mut path = self.resolve_path(dir).await?;
208 path.push(validate_relative_path(relative_path.as_ref())?);
209 Ok(path.into())
210 }
211 }
212}
213
214/*
215#[allow(unused)]
216impl<'a, R: tauri::Runtime> PrivateStorage<'a, R> {
217
218 pub(crate) fn create_new_tmp_file(&self) -> crate::Result<(std::fs::File, std::path::PathBuf)> {
219 on_android!({
220 let tmp_file_path = {
221 use std::sync::atomic::{AtomicUsize, Ordering};
222
223 static COUNTER: AtomicUsize = AtomicUsize::new(0);
224 let id = COUNTER.fetch_add(1, Ordering::Relaxed);
225
226 let tmp_dir_path = self.resolve_tmp_dir()?;
227 let _ = std::fs::create_dir_all(&tmp_dir_path);
228
229 tmp_dir_path.join(format!("{id}"))
230 };
231
232 let tmp_file = std::fs::File::create_new(&tmp_file_path)?;
233
234 Ok((tmp_file, tmp_file_path))
235 })
236 }
237
238 pub(crate) fn remove_all_tmp_files(&self) -> crate::Result<()> {
239 on_android!({
240 match std::fs::remove_dir_all(self.resolve_tmp_dir()?) {
241 Ok(_) => Ok(()),
242 Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()),
243 Err(e) => Err(e.into()),
244 }
245 })
246 }
247
248 pub(crate) fn resolve_tmp_dir(&self) -> crate::Result<std::path::PathBuf> {
249 on_android!({
250 const TMP_DIR_RELATIVE_PATH: &str = "pluginAndroidFs-tmpDir-01K486FKQ2BZSBGFD34RFH9FWJ";
251
252 let mut path = self.resolve_path(PrivateDir::Cache)?;
253 path.push(TMP_DIR_RELATIVE_PATH);
254 Ok(path)
255 })
256 }
257} */