tauri_plugin_android_fs/api/public_storage.rs
1use sync_async::sync_async;
2use crate::*;
3use super::*;
4
5
6/// API of file storage that is available to other applications and users.
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 public_storage = api.public_storage();
15/// }
16/// ```
17#[sync_async]
18pub struct PublicStorage<'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> PublicStorage<'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, AppStorage, PrivateStorage};
42 use(if_sync) api_sync::{AndroidFs, FileOpener, FilePicker, AppStorage, PrivateStorage};
43)]
44impl<'a, R: tauri::Runtime> PublicStorage<'a, R> {
45
46 /// Requests file access permission from the user if needed.
47 ///
48 /// When this function returns `true`,
49 /// the app is allowed to create files in `PublicStorage` and read/write the files it creates.
50 /// Access to files created by other apps is not guaranteed.
51 /// Additionally, after the app is uninstalled and reinstalled,
52 /// previously created files may become inaccessible.
53 ///
54 /// # Version behavior
55 /// ### Android 11 or higher
56 /// Requests no permission.
57 /// This function always returns `true`.
58 ///
59 /// ### Android 10
60 /// Selects either the behavior for Android 11 or higher or for Android 9 or lower, as needed.
61 ///
62 /// ### Android 9 or lower
63 /// Requests [`WRITE_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#WRITE_EXTERNAL_STORAGE) and [`READ_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE) permissions.
64 /// To request the permissions, you must declare it in `AndroidManifest.xml`.
65 /// By enabling the `legacy_storage_permission` feature,
66 /// the permissions will be declared automatically only for Android 9 or lower.
67 ///
68 /// # Support
69 /// All Android versions
70 #[maybe_async]
71 pub fn request_permission(&self) -> Result<bool> {
72 #[cfg(not(target_os = "android"))] {
73 Err(Error::NOT_ANDROID)
74 }
75 #[cfg(target_os = "android")] {
76 self.impls().request_storage_permission_for_public_storage().await
77 }
78 }
79
80 /// Indicates whether the app has file access permission.
81 ///
82 /// When this function returns `true`,
83 /// the app is allowed to create files in `PublicStorage` and read/write the files it creates.
84 /// Access to files created by other apps is not guaranteed.
85 /// Additionally, after the app is uninstalled and reinstalled,
86 /// previously created files may become inaccessible.
87 ///
88 /// # Version behavior
89 /// ### Android 11 or higher
90 /// Always returns `true`.
91 ///
92 /// ### Android 10
93 /// Selects either the behavior for Android 11 or higher or for Android 9 or lower, as needed.
94 ///
95 /// ### Android 9 or lower
96 /// Returns `true` if the app has been granted [`WRITE_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#WRITE_EXTERNAL_STORAGE) and [`READ_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE) permissions.
97 /// See [`PublicStorage::request_permission`] for requesting the permissions.
98 ///
99 /// # Support
100 /// All Android versions.
101 #[maybe_async]
102 pub fn has_permission(&self) -> Result<bool> {
103 #[cfg(not(target_os = "android"))] {
104 Err(Error::NOT_ANDROID)
105 }
106 #[cfg(target_os = "android")] {
107 self.impls().has_storage_permission_for_public_storage().await
108 }
109 }
110
111 /// Gets a list of currently available storage volumes (internal storage, SD card, USB drive, etc.).
112 /// Be aware of TOCTOU.
113 ///
114 /// Since read-only SD cards and similar cases may be included,
115 /// please use [`StorageVolume { is_readonly, .. }`](StorageVolume) for filtering as needed.
116 ///
117 /// This typically includes [`primary storage volume`](PublicStorage::get_primary_volume),
118 /// but it may occasionally be absent if the primary volume is inaccessible
119 /// (e.g., mounted on a computer, removed, or another issue).
120 ///
121 /// Primary storage volume is always listed first, if included.
122 /// But the order of the others is not guaranteed.
123 ///
124 /// # Note
125 /// For Android 9 (API level 28) or lower,
126 /// this does not include any storage volumes other than the primary one.
127 ///
128 /// The volume represents the logical view of a storage volume for an individual user:
129 /// each user may have a different view for the same physical volume.
130 /// In other words, it provides a separate area for each user in a multi-user environment.
131 ///
132 /// # Support
133 /// All Android version.
134 #[maybe_async]
135 pub fn get_volumes(&self) -> Result<Vec<StorageVolume>> {
136 #[cfg(not(target_os = "android"))] {
137 Err(Error::NOT_ANDROID)
138 }
139 #[cfg(target_os = "android")] {
140 self.impls().get_available_storage_volumes_for_public_storage().await
141 }
142 }
143
144 /// Gets a primary storage volume.
145 /// This is the most common and recommended storage volume for placing files that can be accessed by other apps or user.
146 /// In many cases, it is device's built-in storage.
147 ///
148 /// A device always has one (and one only) primary storage volume.
149 ///
150 /// Primary volume may not currently be accessible
151 /// if it has been mounted by the user on their computer,
152 /// has been removed from the device, or some other problem has happened.
153 /// If so, this returns `None`.
154 ///
155 /// # Note
156 /// The volume represents the logical view of a storage volume for an individual user:
157 /// each user may have a different view for the same physical volume.
158 /// In other words, it provides a separate area for each user in a multi-user environment.
159 ///
160 /// # Support
161 /// All Android version.
162 #[maybe_async]
163 pub fn get_primary_volume(&self) -> Result<Option<StorageVolume>> {
164 #[cfg(not(target_os = "android"))] {
165 Err(Error::NOT_ANDROID)
166 }
167 #[cfg(target_os = "android")] {
168 self.impls().get_primary_storage_volume_if_available_for_public_storage().await
169 }
170 }
171
172 /// Creates a new empty file in the specified public directory of the storage volume.
173 /// This returns a **persistent read-write** URI.
174 ///
175 /// The app can read/write it until the app is uninstalled.
176 /// And it is **not** removed when the app itself is uninstalled.
177 ///
178 /// # Note
179 /// ### Android 10 or higher.
180 /// Files are automatically registered in the appropriate MediaStore as needed.
181 /// Scanning is triggered when the file descriptor is closed
182 /// or as part of the [`pending`](PublicStorage::set_pending) lifecycle.
183 ///
184 /// ### Android 9 or lower
185 /// [`WRITE_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#WRITE_EXTERNAL_STORAGE) and [`READ_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE) permissions are required.
186 /// This needs two steps:
187 ///
188 /// 1. Declare :
189 /// By enabling the `legacy_storage_permission` feature,
190 /// you can declare the permissions only for Android 9 or lower automatically at build time.
191 ///
192 /// 2. Runtime request :
193 /// By calling [`PublicStorage::request_permission`],
194 /// you can request the permissions from the user at runtime.
195 ///
196 /// After writing content to the file, call [`PublicStorage::scan`].
197 /// Until then, the file may not appear in the gallery or other apps.
198 ///
199 /// # Args
200 /// - ***volume_id*** :
201 /// The ID of the storage volume, such as internal storage or an SD card.
202 /// Usually, you don't need to specify this unless there is a special reason.
203 /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.
204 ///
205 /// - ***base_dir*** :
206 /// The base directory for the file.
207 /// When using [`PublicImageDir`], only image MIME types should be used for ***mime_type***; using other types may cause errors.
208 /// Similarly, [`PublicVideoDir`] and [`PublicAudioDir`] should only be used with their respective media types.
209 /// Only [`PublicGeneralPurposeDir`] supports all MIME types.
210 ///
211 /// - ***relative_path*** :
212 /// The file path relative to the base directory.
213 /// To keep files organized, it is recommended to place your app's name directory at the top level.
214 /// Any missing parent directories will be created automatically.
215 /// If a file with the same name already exists, a sequential number is appended to ensure uniqueness.
216 /// If the file has no extension, one may be inferred from ***mime_type*** and appended to the file name.
217 /// Strings may also be sanitized as needed, so they may not be used exactly as provided.
218 /// Note that append-exntesion and sanitize-path operation may vary depending on the device model and Android version.
219 ///
220 /// - ***mime_type*** :
221 /// The MIME type of the file to be created.
222 /// If `None`, the MIME type will be inferred from the extension of ***relative_path***.
223 /// If that also fails, `application/octet-stream` will be used.
224 ///
225 /// # Support
226 /// All Android version.
227 ///
228 /// Note :
229 /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
230 /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].
231 /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
232 /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].
233 /// - Others dirs are available in all Android versions.
234 #[maybe_async]
235 pub fn create_new_file(
236 &self,
237 volume_id: Option<&StorageVolumeId>,
238 base_dir: impl Into<PublicDir>,
239 relative_path: impl AsRef<std::path::Path>,
240 mime_type: Option<&str>
241 ) -> Result<FileUri> {
242
243 #[cfg(not(target_os = "android"))] {
244 Err(Error::NOT_ANDROID)
245 }
246 #[cfg(target_os = "android")] {
247 self.impls().create_new_file_in_public_storage(
248 volume_id,
249 base_dir,
250 relative_path,
251 mime_type,
252 false
253 ).await
254 }
255 }
256
257 /// Creates a new empty file in the specified public directory of the storage volume.
258 /// This returns a **persistent read-write** URI.
259 ///
260 /// The app can read/write it until the app is uninstalled.
261 /// And it is **not** removed when the app itself is uninstalled.
262 ///
263 /// # Note
264 /// ### Android 10 or higher
265 /// Files are automatically registered in the appropriate MediaStore as needed.
266 /// Scanning is triggered when the file descriptor is closed
267 /// or as part of the [`pending`](PublicStorage::set_pending) lifecycle.
268 ///
269 /// Diffrences from [`PublicStorage::create_new_file`] are that
270 /// files are marked as pending and will not be visible to other apps until
271 /// [`PublicStorage::set_pending(..., false)`](PublicStorage::set_pending) is called.
272 ///
273 /// ### Android 9 or lower
274 /// This behavior is equal to [`PublicStorage::create_new_file`].
275 /// So `pending` is ignored.
276 ///
277 /// [`WRITE_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#WRITE_EXTERNAL_STORAGE) and [`READ_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE) permissions are required.
278 /// This needs two steps:
279 ///
280 /// 1. Declare :
281 /// By enabling the `legacy_storage_permission` feature,
282 /// you can declare the permissions only for Android 9 or lower automatically at build time.
283 ///
284 /// 2. Runtime request :
285 /// By calling [`PublicStorage::request_permission`],
286 /// you can request the permissions from the user at runtime.
287 ///
288 /// After writing content to the file, call [`PublicStorage::scan`].
289 /// Until then, the file may not appear in the gallery or other apps.
290 ///
291 /// # Args
292 /// - ***volume_id*** :
293 /// The ID of the storage volume, such as internal storage or an SD card.
294 /// Usually, you don't need to specify this unless there is a special reason.
295 /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.
296 ///
297 /// - ***base_dir*** :
298 /// The base directory for the file.
299 /// When using [`PublicImageDir`], only image MIME types should be used for ***mime_type***; using other types may cause errors.
300 /// Similarly, [`PublicVideoDir`] and [`PublicAudioDir`] should only be used with their respective media types.
301 /// Only [`PublicGeneralPurposeDir`] supports all MIME types.
302 ///
303 /// - ***relative_path*** :
304 /// The file path relative to the base directory.
305 /// To keep files organized, it is recommended to place your app's name directory at the top level.
306 /// Any missing parent directories will be created automatically.
307 /// If a file with the same name already exists, a sequential number is appended to ensure uniqueness.
308 /// If the file has no extension, one may be inferred from ***mime_type*** and appended to the file name.
309 /// Strings may also be sanitized as needed, so they may not be used exactly as provided.
310 /// Note that append-exntesion and sanitize-path operation may vary depending on the device model and Android version.
311 ///
312 /// - ***mime_type*** :
313 /// The MIME type of the file to be created.
314 /// If `None`, the MIME type will be inferred from the extension of ***relative_path***.
315 /// If that also fails, `application/octet-stream` will be used.
316 ///
317 /// # Support
318 /// All Android version.
319 ///
320 /// Note :
321 /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
322 /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].
323 /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
324 /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].
325 /// - Others dirs are available in all Android versions.
326 #[maybe_async]
327 pub fn create_new_file_with_pending(
328 &self,
329 volume_id: Option<&StorageVolumeId>,
330 base_dir: impl Into<PublicDir>,
331 relative_path: impl AsRef<std::path::Path>,
332 mime_type: Option<&str>
333 ) -> Result<FileUri> {
334
335 #[cfg(not(target_os = "android"))] {
336 Err(Error::NOT_ANDROID)
337 }
338 #[cfg(target_os = "android")] {
339 self.impls().create_new_file_in_public_storage(
340 volume_id,
341 base_dir,
342 relative_path,
343 mime_type,
344 true
345 ).await
346 }
347 }
348
349 /// Recursively create a directory and all of its parent components if they are missing.
350 /// If it already exists, do nothing.
351 ///
352 /// [`PublicStorage::create_new_file`] and [`PublicStorage::create_new_file_with_pending`]
353 /// do this automatically, so there is no need to use it together.
354 ///
355 /// # Note
356 /// On Android 9 or lower,
357 /// [`WRITE_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#WRITE_EXTERNAL_STORAGE) and [`READ_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE) permissions are required.
358 /// This needs two steps:
359 ///
360 /// 1. Declare :
361 /// By enabling the `legacy_storage_permission` feature,
362 /// you can declare the permissions only for Android 9 or lower automatically at build time.
363 ///
364 /// 2. Runtime request :
365 /// By calling [`PublicStorage::request_permission`],
366 /// you can request the permissions from the user at runtime.
367 ///
368 /// # Args
369 /// - ***volume_id*** :
370 /// ID of the storage volume, such as internal storage, SD card, etc.
371 /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.
372 ///
373 /// - ***base_dir*** :
374 /// The base directory.
375 ///
376 /// - ***relative_path*** :
377 /// The directory path relative to the base directory.
378 /// Strings may also be sanitized as needed, so they may not be used exactly as provided.
379 /// Note that sanitize-path operation may vary depending on the device model and Android version.
380 ///
381 /// # Support
382 /// All Android Version.
383 ///
384 /// Note :
385 /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
386 /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].
387 /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
388 /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].
389 /// - Others dirs are available in all Android versions.
390 #[maybe_async]
391 pub fn create_dir_all(
392 &self,
393 volume_id: Option<&StorageVolumeId>,
394 base_dir: impl Into<PublicDir>,
395 relative_path: impl AsRef<std::path::Path>,
396 ) -> Result<()> {
397
398 #[cfg(not(target_os = "android"))] {
399 Err(Error::NOT_ANDROID)
400 }
401 #[cfg(target_os = "android")] {
402 self.impls().create_dir_all_in_public_storage(volume_id, base_dir, relative_path).await
403 }
404 }
405
406 /// Writes contents to the new file in the specified public directory of the storage volume.
407 /// This returns a **persistent read-write** URI.
408 ///
409 /// The app can read/write it until the app is uninstalled.
410 /// And it is **not** removed when the app itself is uninstalled.
411 ///
412 /// # Note
413 /// On Android 9 or lower,
414 /// [`WRITE_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#WRITE_EXTERNAL_STORAGE) and [`READ_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE) permissions are required.
415 /// This needs two steps:
416 ///
417 /// 1. Declare :
418 /// By enabling the `legacy_storage_permission` feature,
419 /// you can declare the permissions only for Android 9 or lower automatically at build time.
420 ///
421 /// 2. Runtime request :
422 /// By calling [`PublicStorage::request_permission`],
423 /// you can request the permissions from the user at runtime.
424 ///
425 /// # Args
426 /// - ***volume_id*** :
427 /// The ID of the storage volume, such as internal storage or an SD card.
428 /// Usually, you don't need to specify this unless there is a special reason.
429 /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.
430 ///
431 /// - ***base_dir*** :
432 /// The base directory for the file.
433 /// When using [`PublicImageDir`], only image MIME types should be used for ***mime_type***; using other types may cause errors.
434 /// Similarly, [`PublicVideoDir`] and [`PublicAudioDir`] should only be used with their respective media types.
435 /// Only [`PublicGeneralPurposeDir`] supports all MIME types.
436 ///
437 /// - ***relative_path*** :
438 /// The file path relative to the base directory.
439 /// To keep files organized, it is recommended to place your app's name directory at the top level.
440 /// Any missing parent directories will be created automatically.
441 /// If a file with the same name already exists, a sequential number is appended to ensure uniqueness.
442 /// If the file has no extension, one may be inferred from ***mime_type*** and appended to the file name.
443 /// Strings may also be sanitized as needed, so they may not be used exactly as provided.
444 /// Note that append-exntesion and sanitize-path operation may vary depending on the device model and Android version.
445 ///
446 /// - ***mime_type*** :
447 /// The MIME type of the file to be created.
448 /// If `None`, the MIME type will be inferred from the extension of ***relative_path***.
449 /// If that also fails, `application/octet-stream` will be used.
450 ///
451 /// - ***contents*** :
452 /// Contents.
453 ///
454 /// # Support
455 /// All Android version.
456 ///
457 /// Note :
458 /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
459 /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].
460 /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
461 /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].
462 /// - Others dirs are available in all Android versions.
463 #[maybe_async]
464 pub fn write_new(
465 &self,
466 volume_id: Option<&StorageVolumeId>,
467 base_dir: impl Into<PublicDir>,
468 relative_path: impl AsRef<std::path::Path>,
469 mime_type: Option<&str>,
470 contents: impl AsRef<[u8]>
471 ) -> Result<FileUri> {
472
473 #[cfg(not(target_os = "android"))] {
474 Err(Error::NOT_ANDROID)
475 }
476 #[cfg(target_os = "android")] {
477 self.impls().write_new_file_in_public_storage(volume_id, base_dir, relative_path, mime_type, contents).await
478 }
479 }
480
481 /// Scans the specified file in MediaStore.
482 /// By doing this, the file will be visible with corrent metadata in the Gallery and etc.
483 ///
484 /// You don’t need to call this after [`PublicStorage::write_new`].
485 ///
486 /// # Version behavior
487 /// ### Android 10 or higher
488 /// This function does nothing,
489 /// because files are automatically registered in the appropriate MediaStore as needed.
490 /// Scanning is triggered when the file descriptor is closed
491 /// or as part of the [`pending`](PublicStorage::set_pending) lifecycle.
492 ///
493 /// If you really need to perform this operation in this version,
494 /// please use [`PublicStorage::_scan`].
495 ///
496 /// ### Android 9 or lower
497 /// Requests the specified file to be scanned by MediaStore.
498 /// This function returns when the scan request has been initiated.
499 ///
500 /// # Args
501 /// - ***uri*** :
502 /// The target file URI.
503 /// This must be a URI obtained from one of the following:
504 /// - [`PublicStorage::write_new`]
505 /// - [`PublicStorage::create_new_file`]
506 /// - [`PublicStorage::create_new_file_with_pending`]
507 /// - [`PublicStorage::scan_by_path`]
508 ///
509 /// # Support
510 /// All Android versions.
511 #[maybe_async]
512 pub fn scan(
513 &self,
514 uri: &FileUri,
515 ) -> Result<()> {
516
517 #[cfg(not(target_os = "android"))] {
518 Err(Error::NOT_ANDROID)
519 }
520 #[cfg(target_os = "android")] {
521 self.impls().scan_file_in_public_storage(uri, false).await
522 }
523 }
524
525 /// Scans the specified file in MediaStore and returns it's URI if success.
526 /// By doing this, the file will be visible in the Gallery and etc.
527 ///
528 /// # Note
529 /// Unlike [`PublicStorage::scan`],
530 /// this function waits until the scan is complete and then returns either success or an error.
531 ///
532 /// # Args
533 /// - ***uri*** :
534 /// Absolute path of the target file.
535 /// This must be a path obtained from one of the following:
536 /// - [`PublicStorage::resolve_path`] and it's descendants path.
537 /// - [`PublicStorage::get_path`]
538 ///
539 /// - ***mime_type*** :
540 /// The MIME type of the file.
541 /// If `None`, the MIME type will be inferred from the extension of the path.
542 /// If that also fails, `application/octet-stream` will be used.
543 ///
544 /// # Support
545 /// All Android version.
546 #[maybe_async]
547 pub fn scan_by_path(
548 &self,
549 path: impl AsRef<std::path::Path>,
550 mime_type: Option<&str>
551 ) -> Result<FileUri> {
552
553 #[cfg(not(target_os = "android"))] {
554 Err(Error::NOT_ANDROID)
555 }
556 #[cfg(target_os = "android")] {
557 self.impls().scan_file_by_path_in_public_storage(path, mime_type).await
558 }
559 }
560
561 /// Specifies whether the specified file on PublicStorage is marked as pending.
562 /// When set to `true`, the app has exclusive access to the file, and it becomes invisible to other apps.
563 ///
564 /// If it remains `true` for more than seven days,
565 /// the system will automatically delete the file.
566 ///
567 /// # Note
568 /// This is available for Android 10 or higher.
569 /// On Android 9 or lower, this does nothing.
570 ///
571 /// # Args
572 /// - ***uri*** :
573 /// Target file URI.
574 /// This must be a URI obtained from one of the following:
575 /// - [`PublicStorage::write_new`]
576 /// - [`PublicStorage::create_new_file`]
577 /// - [`PublicStorage::create_new_file_with_pending`]
578 /// - [`PublicStorage::scan_by_path`]
579 ///
580 /// # Support
581 /// All Android version.
582 ///
583 /// # References
584 /// - <https://developer.android.com/reference/android/provider/MediaStore.MediaColumns#IS_PENDING>
585 /// - <https://developer.android.com/training/data-storage/shared/media?hl=en#toggle-pending-status>
586 #[maybe_async]
587 pub fn set_pending(&self, uri: &FileUri, is_pending: bool) -> Result<()> {
588 #[cfg(not(target_os = "android"))] {
589 Err(Error::NOT_ANDROID)
590 }
591 #[cfg(target_os = "android")] {
592 self.impls().set_file_pending_in_public_storage(uri, is_pending).await
593 }
594 }
595
596 /// Gets the absolute path of the specified file.
597 ///
598 /// # Note
599 /// For description and notes on path permissions and handling,
600 /// see [`PublicStorage::resolve_path`].
601 /// It involves important constraints and required settings.
602 /// Therefore, **operate files via paths only when it is truly necessary**.
603 ///
604 /// # Args
605 /// - ***uri*** :
606 /// Target file URI.
607 /// This must be a URI obtained from one of the following:
608 /// - [`PublicStorage::write_new`]
609 /// - [`PublicStorage::create_new_file`]
610 /// - [`PublicStorage::create_new_file_with_pending`]
611 /// - [`PublicStorage::scan_by_path`]
612 ///
613 /// # Support
614 /// All Android version.
615 #[deprecated = "File operations via paths may result in unstable behaviour and inconsistent outcomes."]
616 #[maybe_async]
617 pub fn get_path(
618 &self,
619 uri: &FileUri,
620 ) -> Result<std::path::PathBuf> {
621
622 #[cfg(not(target_os = "android"))] {
623 Err(Error::NOT_ANDROID)
624 }
625 #[cfg(target_os = "android")] {
626 self.impls().get_file_path_in_public_storage(uri).await
627 }
628 }
629
630 /// Retrieves the absolute path for a specified public directory within the given storage volume.
631 /// This function does **not** create any directories; it only constructs the path.
632 ///
633 /// The app is allowed to create files in the directory and read/write the files it creates.
634 /// Access to files created by other apps is not guaranteed.
635 /// Additionally, after the app is uninstalled and reinstalled,
636 /// previously created files may become inaccessible.
637 ///
638 /// The directory is **not** removed when the app itself is uninstalled.
639 ///
640 /// It is strongly recommended to call [`PublicStorage::scan_by_path`]
641 /// after writing to the file to request registration in MediaStore.
642 ///
643 /// # Note
644 /// As shown below, this involves important constraints and required settings.
645 /// Therefore, **operate files via paths only when it is truly necessary**.
646 ///
647 /// Do not use operations such as rename or remove that rely on paths
648 /// (including URIs obtained via [`FileUri::from_path`] with this paths),
649 /// as they may break consistency with the MediaStore on old version.
650 /// Instead, use the URI obtained through [`PublicStorage::scan_by_path`] together with methods
651 /// such as [`AndroidFs::rename`] or [`AndroidFs::remove_file`].
652 ///
653 /// ### Android 11 or higher
654 /// When using [`PublicImageDir`], use only image type for file name extension,
655 /// using other type extension or none may cause errors.
656 /// Similarly, use only the corresponding extesions for [`PublicVideoDir`] and [`PublicAudioDir`].
657 /// Only [`PublicGeneralPurposeDir`] supports all extensions and no extension.
658 ///
659 /// ### Android 10 or lower
660 /// [`WRITE_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#WRITE_EXTERNAL_STORAGE) and [`READ_EXTERNAL_STORAGE`](https://developer.android.com/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE) permissions are required.
661 /// This needs two steps:
662 ///
663 /// 1. Declare :
664 /// By enabling the `legacy_storage_permission_include_android_10` feature,
665 /// you can declare the permissions only for Android 10 or lower automatically at build time.
666 ///
667 /// 2. Runtime request :
668 /// By calling [`PublicStorage::request_permission`],
669 /// you can request the permissions from the user at runtime.
670 ///
671 /// ### Android 10
672 /// Files within PublicStorage cannot be accessed via file paths,
673 /// so it is necessary to declare that the app need access `PublicStorage` using the method of Android 9 or lower.
674 ///
675 /// For it, please [set `android:requestLegacyExternalStorage="true"`](https://developer.android.com/training/data-storage/use-cases#opt-out-in-production-app).
676 ///
677 /// `src-tauri/gen/android/app/src/main/AndroidManifest.xml`
678 /// ```xml
679 /// <manifest ... >
680 /// <application
681 /// android:requestLegacyExternalStorage="true"
682 /// ...
683 /// >
684 /// ...
685 /// </application>
686 /// </manifest>
687 /// ```
688 /// And it is not possible to access `PublicStorage`
689 /// on volumes other than the primary storage via paths, just like on Android 9 or lower.
690 /// But filtering using [`PublicStorage::get_volumes`]
691 /// or [`StorageVolume::is_available_for_public_storage`] may not work correctly,
692 /// as these are intended for access via URIs.
693 ///
694 /// # Args
695 /// - ***volume_id*** :
696 /// ID of the storage volume, such as internal storage, SD card, etc.
697 /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.
698 ///
699 /// - ***base_dir*** :
700 /// The base directory.
701 /// One of [`PublicImageDir`], [`PublicVideoDir`], [`PublicAudioDir`], [`PublicGeneralPurposeDir`].
702 ///
703 /// # Support
704 /// All Android version.
705 ///
706 /// Note :
707 /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
708 /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].
709 /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
710 /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].
711 /// - Others dirs are available in all Android versions.
712 #[deprecated = "File operations via paths may result in unstable behaviour and inconsistent outcomes."]
713 #[maybe_async]
714 pub fn resolve_path(
715 &self,
716 volume_id: Option<&StorageVolumeId>,
717 base_dir: impl Into<PublicDir>,
718 ) -> Result<std::path::PathBuf> {
719
720 #[cfg(not(target_os = "android"))] {
721 Err(Error::NOT_ANDROID)
722 }
723 #[cfg(target_os = "android")] {
724 self.impls().resolve_dir_path_in_public_storage(volume_id, base_dir).await
725 }
726 }
727
728 /// Builds the specified directory URI.
729 ///
730 /// This should only be used as `initial_location` in the file picker, such as [`FilePicker::pick_files`].
731 /// It must not be used for any other purpose.
732 ///
733 /// This is useful when selecting save location,
734 /// but when selecting existing entries, `initial_location` is often better with None.
735 ///
736 /// # Args
737 /// - ***volume_id*** :
738 /// ID of the storage volume, such as internal storage, SD card, etc.
739 /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.
740 ///
741 /// - ***base_dir*** :
742 /// The base directory.
743 ///
744 /// - ***relative_path*** :
745 /// The directory path relative to the base directory.
746 ///
747 /// - ***create_dir_all*** :
748 /// Creates directories if missing.
749 /// See [`PublicStorage::create_dir_all`].
750 /// If error occurs, it will be ignored.
751 ///
752 /// # Support
753 /// All Android version.
754 ///
755 /// Note :
756 /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
757 /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].
758 /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
759 /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].
760 /// - Others dirs are available in all Android versions.
761 #[maybe_async]
762 pub fn resolve_initial_location(
763 &self,
764 volume_id: Option<&StorageVolumeId>,
765 base_dir: impl Into<PublicDir>,
766 relative_path: impl AsRef<std::path::Path>,
767 create_dir_all: bool
768 ) -> Result<FileUri> {
769
770 #[cfg(not(target_os = "android"))] {
771 Err(Error::NOT_ANDROID)
772 }
773 #[cfg(target_os = "android")] {
774 self.impls().resolve_initial_location_in_public_storage(volume_id, base_dir, relative_path, create_dir_all).await
775 }
776 }
777
778 /// Verify whether [`PublicAudioDir::Audiobooks`] is available on a given device.
779 ///
780 /// If on Android 10 (API level 29) or higher, this returns true.
781 /// If on Android 9 (API level 28) and lower, this returns false.
782 ///
783 /// # Support
784 /// All Android version.
785 #[always_sync]
786 pub fn is_audiobooks_dir_available(&self) -> Result<bool> {
787 #[cfg(not(target_os = "android"))] {
788 Err(Error::NOT_ANDROID)
789 }
790 #[cfg(target_os = "android")] {
791 Ok(self.impls().consts()?.env_dir_audiobooks.is_some())
792 }
793 }
794
795 /// Verify whether [`PublicAudioDir::Recordings`] is available on a given device.
796 ///
797 /// If on Android 12 (API level 31) or higher, this returns true.
798 /// If on Android 11 (API level 30) and lower, this returns false.
799 ///
800 /// # Support
801 /// All Android version.
802 #[always_sync]
803 pub fn is_recordings_dir_available(&self) -> Result<bool> {
804 #[cfg(not(target_os = "android"))] {
805 Err(Error::NOT_ANDROID)
806 }
807 #[cfg(target_os = "android")] {
808 Ok(self.impls().consts()?.env_dir_recordings.is_some())
809 }
810 }
811
812 /// For details, see [`PublicStorage::scan`].
813 ///
814 /// The difference is that this method scans the file even on Android 10 or higher.
815 ///
816 /// On Android 10 or higher, files are automatically registered in the appropriate MediaStore as needed.
817 /// Scanning is triggered when the file descriptor is closed
818 /// or as part of the [`pending`](PublicStorage::set_pending) lifecycle.
819 /// Therefore, please consider carefully whether this is truly necessary.
820 #[maybe_async]
821 pub fn _scan(
822 &self,
823 uri: &FileUri,
824 ) -> Result<()> {
825
826 #[cfg(not(target_os = "android"))] {
827 Err(Error::NOT_ANDROID)
828 }
829 #[cfg(target_os = "android")] {
830 self.impls().scan_file_in_public_storage(uri, true).await
831 }
832 }
833
834 /// For details, see [`PublicStorage::_scan`].
835 ///
836 /// The difference is that this function waits until the scan is complete and then returns either success or an error.
837 #[maybe_async]
838 pub fn _scan_for_result(
839 &self,
840 uri: &FileUri,
841 ) -> Result<()> {
842
843 #[cfg(not(target_os = "android"))] {
844 Err(Error::NOT_ANDROID)
845 }
846 #[cfg(target_os = "android")] {
847 self.impls().scan_file_in_public_storage_for_result(uri, true).await
848 }
849 }
850}