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