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, WritableStream};
42 use(if_sync) api_sync::{AndroidFs, FileOpener, FilePicker, AppStorage, PrivateStorage, WritableStream};
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 /// For details, see [`PublicStorage::scan`].
526 ///
527 /// The difference is that this method scans the file even on Android 10 or higher.
528 ///
529 /// On Android 10 or higher, files are automatically registered in the appropriate MediaStore as needed.
530 /// Scanning is triggered when the file descriptor is closed
531 /// or as part of the [`pending`](PublicStorage::set_pending) lifecycle.
532 /// Therefore, please consider carefully whether this is truly necessary.
533 #[maybe_async]
534 pub fn _scan(
535 &self,
536 uri: &FileUri,
537 ) -> Result<()> {
538
539 #[cfg(not(target_os = "android"))] {
540 Err(Error::NOT_ANDROID)
541 }
542 #[cfg(target_os = "android")] {
543 self.impls().scan_file_in_public_storage(uri, true).await
544 }
545 }
546
547 /// For details, see [`PublicStorage::_scan`].
548 ///
549 /// The difference is that this function waits until the scan is complete and then returns either success or an error.
550 #[maybe_async]
551 pub fn _scan_for_result(
552 &self,
553 uri: &FileUri,
554 ) -> Result<()> {
555
556 #[cfg(not(target_os = "android"))] {
557 Err(Error::NOT_ANDROID)
558 }
559 #[cfg(target_os = "android")] {
560 self.impls().scan_file_in_public_storage_for_result(uri, true).await
561 }
562 }
563
564 /// Scans the specified file in MediaStore and returns it's URI if success.
565 /// By doing this, the file will be visible in the Gallery and etc.
566 ///
567 /// # Note
568 /// Unlike [`PublicStorage::scan`],
569 /// this function waits until the scan is complete and then returns either success or an error.
570 ///
571 /// # Args
572 /// - ***uri*** :
573 /// Absolute path of the target file.
574 /// This must be a path obtained from one of the following:
575 /// - [`PublicStorage::resolve_path`] and it's descendants path.
576 /// - [`PublicStorage::get_path`]
577 ///
578 /// - ***mime_type*** :
579 /// The MIME type of the file.
580 /// If `None`, the MIME type will be inferred from the extension of the path.
581 /// If that also fails, `application/octet-stream` will be used.
582 ///
583 /// # Support
584 /// All Android version.
585 #[maybe_async]
586 pub fn scan_by_path(
587 &self,
588 path: impl AsRef<std::path::Path>,
589 mime_type: Option<&str>
590 ) -> Result<FileUri> {
591
592 #[cfg(not(target_os = "android"))] {
593 Err(Error::NOT_ANDROID)
594 }
595 #[cfg(target_os = "android")] {
596 self.impls().scan_file_by_path_in_public_storage(path, mime_type).await
597 }
598 }
599
600 /// Specifies whether the specified file on PublicStorage is marked as pending.
601 /// When set to `true`, the app has exclusive access to the file, and it becomes invisible to other apps.
602 ///
603 /// If it remains `true` for more than seven days,
604 /// the system will automatically delete the file.
605 ///
606 /// # Note
607 /// This is available for Android 10 or higher.
608 /// On Android 9 or lower, this does nothing.
609 ///
610 /// # Args
611 /// - ***uri*** :
612 /// Target file URI.
613 /// This must be a URI obtained from one of the following:
614 /// - [`PublicStorage::write_new`]
615 /// - [`PublicStorage::create_new_file`]
616 /// - [`PublicStorage::create_new_file_with_pending`]
617 /// - [`PublicStorage::scan_by_path`]
618 ///
619 /// # Support
620 /// All Android version.
621 ///
622 /// # References
623 /// - <https://developer.android.com/reference/android/provider/MediaStore.MediaColumns#IS_PENDING>
624 /// - <https://developer.android.com/training/data-storage/shared/media?hl=en#toggle-pending-status>
625 #[maybe_async]
626 pub fn set_pending(&self, uri: &FileUri, is_pending: bool) -> Result<()> {
627 #[cfg(not(target_os = "android"))] {
628 Err(Error::NOT_ANDROID)
629 }
630 #[cfg(target_os = "android")] {
631 self.impls().set_file_pending_in_public_storage(uri, is_pending).await
632 }
633 }
634
635 /// Gets the absolute path of the specified file.
636 ///
637 /// # Note
638 /// For description and notes on path permissions and handling,
639 /// see [`PublicStorage::resolve_path`].
640 /// It involves important constraints and required settings.
641 /// Therefore, **operate files via paths only when it is truly necessary**.
642 ///
643 /// # Args
644 /// - ***uri*** :
645 /// Target file URI.
646 /// This must be a URI obtained from one of the following:
647 /// - [`PublicStorage::write_new`]
648 /// - [`PublicStorage::create_new_file`]
649 /// - [`PublicStorage::create_new_file_with_pending`]
650 /// - [`PublicStorage::scan_by_path`]
651 ///
652 /// # Support
653 /// All Android version.
654 #[deprecated = "File operations via paths may result in unstable behaviour and inconsistent outcomes."]
655 #[maybe_async]
656 pub fn get_path(
657 &self,
658 uri: &FileUri,
659 ) -> Result<std::path::PathBuf> {
660
661 #[cfg(not(target_os = "android"))] {
662 Err(Error::NOT_ANDROID)
663 }
664 #[cfg(target_os = "android")] {
665 self.impls().get_file_path_in_public_storage(uri).await
666 }
667 }
668
669 /// Retrieves the absolute path for a specified public directory within the given storage volume.
670 /// This function does **not** create any directories; it only constructs the path.
671 ///
672 /// The app is allowed to create files in the directory and read/write the files it creates.
673 /// Access to files created by other apps is not guaranteed.
674 /// Additionally, after the app is uninstalled and reinstalled,
675 /// previously created files may become inaccessible.
676 ///
677 /// The directory is **not** removed when the app itself is uninstalled.
678 ///
679 /// It is strongly recommended to call [`PublicStorage::scan_by_path`]
680 /// after writing to the file to request registration in MediaStore.
681 ///
682 /// # Note
683 /// As shown below, this involves important constraints and required settings.
684 /// Therefore, **operate files via paths only when it is truly necessary**.
685 ///
686 /// Do not use operations such as rename or remove that rely on paths
687 /// (including URIs obtained via [`FileUri::from_path`] with this paths),
688 /// as they may break consistency with the MediaStore on old version.
689 /// Instead, use the URI obtained through [`PublicStorage::scan_by_path`] together with methods
690 /// such as [`AndroidFs::rename`] or [`AndroidFs::remove_file`].
691 ///
692 /// ### Android 11 or higher
693 /// No permission is required.
694 /// When using [`PublicImageDir`], use only image type for file name extension,
695 /// using other type extension or none may cause errors.
696 /// Similarly, use only the corresponding extesions for [`PublicVideoDir`] and [`PublicAudioDir`].
697 /// Only [`PublicGeneralPurposeDir`] supports all extensions and no extension.
698 ///
699 /// ### Android 10 or lower
700 /// [`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.
701 /// This needs two steps:
702 ///
703 /// 1. Declare :
704 /// By enabling the `legacy_storage_permission_include_android_10` feature,
705 /// you can declare the permissions only for Android 10 or lower automatically at build time.
706 ///
707 /// 2. Runtime request :
708 /// By calling [`PublicStorage::request_permission`],
709 /// you can request the permissions from the user at runtime.
710 ///
711 /// ### Android 10
712 /// Files within PublicStorage cannot be accessed via file paths,
713 /// so it is necessary to declare that the app need access `PublicStorage` using the method of Android 9 or lower.
714 ///
715 /// For it, please [set `android:requestLegacyExternalStorage="true"`](https://developer.android.com/training/data-storage/use-cases#opt-out-in-production-app).
716 ///
717 /// `src-tauri/gen/android/app/src/main/AndroidManifest.xml`
718 /// ```xml
719 /// <manifest ... >
720 /// <application
721 /// android:requestLegacyExternalStorage="true"
722 /// ...
723 /// >
724 /// ...
725 /// </application>
726 /// </manifest>
727 /// ```
728 /// And it is not possible to access `PublicStorage`
729 /// on volumes other than the primary storage via paths, just like on Android 9 or lower.
730 /// But filtering using [`PublicStorage::get_volumes`]
731 /// or [`StorageVolume::is_available_for_public_storage`] may not work correctly,
732 /// as these are intended for access via URIs.
733 ///
734 /// # Args
735 /// - ***volume_id*** :
736 /// ID of the storage volume, such as internal storage, SD card, etc.
737 /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.
738 ///
739 /// - ***base_dir*** :
740 /// The base directory.
741 /// One of [`PublicImageDir`], [`PublicVideoDir`], [`PublicAudioDir`], [`PublicGeneralPurposeDir`].
742 ///
743 /// # Support
744 /// All Android version.
745 ///
746 /// Note :
747 /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
748 /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].
749 /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
750 /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].
751 /// - Others dirs are available in all Android versions.
752 #[deprecated = "File operations via paths may result in unstable behaviour and inconsistent outcomes."]
753 #[maybe_async]
754 pub fn resolve_path(
755 &self,
756 volume_id: Option<&StorageVolumeId>,
757 base_dir: impl Into<PublicDir>,
758 ) -> Result<std::path::PathBuf> {
759
760 #[cfg(not(target_os = "android"))] {
761 Err(Error::NOT_ANDROID)
762 }
763 #[cfg(target_os = "android")] {
764 self.impls().resolve_dir_path_in_public_storage(volume_id, base_dir).await
765 }
766 }
767
768 /// Builds the specified directory URI.
769 ///
770 /// This should only be used as `initial_location` in the file picker, such as [`FilePicker::pick_files`].
771 /// It must not be used for any other purpose.
772 ///
773 /// This is useful when selecting save location,
774 /// but when selecting existing entries, `initial_location` is often better with None.
775 ///
776 /// # Args
777 /// - ***volume_id*** :
778 /// ID of the storage volume, such as internal storage, SD card, etc.
779 /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.
780 ///
781 /// - ***base_dir*** :
782 /// The base directory.
783 ///
784 /// - ***relative_path*** :
785 /// The directory path relative to the base directory.
786 ///
787 /// - ***create_dir_all*** :
788 /// Creates directories if missing.
789 /// See [`PublicStorage::create_dir_all`].
790 /// If error occurs, it will be ignored.
791 ///
792 /// # Support
793 /// All Android version.
794 ///
795 /// Note :
796 /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
797 /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].
798 /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
799 /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].
800 /// - Others dirs are available in all Android versions.
801 #[maybe_async]
802 pub fn resolve_initial_location(
803 &self,
804 volume_id: Option<&StorageVolumeId>,
805 base_dir: impl Into<PublicDir>,
806 relative_path: impl AsRef<std::path::Path>,
807 create_dir_all: bool
808 ) -> Result<FileUri> {
809
810 #[cfg(not(target_os = "android"))] {
811 Err(Error::NOT_ANDROID)
812 }
813 #[cfg(target_os = "android")] {
814 self.impls().resolve_initial_location_in_public_storage(volume_id, base_dir, relative_path, create_dir_all).await
815 }
816 }
817
818 /// Verify whether [`PublicAudioDir::Audiobooks`] is available on a given device.
819 ///
820 /// If on Android 10 (API level 29) or higher, this returns true.
821 /// If on Android 9 (API level 28) and lower, this returns false.
822 ///
823 /// # Support
824 /// All Android version.
825 #[always_sync]
826 pub fn is_audiobooks_dir_available(&self) -> Result<bool> {
827 #[cfg(not(target_os = "android"))] {
828 Err(Error::NOT_ANDROID)
829 }
830 #[cfg(target_os = "android")] {
831 Ok(self.impls().consts()?.env_dir_audiobooks.is_some())
832 }
833 }
834
835 /// Verify whether [`PublicAudioDir::Recordings`] is available on a given device.
836 ///
837 /// If on Android 12 (API level 31) or higher, this returns true.
838 /// If on Android 11 (API level 30) and lower, this returns false.
839 ///
840 /// # Support
841 /// All Android version.
842 #[always_sync]
843 pub fn is_recordings_dir_available(&self) -> Result<bool> {
844 #[cfg(not(target_os = "android"))] {
845 Err(Error::NOT_ANDROID)
846 }
847 #[cfg(target_os = "android")] {
848 Ok(self.impls().consts()?.env_dir_recordings.is_some())
849 }
850 }
851}