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 /// # Version behavior
125 /// For Android 9 (API level 28) or lower,
126 /// this does not include any storage volumes other than the primary one.
127 ///
128 /// # Note
129 /// The volume represents the logical view of a storage volume for an individual user:
130 /// each user may have a different view for the same physical volume.
131 /// In other words, it provides a separate area for each user in a multi-user environment.
132 ///
133 /// # Support
134 /// All Android version.
135 #[maybe_async]
136 pub fn get_volumes(&self) -> Result<Vec<StorageVolume>> {
137 #[cfg(not(target_os = "android"))] {
138 Err(Error::NOT_ANDROID)
139 }
140 #[cfg(target_os = "android")] {
141 self.impls().get_available_storage_volumes_for_public_storage().await
142 }
143 }
144
145 /// Gets a primary storage volume.
146 /// This is the most common and recommended storage volume for placing files that can be accessed by other apps or user.
147 /// In many cases, it is device's built-in storage.
148 ///
149 /// A device always has one (and one only) primary storage volume.
150 ///
151 /// Primary volume may not currently be accessible
152 /// if it has been mounted by the user on their computer,
153 /// has been removed from the device, or some other problem has happened.
154 /// If so, this returns `None`.
155 ///
156 /// # Note
157 /// The volume represents the logical view of a storage volume for an individual user:
158 /// each user may have a different view for the same physical volume.
159 /// In other words, it provides a separate area for each user in a multi-user environment.
160 ///
161 /// # Support
162 /// All Android version.
163 #[maybe_async]
164 pub fn get_primary_volume(&self) -> Result<Option<StorageVolume>> {
165 #[cfg(not(target_os = "android"))] {
166 Err(Error::NOT_ANDROID)
167 }
168 #[cfg(target_os = "android")] {
169 self.impls().get_primary_storage_volume_if_available_for_public_storage().await
170 }
171 }
172
173 /// Creates a new empty file in the specified public directory of the storage volume.
174 /// This returns a **persistent read-write** URI.
175 ///
176 /// The app can read/write it until the app is uninstalled.
177 /// And it is **not** removed when the app itself is uninstalled.
178 ///
179 /// # Note
180 /// ### Android 10 or higher.
181 /// Files are automatically registered in the appropriate MediaStore as needed.
182 /// Scanning is triggered when the file descriptor is closed
183 /// or as part of the [`pending`](PublicStorage::set_pending) lifecycle.
184 ///
185 /// ### Android 9 or lower
186 /// [`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.
187 /// This needs two steps:
188 ///
189 /// 1. Declare :
190 /// By enabling the `legacy_storage_permission` feature,
191 /// you can declare the permissions only for Android 9 or lower automatically at build time.
192 ///
193 /// 2. Runtime request :
194 /// By calling [`PublicStorage::request_permission`],
195 /// you can request the permissions from the user at runtime.
196 ///
197 /// After writing content to the file, call [`PublicStorage::scan`].
198 /// Until then, the file may not appear in the gallery or other apps.
199 ///
200 /// # Args
201 /// - ***volume_id*** :
202 /// The ID of the storage volume, such as internal storage or an SD card.
203 /// Usually, you don't need to specify this unless there is a special reason.
204 /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.
205 ///
206 /// - ***base_dir*** :
207 /// The base directory for the file.
208 /// When using [`PublicImageDir`], only image MIME types should be used for ***mime_type***; using other types may cause errors.
209 /// Similarly, [`PublicVideoDir`] and [`PublicAudioDir`] should only be used with their respective media types.
210 /// Only [`PublicGeneralPurposeDir`] supports all MIME types.
211 ///
212 /// - ***relative_path*** :
213 /// The file path relative to the base directory.
214 /// To keep files organized, it is recommended to place your app's name directory at the top level.
215 /// Any missing parent directories will be created automatically.
216 /// If a file with the same name already exists, a sequential number is appended to ensure uniqueness.
217 /// If the file has no extension, one may be inferred from ***mime_type*** and appended to the file name.
218 /// Strings may also be sanitized as needed, so they may not be used exactly as provided.
219 /// Note that append-exntesion and sanitize-path operation may vary depending on the device model and Android version.
220 ///
221 /// - ***mime_type*** :
222 /// The MIME type of the file to be created.
223 /// If `None`, the MIME type will be inferred from the extension of ***relative_path***.
224 /// If that also fails, `application/octet-stream` will be used.
225 ///
226 /// # Support
227 /// All Android version.
228 ///
229 /// Note :
230 /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
231 /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].
232 /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
233 /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].
234 /// - Others dirs are available in all Android versions.
235 #[maybe_async]
236 pub fn create_new_file(
237 &self,
238 volume_id: Option<&StorageVolumeId>,
239 base_dir: impl Into<PublicDir>,
240 relative_path: impl AsRef<std::path::Path>,
241 mime_type: Option<&str>
242 ) -> Result<FileUri> {
243
244 #[cfg(not(target_os = "android"))] {
245 Err(Error::NOT_ANDROID)
246 }
247 #[cfg(target_os = "android")] {
248 self.impls().create_new_file_in_public_storage(
249 volume_id,
250 base_dir,
251 relative_path,
252 mime_type,
253 false
254 ).await
255 }
256 }
257
258 /// Creates a new empty file in the specified public directory of the storage volume.
259 /// This returns a **persistent read-write** URI.
260 ///
261 /// The app can read/write it until the app is uninstalled.
262 /// And it is **not** removed when the app itself is uninstalled.
263 ///
264 /// # Note
265 /// ### Android 10 or higher
266 /// Files are automatically registered in the appropriate MediaStore as needed.
267 /// Scanning is triggered when the file descriptor is closed
268 /// or as part of the [`pending`](PublicStorage::set_pending) lifecycle.
269 ///
270 /// Diffrences from [`PublicStorage::create_new_file`] are that
271 /// files are marked as pending and will not be visible to other apps until
272 /// [`PublicStorage::set_pending(..., false)`](PublicStorage::set_pending) is called.
273 ///
274 /// ### Android 9 or lower
275 /// This behavior is equal to [`PublicStorage::create_new_file`].
276 /// So `pending` is ignored.
277 ///
278 /// [`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.
279 /// This needs two steps:
280 ///
281 /// 1. Declare :
282 /// By enabling the `legacy_storage_permission` feature,
283 /// you can declare the permissions only for Android 9 or lower automatically at build time.
284 ///
285 /// 2. Runtime request :
286 /// By calling [`PublicStorage::request_permission`],
287 /// you can request the permissions from the user at runtime.
288 ///
289 /// After writing content to the file, call [`PublicStorage::scan`].
290 /// Until then, the file may not appear in the gallery or other apps.
291 ///
292 /// # Args
293 /// - ***volume_id*** :
294 /// The ID of the storage volume, such as internal storage or an SD card.
295 /// Usually, you don't need to specify this unless there is a special reason.
296 /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.
297 ///
298 /// - ***base_dir*** :
299 /// The base directory for the file.
300 /// When using [`PublicImageDir`], only image MIME types should be used for ***mime_type***; using other types may cause errors.
301 /// Similarly, [`PublicVideoDir`] and [`PublicAudioDir`] should only be used with their respective media types.
302 /// Only [`PublicGeneralPurposeDir`] supports all MIME types.
303 ///
304 /// - ***relative_path*** :
305 /// The file path relative to the base directory.
306 /// To keep files organized, it is recommended to place your app's name directory at the top level.
307 /// Any missing parent directories will be created automatically.
308 /// If a file with the same name already exists, a sequential number is appended to ensure uniqueness.
309 /// If the file has no extension, one may be inferred from ***mime_type*** and appended to the file name.
310 /// Strings may also be sanitized as needed, so they may not be used exactly as provided.
311 /// Note that append-exntesion and sanitize-path operation may vary depending on the device model and Android version.
312 ///
313 /// - ***mime_type*** :
314 /// The MIME type of the file to be created.
315 /// If `None`, the MIME type will be inferred from the extension of ***relative_path***.
316 /// If that also fails, `application/octet-stream` will be used.
317 ///
318 /// # Support
319 /// All Android version.
320 ///
321 /// Note :
322 /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
323 /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].
324 /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
325 /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].
326 /// - Others dirs are available in all Android versions.
327 #[maybe_async]
328 pub fn create_new_file_with_pending(
329 &self,
330 volume_id: Option<&StorageVolumeId>,
331 base_dir: impl Into<PublicDir>,
332 relative_path: impl AsRef<std::path::Path>,
333 mime_type: Option<&str>
334 ) -> Result<FileUri> {
335
336 #[cfg(not(target_os = "android"))] {
337 Err(Error::NOT_ANDROID)
338 }
339 #[cfg(target_os = "android")] {
340 self.impls().create_new_file_in_public_storage(
341 volume_id,
342 base_dir,
343 relative_path,
344 mime_type,
345 true
346 ).await
347 }
348 }
349
350 /// Recursively create a directory and all of its parent components if they are missing.
351 /// If it already exists, do nothing.
352 ///
353 /// [`PublicStorage::create_new_file`] and [`PublicStorage::create_new_file_with_pending`]
354 /// do this automatically, so there is no need to use it together.
355 ///
356 /// # Note
357 /// On Android 9 or lower,
358 /// [`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.
359 /// This needs two steps:
360 ///
361 /// 1. Declare :
362 /// By enabling the `legacy_storage_permission` feature,
363 /// you can declare the permissions only for Android 9 or lower automatically at build time.
364 ///
365 /// 2. Runtime request :
366 /// By calling [`PublicStorage::request_permission`],
367 /// you can request the permissions from the user at runtime.
368 ///
369 /// # Args
370 /// - ***volume_id*** :
371 /// ID of the storage volume, such as internal storage, SD card, etc.
372 /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.
373 ///
374 /// - ***base_dir*** :
375 /// The base directory.
376 ///
377 /// - ***relative_path*** :
378 /// The directory path relative to the base directory.
379 /// Strings may also be sanitized as needed, so they may not be used exactly as provided.
380 /// Note that sanitize-path operation may vary depending on the device model and Android version.
381 ///
382 /// # Support
383 /// All Android Version.
384 ///
385 /// Note :
386 /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
387 /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].
388 /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
389 /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].
390 /// - Others dirs are available in all Android versions.
391 #[maybe_async]
392 pub fn create_dir_all(
393 &self,
394 volume_id: Option<&StorageVolumeId>,
395 base_dir: impl Into<PublicDir>,
396 relative_path: impl AsRef<std::path::Path>,
397 ) -> Result<()> {
398
399 #[cfg(not(target_os = "android"))] {
400 Err(Error::NOT_ANDROID)
401 }
402 #[cfg(target_os = "android")] {
403 self.impls().create_dir_all_in_public_storage(volume_id, base_dir, relative_path).await
404 }
405 }
406
407 /// Writes contents to the new file in the specified public directory of the storage volume.
408 /// This returns a **persistent read-write** URI.
409 ///
410 /// The app can read/write it until the app is uninstalled.
411 /// And it is **not** removed when the app itself is uninstalled.
412 ///
413 /// # Note
414 /// On Android 9 or lower,
415 /// [`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.
416 /// This needs two steps:
417 ///
418 /// 1. Declare :
419 /// By enabling the `legacy_storage_permission` feature,
420 /// you can declare the permissions only for Android 9 or lower automatically at build time.
421 ///
422 /// 2. Runtime request :
423 /// By calling [`PublicStorage::request_permission`],
424 /// you can request the permissions from the user at runtime.
425 ///
426 /// # Args
427 /// - ***volume_id*** :
428 /// The ID of the storage volume, such as internal storage or an SD card.
429 /// Usually, you don't need to specify this unless there is a special reason.
430 /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.
431 ///
432 /// - ***base_dir*** :
433 /// The base directory for the file.
434 /// When using [`PublicImageDir`], only image MIME types should be used for ***mime_type***; using other types may cause errors.
435 /// Similarly, [`PublicVideoDir`] and [`PublicAudioDir`] should only be used with their respective media types.
436 /// Only [`PublicGeneralPurposeDir`] supports all MIME types.
437 ///
438 /// - ***relative_path*** :
439 /// The file path relative to the base directory.
440 /// To keep files organized, it is recommended to place your app's name directory at the top level.
441 /// Any missing parent directories will be created automatically.
442 /// If a file with the same name already exists, a sequential number is appended to ensure uniqueness.
443 /// If the file has no extension, one may be inferred from ***mime_type*** and appended to the file name.
444 /// Strings may also be sanitized as needed, so they may not be used exactly as provided.
445 /// Note that append-exntesion and sanitize-path operation may vary depending on the device model and Android version.
446 ///
447 /// - ***mime_type*** :
448 /// The MIME type of the file to be created.
449 /// If `None`, the MIME type will be inferred from the extension of ***relative_path***.
450 /// If that also fails, `application/octet-stream` will be used.
451 ///
452 /// - ***contents*** :
453 /// Contents.
454 ///
455 /// # Support
456 /// All Android version.
457 ///
458 /// Note :
459 /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
460 /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].
461 /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
462 /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].
463 /// - Others dirs are available in all Android versions.
464 #[maybe_async]
465 pub fn write_new(
466 &self,
467 volume_id: Option<&StorageVolumeId>,
468 base_dir: impl Into<PublicDir>,
469 relative_path: impl AsRef<std::path::Path>,
470 mime_type: Option<&str>,
471 contents: impl AsRef<[u8]>
472 ) -> Result<FileUri> {
473
474 #[cfg(not(target_os = "android"))] {
475 Err(Error::NOT_ANDROID)
476 }
477 #[cfg(target_os = "android")] {
478 self.impls().write_new_file_in_public_storage(volume_id, base_dir, relative_path, mime_type, contents).await
479 }
480 }
481
482 /// Scans the specified file in MediaStore.
483 /// By doing this, the file will be visible with corrent metadata in the Gallery and etc.
484 ///
485 /// You don’t need to call this after [`PublicStorage::write_new`].
486 ///
487 /// # Version behavior
488 /// ### Android 10 or higher
489 /// This function does nothing,
490 /// because files are automatically registered in the appropriate MediaStore as needed.
491 /// Scanning is triggered when the file descriptor is closed
492 /// or as part of the [`pending`](PublicStorage::set_pending) lifecycle.
493 ///
494 /// If you really need to perform this operation in this version,
495 /// please use [`PublicStorage::_scan`].
496 ///
497 /// ### Android 9 or lower
498 /// Requests the specified file to be scanned by MediaStore.
499 /// This function returns when the scan request has been initiated.
500 ///
501 /// # Args
502 /// - ***uri*** :
503 /// The target file URI.
504 /// This must be a URI obtained from one of the following:
505 /// - [`PublicStorage::write_new`]
506 /// - [`PublicStorage::create_new_file`]
507 /// - [`PublicStorage::create_new_file_with_pending`]
508 /// - [`PublicStorage::scan_by_path`]
509 ///
510 /// # Support
511 /// All Android versions.
512 #[maybe_async]
513 pub fn scan(
514 &self,
515 uri: &FileUri,
516 ) -> Result<()> {
517
518 #[cfg(not(target_os = "android"))] {
519 Err(Error::NOT_ANDROID)
520 }
521 #[cfg(target_os = "android")] {
522 self.impls().scan_file_in_public_storage(uri, false).await
523 }
524 }
525
526 /// For details, see [`PublicStorage::scan`].
527 ///
528 /// The difference is that this method scans the file even on Android 10 or higher.
529 ///
530 /// On Android 10 or higher, files are automatically registered in the appropriate MediaStore as needed.
531 /// Scanning is triggered when the file descriptor is closed
532 /// or as part of the [`pending`](PublicStorage::set_pending) lifecycle.
533 /// Therefore, please consider carefully whether this is truly necessary.
534 #[maybe_async]
535 pub fn _scan(
536 &self,
537 uri: &FileUri,
538 ) -> Result<()> {
539
540 #[cfg(not(target_os = "android"))] {
541 Err(Error::NOT_ANDROID)
542 }
543 #[cfg(target_os = "android")] {
544 self.impls().scan_file_in_public_storage(uri, true).await
545 }
546 }
547
548 /// For details, see [`PublicStorage::_scan`].
549 ///
550 /// The difference is that this function waits until the scan is complete and then returns either success or an error.
551 #[maybe_async]
552 pub fn _scan_for_result(
553 &self,
554 uri: &FileUri,
555 ) -> Result<()> {
556
557 #[cfg(not(target_os = "android"))] {
558 Err(Error::NOT_ANDROID)
559 }
560 #[cfg(target_os = "android")] {
561 self.impls().scan_file_in_public_storage_for_result(uri, true).await
562 }
563 }
564
565 /// Scans the specified file in MediaStore and returns it's URI if success.
566 /// By doing this, the file will be visible in the Gallery and etc.
567 ///
568 /// # Note
569 /// Unlike [`PublicStorage::scan`],
570 /// this function waits until the scan is complete and then returns either success or an error.
571 ///
572 /// # Args
573 /// - ***uri*** :
574 /// Absolute path of the target file.
575 /// This must be a path obtained from one of the following:
576 /// - [`PublicStorage::_resolve_path`] and it's descendants path.
577 /// - [`PublicStorage::_get_path`]
578 ///
579 /// - ***mime_type*** :
580 /// The MIME type of the file.
581 /// If `None`, the MIME type will be inferred from the extension of the path.
582 /// If that also fails, `application/octet-stream` will be used.
583 ///
584 /// # Support
585 /// All Android version.
586 #[maybe_async]
587 pub fn scan_by_path(
588 &self,
589 path: impl AsRef<std::path::Path>,
590 mime_type: Option<&str>
591 ) -> Result<FileUri> {
592
593 #[cfg(not(target_os = "android"))] {
594 Err(Error::NOT_ANDROID)
595 }
596 #[cfg(target_os = "android")] {
597 self.impls().scan_file_by_path_in_public_storage(path, mime_type).await
598 }
599 }
600
601 /// Specifies whether the specified file on PublicStorage is marked as pending.
602 /// When set to `true`, the app has exclusive access to the file, and it becomes invisible to other apps.
603 ///
604 /// If it remains `true` for more than seven days,
605 /// the system will automatically delete the file.
606 ///
607 /// # Version behavior
608 /// This is available for Android 10 or higher.
609 /// On Android 9 or lower, this does nothing.
610 ///
611 /// # Args
612 /// - ***uri*** :
613 /// Target file URI.
614 /// This must be a URI obtained from one of the following:
615 /// - [`PublicStorage::write_new`]
616 /// - [`PublicStorage::create_new_file`]
617 /// - [`PublicStorage::create_new_file_with_pending`]
618 /// - [`PublicStorage::scan_by_path`]
619 ///
620 /// # Support
621 /// All Android version.
622 ///
623 /// # References
624 /// - <https://developer.android.com/reference/android/provider/MediaStore.MediaColumns#IS_PENDING>
625 /// - <https://developer.android.com/training/data-storage/shared/media?hl=en#toggle-pending-status>
626 #[maybe_async]
627 pub fn set_pending(&self, uri: &FileUri, is_pending: bool) -> Result<()> {
628 #[cfg(not(target_os = "android"))] {
629 Err(Error::NOT_ANDROID)
630 }
631 #[cfg(target_os = "android")] {
632 self.impls().set_file_pending_in_public_storage(uri, is_pending).await
633 }
634 }
635
636 /// Gets the absolute path of the specified file.
637 ///
638 /// # Note
639 /// For description and notes on path permissions and handling,
640 /// see [`PublicStorage::_resolve_path`].
641 /// It involves important constraints and required settings.
642 /// Therefore, **operate files via paths only when it is truly necessary**.
643 ///
644 /// # Args
645 /// - ***uri*** :
646 /// Target file URI.
647 /// This must be a URI obtained from one of the following:
648 /// - [`PublicStorage::write_new`]
649 /// - [`PublicStorage::create_new_file`]
650 /// - [`PublicStorage::create_new_file_with_pending`]
651 /// - [`PublicStorage::scan_by_path`]
652 ///
653 /// # Support
654 /// All Android version.
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 #[maybe_async]
753 pub fn _resolve_path(
754 &self,
755 volume_id: Option<&StorageVolumeId>,
756 base_dir: impl Into<PublicDir>,
757 ) -> Result<std::path::PathBuf> {
758
759 #[cfg(not(target_os = "android"))] {
760 Err(Error::NOT_ANDROID)
761 }
762 #[cfg(target_os = "android")] {
763 self.impls().resolve_dir_path_in_public_storage(volume_id, base_dir).await
764 }
765 }
766
767 /// Builds the specified directory URI.
768 ///
769 /// This should only be used as `initial_location` in the file picker, such as [`FilePicker::pick_files`].
770 /// It must not be used for any other purpose.
771 ///
772 /// This is useful when selecting save location,
773 /// but when selecting existing entries, `initial_location` is often better with None.
774 ///
775 /// # Args
776 /// - ***volume_id*** :
777 /// ID of the storage volume, such as internal storage, SD card, etc.
778 /// If `None` is provided, [`the primary storage volume`](PublicStorage::get_primary_volume) will be used.
779 ///
780 /// - ***base_dir*** :
781 /// The base directory.
782 ///
783 /// - ***relative_path*** :
784 /// The directory path relative to the base directory.
785 ///
786 /// - ***create_dir_all*** :
787 /// Creates directories if missing.
788 /// See [`PublicStorage::create_dir_all`].
789 /// If error occurs, it will be ignored.
790 ///
791 /// # Support
792 /// All Android version.
793 ///
794 /// Note :
795 /// - [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
796 /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].
797 /// - [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
798 /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].
799 /// - Others dirs are available in all Android versions.
800 #[maybe_async]
801 pub fn resolve_initial_location(
802 &self,
803 volume_id: Option<&StorageVolumeId>,
804 base_dir: impl Into<PublicDir>,
805 relative_path: impl AsRef<std::path::Path>,
806 create_dir_all: bool
807 ) -> Result<FileUri> {
808
809 #[cfg(not(target_os = "android"))] {
810 Err(Error::NOT_ANDROID)
811 }
812 #[cfg(target_os = "android")] {
813 self.impls().resolve_initial_location_in_public_storage(volume_id, base_dir, relative_path, create_dir_all).await
814 }
815 }
816
817 /// Verify whether [`PublicAudioDir::Audiobooks`] is available on a given device.
818 ///
819 /// If on Android 10 (API level 29) or higher, this returns true.
820 /// If on Android 9 (API level 28) and lower, this returns false.
821 ///
822 /// # Support
823 /// All Android version.
824 #[always_sync]
825 pub fn is_audiobooks_dir_available(&self) -> Result<bool> {
826 #[cfg(not(target_os = "android"))] {
827 Err(Error::NOT_ANDROID)
828 }
829 #[cfg(target_os = "android")] {
830 Ok(self.impls().consts()?.env_dir_audiobooks.is_some())
831 }
832 }
833
834 /// Verify whether [`PublicAudioDir::Recordings`] is available on a given device.
835 ///
836 /// If on Android 12 (API level 31) or higher, this returns true.
837 /// If on Android 11 (API level 30) and lower, this returns false.
838 ///
839 /// # Support
840 /// All Android version.
841 #[always_sync]
842 pub fn is_recordings_dir_available(&self) -> Result<bool> {
843 #[cfg(not(target_os = "android"))] {
844 Err(Error::NOT_ANDROID)
845 }
846 #[cfg(target_os = "android")] {
847 Ok(self.impls().consts()?.env_dir_recordings.is_some())
848 }
849 }
850
851
852 #[deprecated = "Use PublicStorage::scan instead"]
853 #[maybe_async]
854 pub fn scan_file(
855 &self,
856 uri: &FileUri,
857 ) -> Result<()> {
858
859 self.scan(uri).await
860 }
861
862 #[deprecated = "Use `AndroidFs::resolve_root_initial_location` instead"]
863 #[maybe_async]
864 pub fn resolve_initial_location_top(
865 &self,
866 volume_id: Option<&StorageVolumeId>
867 ) -> Result<FileUri> {
868
869 #[cfg(not(target_os = "android"))] {
870 Err(Error::NOT_ANDROID)
871 }
872 #[cfg(target_os = "android")] {
873 self.impls().resolve_root_initial_location(volume_id).await
874 }
875 }
876
877 #[deprecated = "Use `PublicStorage::_resolve_path` instead"]
878 #[maybe_async]
879 pub fn resolve_path(
880 &self,
881 volume_id: Option<&StorageVolumeId>,
882 base_dir: impl Into<PublicDir>,
883 ) -> Result<std::path::PathBuf> {
884
885 self._resolve_path(volume_id, base_dir).await
886 }
887
888 #[deprecated = "Use `PublicStorage::_get_path` instead"]
889 #[maybe_async]
890 pub fn get_path(
891 &self,
892 uri: &FileUri,
893 ) -> Result<std::path::PathBuf> {
894
895 self._get_path(uri).await
896 }
897}