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