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} */