tauri_plugin_android_fs/lib.rs
1//! # Overview
2//!
3//! The Android file system is strict and complex because its behavior and the available APIs vary depending on the version.
4//! This plugin was created to provide explicit and consistent file operations.
5//! No special permission or configuration is required.
6//!
7//! # Setup
8//! All you need to do is register the core plugin with Tauri:
9//!
10//! `src-tauri/src/lib.rs`
11//!
12//! ```no_run
13//! #[cfg_attr(mobile, tauri::mobile_entry_point)]
14//! pub fn run() {
15//! tauri::Builder::default()
16//! .plugin(tauri_plugin_android_fs::init()) // This
17//! .run(tauri::generate_context!())
18//! .expect("error while running tauri application");
19//! }
20//! ```
21//!
22//! # Usage
23//! There are three main ways to manipulate files:
24//!
25//! ### 1. Dialog
26//! Opens the file picker to read and write user-selected files.
27//!
28//! ```no_run
29//! use tauri_plugin_android_fs::{AndroidFs, AndroidFsExt};
30//!
31//! fn read_files(app: tauri::AppHandle) {
32//! let api = app.android_fs();
33//! let selected_paths = api.show_open_file_dialog(
34//! &["*/*"], // Target MIME types
35//! true // Allow multiple files
36//! ).unwrap();
37//!
38//! if selected_paths.is_empty() {
39//! // Handle cancel
40//! }
41//! else {
42//! for path in selected_paths {
43//! let file_name = api.get_file_name(&path).unwrap();
44//! let file: std::fs::File = api.open_file(&path).unwrap();
45//! // Handle read-only file
46//! }
47//! }
48//! }
49//!
50//! fn write_file(app: tauri::AppHandle) {
51//! let api = app.android_fs();
52//! let selected_path = api.show_save_file_dialog(
53//! "", // Initial file name
54//! Some("image/png") // Target MIME type
55//! ).unwrap();
56//!
57//! if let Some(path) = selected_path {
58//! let mut file: std::fs::File = api.create_file(&path).unwrap();
59//! // Handle write-only file
60//! }
61//! else {
62//! // Handle cancel
63//! }
64//! }
65//! ```
66//!
67//! ### 2. Public Storage
68//! File storage that is available to other applications and users.
69//!
70//! ```no_run
71//! use tauri_plugin_android_fs::{AndroidFs, AndroidFsExt, PublicImageDir, PublicStorage};
72//!
73//! fn example(app: tauri::AppHandle) {
74//! let api = app.android_fs().public_storage();
75//! let contents: Vec<u8> = todo!();
76//!
77//! // Write a PNG image
78//! api.write_image(
79//! PublicImageDir::Pictures, // Base directory
80//! "myApp/2025-02-13.png", // Relative file path
81//! Some("image/png"), // MIME type
82//! &contents
83//! ).unwrap();
84//! }
85//! ```
86//!
87//! ### 3. Private Storage
88//! File storage intended for the app’s use only.
89//!
90//! ```no_run
91//! use tauri_plugin_android_fs::{AndroidFs, AndroidFsExt, PrivateDir, PrivateStorage};
92//!
93//! fn example(app: tauri::AppHandle) {
94//! let api = app.android_fs().private_storage();
95//! let contents: Vec<u8> = todo!();
96//!
97//! // Write contents
98//! api.write(
99//! PrivateDir::Data, // Base directory
100//! "config/data1", // Relative file path
101//! &contents
102//! ).unwrap();
103//!
104//! // Read contents
105//! let contents = api.read(
106//! PrivateDir::Data, // Base directory
107//! "config/data1" // Relative file path
108//! ).unwrap();
109//! }
110//! ```
111//!
112//! # License
113//! MIT OR Apache-2.0
114
115mod models;
116mod impls;
117mod error;
118
119use std::io::{Read, Write};
120
121pub use models::*;
122pub use error::{Error, Result, PathError};
123pub use impls::{AndroidFsExt, init};
124
125/// This is [`tauri_plugin_fs::FilePath`] for compatibility.
126///
127/// # Note
128/// In this crate, functions that take this as an argument will work correctly if it is the value returned by a function within this crate, at a minimum.
129/// And [`FilePath::Path(_)`](tauri_plugin_fs::FilePath::Path) will not work in this crate.
130pub type FilePath = tauri_plugin_fs::FilePath;
131
132/// API
133pub trait AndroidFs {
134
135 /// Verify whether this plugin is available.
136 ///
137 /// On Android, this returns true.
138 /// On other platforms, this returns false.
139 fn is_available(&self) -> bool {
140 #[cfg(not(target_os = "android"))] {
141 false
142 }
143 #[cfg(target_os = "android")] {
144 true
145 }
146 }
147
148 /// Get the file name.
149 ///
150 /// `FilePath` can be obtained from functions such as [`AndroidFs::show_open_file_dialog`], [`AndroidFs::show_open_visual_media_dialog`], or [`AndroidFs::show_save_file_dialog`].
151 ///
152 /// # Support
153 /// All Android version.
154 fn get_file_name(&self, path: &FilePath) -> crate::Result<String>;
155
156 /// Get the mime type.
157 /// If the type is unknown, this returns None.
158 /// And this mime type might differ from the file name extension.
159 ///
160 /// `FilePath` can be obtained from functions such as [`AndroidFs::show_open_file_dialog`], [`AndroidFs::show_open_visual_media_dialog`], or [`AndroidFs::show_save_file_dialog`].
161 ///
162 /// # Support
163 /// All Android version.
164 fn get_mime_type(&self, path: &FilePath) -> crate::Result<Option<String>>;
165
166 /// Open a file in read-only mode.
167 ///
168 /// If you only need to read the entire file contents, consider using [`AndroidFs::read`] or [`AndroidFs::read_to_string`] instead.
169 ///
170 /// `FilePath` can be obtained from functions such as [`AndroidFs::show_open_file_dialog`] or [`AndroidFs::show_open_visual_media_dialog`].
171 ///
172 /// # Support
173 /// All Android version.
174 fn open_file(&self, path: &FilePath) -> crate::Result<std::fs::File>;
175
176 /// This is deprecated. Because inappropriate fn name.
177 /// Use [`AndroidFs::create_file`] insted.
178 #[deprecated(note = "Because inappropriate fn name. Use AndroidFs::create_file insted.")]
179 #[warn(deprecated)]
180 fn open_file_writable(&self, path: &FilePath) -> crate::Result<std::fs::File> {
181 self.create_file(path)
182 }
183
184 /// Opens a file in write-only mode from ***writable*** `FilePath`.
185 /// This function will create a file if it does not exist, and will truncate it if it does.
186 ///
187 /// If you only need to write the contents, consider using [`AndroidFs::write`] instead.
188 ///
189 /// # Note
190 /// A **writable** `FilePath` can be obtained from [`AndroidFs::show_save_file_dialog`],
191 /// but NOT from [`AndroidFs::show_open_file_dialog`] or [`AndroidFs::show_open_visual_media_dialog`].
192 ///
193 /// # Support
194 /// All Android version.
195 fn create_file(&self, path: &FilePath) -> crate::Result<std::fs::File>;
196
197 /// Reads the entire contents of a file into a bytes vector.
198 ///
199 /// If you need to operate on a readable file, use [`AndroidFs::open_file`] instead.
200 ///
201 /// `FilePath` can be obtained from functions such as [`AndroidFs::show_open_file_dialog`] or [`AndroidFs::show_open_visual_media_dialog`].
202 ///
203 /// # Support
204 /// All Android version.
205 fn read(&self, path: &FilePath) -> crate::Result<Vec<u8>> {
206 let mut file = self.open_file(path)?;
207 let mut buf = file.metadata().ok()
208 .map(|m| m.len() as usize)
209 .map(Vec::with_capacity)
210 .unwrap_or_else(Vec::new);
211
212 file.read_to_end(&mut buf)?;
213 Ok(buf)
214 }
215
216 /// Reads the entire contents of a file into a string.
217 ///
218 /// If you need to operate on a readable file, use [`AndroidFs::open_file`] instead.
219 ///
220 /// `FilePath` can be obtained from functions such as [`AndroidFs::show_open_file_dialog`] or [`AndroidFs::show_open_visual_media_dialog`].
221 ///
222 /// # Support
223 /// All Android version.
224 fn read_to_string(&self, path: &FilePath) -> crate::Result<String> {
225 let mut file = self.open_file(path)?;
226 let mut buf = file.metadata().ok()
227 .map(|m| m.len() as usize)
228 .map(String::with_capacity)
229 .unwrap_or_else(String::new);
230
231 file.read_to_string(&mut buf)?;
232 Ok(buf)
233 }
234
235 /// Writes a slice as the entire contents of a file in a **writable** `FilePath`.
236 /// This function will create a file if it does not exist, and will entirely replace its contents if it does.
237 ///
238 /// If you want to write to a file, use [`AndroidFs::create_file`] instead.
239 ///
240 /// # Note
241 /// A **writable** `FilePath` can be obtained from [`AndroidFs::show_save_file_dialog`],
242 /// but not from [`AndroidFs::show_open_file_dialog`] or [`AndroidFs::show_open_visual_media_dialog`].
243 ///
244 /// # Support
245 /// All Android version.
246 fn write(&self, path: &FilePath, contetns: impl AsRef<[u8]>) -> crate::Result<()> {
247 let mut file = self.create_file(path)?;
248 file.write_all(contetns.as_ref())?;
249 Ok(())
250 }
251
252 /// Open a dialog for file selection.
253 /// This returns a **readonly** `FilePath` vec. If no file is selected or canceled by user, it is an empty vec.
254 ///
255 /// For images and videos, consider using [`AndroidFs::show_open_visual_media_dialog`] instead.
256 ///
257 /// # Note
258 /// `mime_types` represents the types of files that should be selected.
259 /// However, there is no guarantee that the returned file will match the specified types.
260 /// If this is empty, all file types will be available for selection.
261 /// This is equivalent to `["*/*"]`, and it will invoke the standard file picker in most cases.
262 ///
263 /// By default, this [`FilePath`] is valid until the app is terminated.
264 /// If you want to persist it across app restarts, use [`AndroidFs::take_persistable_read_permission`].
265 ///
266 /// # Support
267 /// All Android version.
268 fn show_open_file_dialog(
269 &self,
270 mime_types: &[&str],
271 multiple: bool
272 ) -> crate::Result<Vec<FilePath>>;
273
274 /// Opens a dialog for media file selection, such as images and videos.
275 /// This returns a **readonly** `FilePath` vec. If no file is selected or canceled by user, it is an empty vec.
276 ///
277 /// This is more user-friendly than [`AndroidFs::show_open_file_dialog`].
278 ///
279 /// # Note
280 /// By default, this [`FilePath`] is valid until the app is terminated.
281 /// If you want to persist it across app restarts, use [`AndroidFs::take_persistable_read_permission`].
282 ///
283 /// The file obtained from this function cannot retrieve the correct file name using [`AndroidFs::get_file_name`].
284 /// Instead, it will be assigned a sequential number, such as `1000091523.png`.
285 /// <https://issuetracker.google.com/issues/268079113>
286 ///
287 /// # Support
288 /// This is available on devices that meet the following criteria:
289 /// - Run Android 11 (API level 30) or higher
290 /// - Receive changes to Modular System Components through Google System Updates
291 ///
292 /// Availability on a given device can be verified by calling [`AndroidFs::is_visual_media_dialog_available`].
293 /// If not supported, this functions the same as [`AndroidFs::show_open_file_dialog`].
294 fn show_open_visual_media_dialog(
295 &self,
296 target: VisualMediaTarget,
297 multiple: bool
298 ) -> crate::Result<Vec<FilePath>>;
299
300 /// Open a dialog for file saving, and write contents to the selected file, then return that path.
301 /// This returns a **writable** `FilePath` . If canceled by the user, return None, and do not write it.
302 ///
303 /// If you want to write to a file, use [`AndroidFs::show_save_file_dialog`] then [`AndroidFs::create_file`] insted.
304 ///
305 /// When storing media files such as images, videos, and audio, consider using [`PublicStorage::write_image`] or a similar method.
306 /// When a file does not need to be accessed by other applications and users, consider using [`PrivateStorage::write`].
307 /// These are easier because the destination does not need to be selected in a dialog.
308 ///
309 /// # Note
310 /// `mime_type` specify the type of the target file to be saved.
311 /// It should be provided whenever possible. If not specified, `application/octet-stream` is used, as generic, unknown, or undefined file types.
312 ///
313 /// By default, this [`FilePath`] is valid until the app is terminated.
314 /// If you want to persist it across app restarts, use [`AndroidFs::take_persistable_read_permission`] or [`AndroidFs::take_persistable_write_permission`].
315 ///
316 /// # Support
317 /// All Android version.
318 fn show_save_file_dialog_with_contents(
319 &self,
320 default_file_name: impl AsRef<str>,
321 mime_type: Option<&str>,
322 contents: impl AsRef<[u8]>,
323 ) -> crate::Result<Option<FilePath>> {
324
325 if let Some(path) = self.show_save_file_dialog(default_file_name, mime_type)? {
326 self.write(&path, contents)?;
327 return Ok(Some(path))
328 }
329
330 Ok(None)
331 }
332
333 /// Open a dialog for file saving, and return the selected path.
334 /// This returns a **writable** `FilePath` . If canceled by the user, return None.
335 ///
336 /// If you only need to write contents, use [`AndroidFs::show_save_file_dialog_with_contents`] instead.
337 ///
338 /// When storing media files such as images, videos, and audio, consider using [`PublicStorage::write_image`] or a similar method.
339 /// When a file does not need to be accessed by other applications and users, consider using [`PrivateStorage::write`].
340 /// These are easier because the destination does not need to be selected in a dialog.
341 ///
342 /// # Note
343 /// `mime_type` specify the type of the target file to be saved.
344 /// It should be provided whenever possible. If not specified, `application/octet-stream` is used, as generic, unknown, or undefined file types.
345 ///
346 /// By default, this [`FilePath`] is valid until the app is terminated.
347 /// If you want to persist it across app restarts, use [`AndroidFs::take_persistable_read_permission`] or [`AndroidFs::take_persistable_write_permission`].
348 ///
349 /// # Support
350 /// All Android version.
351 fn show_save_file_dialog(
352 &self,
353 default_file_name: impl AsRef<str>,
354 mime_type: Option<&str>,
355 ) -> crate::Result<Option<FilePath>>;
356
357 /// Take persistent permission to read the file.
358 ///
359 /// To preserve access to files across app restarts and improve the user experience.
360 /// If you only need it until the end of the app session, you do not need to use this.
361 ///
362 /// This works by just calling it, without displaying any confirmation to the user.
363 ///
364 /// # Note
365 /// Even after calling this, the app doesn't retain access to the file if the file is moved or deleted.
366 ///
367 /// # Helper
368 /// There are helper functions, such as [`convert_file_path_to_string`] and [`convert_string_to_file_path`], for storing [`FilePath`].
369 fn take_persistable_read_permission(&self, path: &FilePath) -> crate::Result<()>;
370
371 /// Take persistent permission to write the file.
372 /// This is only for **writable** [`FilePath`].
373 ///
374 /// To preserve access to files across app restarts and improve the user experience.
375 /// If you only need it until the end of the app session, you do not need to use this.
376 ///
377 /// This works by just calling it, without displaying any confirmation to the user.
378 /// # Note
379 /// Even after calling this, the app doesn't retain access to the file if the file is moved or deleted.
380 ///
381 /// # Helper
382 /// There are helper functions, such as [`convert_file_path_to_string`] and [`convert_string_to_file_path`], for storing [`FilePath`].
383 fn take_persistable_write_permission(&self, path: &FilePath) -> crate::Result<()>;
384
385 /// Verify whether [`AndroidFs::show_open_visual_media_dialog`] is available on a given device.
386 ///
387 /// # Support
388 /// All Android version.
389 fn is_visual_media_dialog_available(&self) -> crate::Result<bool>;
390
391 /// File storage API intended to be shared with other apps.
392 fn public_storage(&self) -> &impl PublicStorage;
393
394 /// This is typo and deprecated.
395 /// Use [`AndroidFs::public_storage`] instead.
396 #[deprecated(note = "This is typo. Use AndroidFs::public_storage instead.")]
397 #[warn(deprecated)]
398 fn pubic_storage(&self) -> &impl PublicStorage {
399 self.public_storage()
400 }
401
402 /// File storage API intended for the app’s use only.
403 fn private_storage(&self) -> &impl PrivateStorage;
404}
405
406/// File storage that is available to other applications and users.
407pub trait PublicStorage {
408
409 /// Save the contents to public storage.
410 /// This is used when saving a file for access by other applications and users.
411 ///
412 /// When storing media files such as images, videos, and audio, consider using [`PublicStorage::write_image`] or a similar method.
413 /// For saving a general-purpose file, it is often better to use [`AndroidFs::show_save_file_dialog`].
414 ///
415 /// If the same file name already exists, a sequential number is added to the name and saved.
416 ///
417 /// If you want to operate directly on a write-only file, use [`PublicStorage::write_with_contents_writer`] insted.
418 ///
419 /// # Note
420 /// Do not save files directly in the base directory.
421 /// Please specify a subdirectory in the `relative_path_with_sub_dir`, such as `appName/file.txt` or `appName/2025-2-11/file.txt`.
422 /// Do not use `file.txt`.
423 ///
424 /// # Support
425 /// All Android version.
426 fn write(
427 &self,
428 base_dir: PublicGeneralPurposeDir,
429 relative_path_with_sub_dir: impl AsRef<str>,
430 mime_type: Option<&str>,
431 contents: impl AsRef<[u8]>,
432 ) -> crate::Result<FilePath> {
433
434 self.write_with_contents_writer(
435 base_dir,
436 relative_path_with_sub_dir,
437 mime_type,
438 |file| file.write_all(contents.as_ref())
439 )
440 }
441
442 /// Save the contents as an image file to public storage.
443 /// This is used when saving a file for access by other applications and users.
444 ///
445 /// If the same file name already exists, a sequential number is added to the name and saved.
446 ///
447 /// If you want to operate directly on a write-only file, use [`PublicStorage::write_image_with_contents_writer`] insted.
448 ///
449 /// # Note
450 /// Do not set a non-image type to `mime_type`, as it may result in an error.
451 /// Even if the type is an image, an error will occur if the Android system does not recognize the type or contents as an image.
452 ///
453 /// Do not save files directly in the base directory.
454 /// Please specify a subdirectory in the `relative_path_with_sub_dir`, such as `appName/file.png` or `appName/2025-2-11/file.png`.
455 /// Do not use `file.png`.
456 ///
457 /// # Support
458 /// All Android version.
459 fn write_image(
460 &self,
461 base_dir: PublicImageDir,
462 relative_path_with_sub_dir: impl AsRef<str>,
463 mime_type: Option<&str>,
464 contents: impl AsRef<[u8]>,
465 ) -> crate::Result<FilePath> {
466
467 self.write_image_with_contents_writer(
468 base_dir,
469 relative_path_with_sub_dir,
470 mime_type,
471 |file| file.write_all(contents.as_ref())
472 )
473 }
474
475 /// Save the contents as an video file to public storage.
476 /// This is used when saving a file for access by other applications and users.
477 ///
478 /// If the same file name already exists, a sequential number is added to the name and saved.
479 ///
480 /// If you want to operate directly on a write-only file, use [`PublicStorage::write_video_with_contents_writer`] insted.
481 ///
482 /// # Note
483 /// Do not set a non-video type to `mime_type`, as it may result in an error.
484 /// Even if the type is an video, an error will occur if the Android system does not recognize the type or contents as an video.
485 ///
486 /// Do not save files directly in the base directory.
487 /// Please specify a subdirectory in the `relative_path_with_sub_dir`, such as `appName/file.mp4` or `appName/2025-2-11/file.mp4`.
488 /// Do not use `file.mp4`.
489 ///
490 /// # Support
491 /// All Android version.
492 fn write_video(
493 &self,
494 base_dir: PublicVideoDir,
495 relative_path_with_sub_dir: impl AsRef<str>,
496 mime_type: Option<&str>,
497 contents: impl AsRef<[u8]>,
498 ) -> crate::Result<FilePath> {
499
500 self.write_video_with_contents_writer(
501 base_dir,
502 relative_path_with_sub_dir,
503 mime_type,
504 |file| file.write_all(contents.as_ref())
505 )
506 }
507
508 /// Save the contents as an audio file to public storage.
509 /// This is used when saving a file for access by other applications and users.
510 ///
511 /// If the same file name already exists, a sequential number is added to the name and saved.
512 ///
513 /// If you want to operate directly on a write-only file, use [`PublicStorage::write_audio_with_contents_writer`] insted.
514 ///
515 /// # Note
516 /// Do not set a non-audio type to `mime_type`, as it may result in an error.
517 /// Even if the type is an audio, an error will occur if the Android system does not recognize the type or contents as an audio.
518 ///
519 /// Do not save files directly in the base directory.
520 /// Please specify a subdirectory in the `relative_path_with_sub_dir`, such as `appName/file.mp3` or `appName/2025-2-11/file.mp3`.
521 /// Do not use `file.mp3`.
522 ///
523 /// # Support
524 /// [`PublicAudioDir::Audiobooks`] is not available on Android 9 (API level 28) and lower.
525 /// Availability on a given device can be verified by calling [`PublicStorage::is_audiobooks_dir_available`].
526 ///
527 /// [`PublicAudioDir::Recordings`] is not available on Android 11 (API level 30) and lower.
528 /// Availability on a given device can be verified by calling [`PublicStorage::is_recordings_dir_available`].
529 ///
530 /// Others are available in all Android versions.
531 fn write_audio(
532 &self,
533 base_dir: PublicAudioDir,
534 relative_path_with_sub_dir: impl AsRef<str>,
535 mime_type: Option<&str>,
536 contents: impl AsRef<[u8]>,
537 ) -> crate::Result<FilePath> {
538
539 self.write_audio_with_contents_writer(
540 base_dir,
541 relative_path_with_sub_dir,
542 mime_type,
543 |file| file.write_all(contents.as_ref())
544 )
545 }
546
547 /// See [`PublicStorage::write`] for description.
548 ///
549 /// # Note
550 /// The file provided in `contents_writer` is write-only.
551 ///
552 /// # Examples
553 /// The following is equivalent to [`PublicStorage::write`].
554 /// ```ignore
555 /// self.write_with_contents_writer(
556 /// base_dir,
557 /// relative_path_with_sub_dir,
558 /// mime_type,
559 /// |file| file.write_all(contents)
560 /// )
561 /// ```
562 fn write_with_contents_writer(
563 &self,
564 base_dir: PublicGeneralPurposeDir,
565 relative_path_with_sub_dir: impl AsRef<str>,
566 mime_type: Option<&str>,
567 contents_writer: impl FnOnce(&mut std::fs::File) -> std::io::Result<()>
568 ) -> crate::Result<FilePath>;
569
570 /// See [`PublicStorage::write_image`] for description.
571 ///
572 /// # Note
573 /// The file provided in `contents_writer` is write-only.
574 ///
575 /// # Examples
576 /// The following is equivalent to [`PublicStorage::write_image`].
577 /// ```ignore
578 /// self.write_image_with_contents_writer(
579 /// base_dir,
580 /// relative_path_with_sub_dir,
581 /// mime_type,
582 /// |file| file.write_all(contents)
583 /// )
584 /// ```
585 fn write_image_with_contents_writer(
586 &self,
587 base_dir: PublicImageDir,
588 relative_path_with_sub_dir: impl AsRef<str>,
589 mime_type: Option<&str>,
590 contents_writer: impl FnOnce(&mut std::fs::File) -> std::io::Result<()>
591 ) -> crate::Result<FilePath>;
592
593 /// See [`PublicStorage::write_video`] for description.
594 ///
595 /// # Note
596 /// The file provided in `contents_writer` is write-only.
597 ///
598 /// # Examples
599 /// The following is equivalent to [`PublicStorage::write_video`].
600 /// ```ignore
601 /// self.write_video_with_contents_writer(
602 /// base_dir,
603 /// relative_path_with_sub_dir,
604 /// mime_type,
605 /// |file| file.write_all(contents)
606 /// )
607 /// ```
608 fn write_video_with_contents_writer(
609 &self,
610 base_dir: PublicVideoDir,
611 relative_path_with_sub_dir: impl AsRef<str>,
612 mime_type: Option<&str>,
613 contents_writer: impl FnOnce(&mut std::fs::File) -> std::io::Result<()>
614 ) -> crate::Result<FilePath>;
615
616 /// See [`PublicStorage::write_audio`] for description.
617 ///
618 /// # Note
619 /// The file provided in `contents_writer` is write-only.
620 ///
621 /// # Examples
622 /// The following is equivalent to [`PublicStorage::write_audio`].
623 /// ```ignore
624 /// self.write_audio_with_contents_writer(
625 /// base_dir,
626 /// relative_path_with_sub_dir,
627 /// mime_type,
628 /// |file| file.write_all(contents)
629 /// )
630 /// ```
631 fn write_audio_with_contents_writer(
632 &self,
633 base_dir: PublicAudioDir,
634 relative_path_with_sub_dir: impl AsRef<str>,
635 mime_type: Option<&str>,
636 contents_writer: impl FnOnce(&mut std::fs::File) -> std::io::Result<()>
637 ) -> crate::Result<FilePath>;
638
639 /// Verify whether [`PublicAudioDir::Audiobooks`] is available on a given device.
640 ///
641 /// # Support
642 /// All Android version.
643 fn is_audiobooks_dir_available(&self) -> crate::Result<bool>;
644
645 /// Verify whether [`PublicAudioDir::Recordings`] is available on a given device.
646 ///
647 /// # Support
648 /// All Android version.
649 fn is_recordings_dir_available(&self) -> crate::Result<bool>;
650}
651
652/// File storage intended for the app’s use only.
653pub trait PrivateStorage {
654
655 /// Get the absolute path of the specified directory.
656 /// Apps require no extra permissions to read or write to the returned path, since this path lives in their private storage.
657 ///
658 /// These files will be deleted when the app is uninstalled and may also be deleted at the user’s request.
659 /// When using [`PrivateDir::Cache`], the system will automatically delete files in this directory as disk space is needed elsewhere on the device.
660 ///
661 /// The returned path may change over time if the calling app is moved to an adopted storage device, so only relative paths should be persisted.
662 ///
663 /// # Examples
664 /// ```no_run
665 /// use tauri_plugin_android_fs::{AndroidFs, AndroidFsExt, PrivateDir, PrivateStorage};
666 ///
667 /// fn example(app: tauri::AppHandle) {
668 /// let api = app.android_fs().private_storage();
669 ///
670 /// let dir_path = api.resolve_path(PrivateDir::Data).unwrap();
671 /// let file_path = dir_path.join("2025-2-12/data.txt");
672 ///
673 /// // Write file
674 /// std::fs::create_dir_all(file_path.parent().unwrap()).unwrap();
675 /// std::fs::write(&file_path, "aaaa").unwrap();
676 ///
677 /// // Read file
678 /// let _ = std::fs::read_to_string(&file_path).unwrap();
679 ///
680 /// // Remove file
681 /// std::fs::remove_file(&file_path).unwrap();
682 ///
683 /// // Remove all files in the dir.
684 /// std::fs::remove_dir_all(&dir_path).unwrap();
685 /// }
686 /// ```
687 ///
688 /// # Support
689 /// All Android version.
690 fn resolve_path(&self, dir: PrivateDir) -> crate::Result<std::path::PathBuf>;
691
692 /// Get the absolute path of the specified relative path and base directory.
693 /// Apps require no extra permissions to read or write to the returned path, since this path lives in their private storage.
694 ///
695 /// See [`PrivateStorage::resolve_path`] for details.
696 ///
697 /// # Support
698 /// All Android version.
699 fn resolve_path_with(
700 &self,
701 base_dir: PrivateDir,
702 relative_path: impl AsRef<str>
703 ) -> crate::Result<std::path::PathBuf> {
704
705 let relative_path = relative_path.as_ref().trim_start_matches('/');
706 let path = self.resolve_path(base_dir)?.join(relative_path);
707 Ok(path)
708 }
709
710 /// Writes a slice as the entire contents of a file.
711 ///
712 /// This function will create a file if it does not exist, and will entirely replace its contents if it does.
713 /// Recursively create parent directories if they are missing.
714 ///
715 /// This internally uses [`PrivateStorage::resolve_path`] , [`std::fs::create_dir_all`], and [`std::fs::write`].
716 /// See [`PrivateStorage::resolve_path`] for details.
717 ///
718 /// # Support
719 /// All Android version.
720 fn write(
721 &self,
722 base_dir: PrivateDir,
723 relative_path: impl AsRef<str>,
724 contents: impl AsRef<[u8]>
725 ) -> crate::Result<()> {
726
727 let path = self.resolve_path_with(base_dir, relative_path)?;
728
729 if let Some(parent_dir) = path.parent() {
730 std::fs::create_dir_all(parent_dir)?;
731 }
732
733 std::fs::write(path, contents)?;
734
735 Ok(())
736 }
737
738 /// Open a file in read-only mode.
739 ///
740 /// If you only need to read the entire file contents, consider using [`PrivateStorage::read`] or [`PrivateStorage::read_to_string`] instead.
741 ///
742 /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::File::open`].
743 /// See [`PrivateStorage::resolve_path`] for details.
744 ///
745 /// # Support
746 /// All Android version.
747 fn open_file(
748 &self,
749 base_dir: PrivateDir,
750 relative_path: impl AsRef<str>,
751 ) -> crate::Result<std::fs::File> {
752
753 let path = self.resolve_path_with(base_dir, relative_path)?;
754 Ok(std::fs::File::open(path)?)
755 }
756
757 /// Opens a file in write-only mode.
758 /// This function will create a file if it does not exist, and will truncate it if it does.
759 ///
760 /// If you only need to write the contents, consider using [`PrivateStorage::write`] instead.
761 ///
762 /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::File::create`].
763 /// See [`PrivateStorage::resolve_path`] for details.
764 ///
765 /// # Support
766 /// All Android version.
767 fn create_file(
768 &self,
769 base_dir: PrivateDir,
770 relative_path: impl AsRef<str>,
771 ) -> crate::Result<std::fs::File> {
772
773 let path = self.resolve_path_with(base_dir, relative_path)?;
774 Ok(std::fs::File::create(path)?)
775 }
776
777 /// Reads the entire contents of a file into a bytes vector.
778 ///
779 /// If you need [`std::fs::File`], use [`PrivateStorage::open_file`] insted.
780 ///
781 /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::read`].
782 /// See [`PrivateStorage::resolve_path`] for details.
783 ///
784 /// # Support
785 /// All Android version.
786 fn read(
787 &self,
788 base_dir: PrivateDir,
789 relative_path: impl AsRef<str>,
790 ) -> crate::Result<Vec<u8>> {
791
792 let path = self.resolve_path_with(base_dir, relative_path)?;
793 Ok(std::fs::read(path)?)
794 }
795
796 /// Reads the entire contents of a file into a string.
797 ///
798 /// If you need [`std::fs::File`], use [`PrivateStorage::open_file`] insted.
799 ///
800 /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::read_to_string`].
801 /// See [`PrivateStorage::resolve_path`] for details.
802 ///
803 /// # Support
804 /// All Android version.
805 fn read_to_string(
806 &self,
807 base_dir: PrivateDir,
808 relative_path: impl AsRef<str>,
809 ) -> crate::Result<String> {
810
811 let path = self.resolve_path_with(base_dir, relative_path)?;
812 Ok(std::fs::read_to_string(path)?)
813 }
814
815 /// Returns an iterator over the entries within a directory.
816 ///
817 /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::read_dir`].
818 /// See [`PrivateStorage::resolve_path`] for details.
819 ///
820 /// # Support
821 /// All Android version.
822 fn read_dir(
823 &self,
824 base_dir: PrivateDir,
825 relative_path: Option<&str>,
826 ) -> crate::Result<std::fs::ReadDir> {
827
828 let path = match relative_path {
829 Some(relative_path) => self.resolve_path_with(base_dir, relative_path)?,
830 None => self.resolve_path(base_dir)?,
831 };
832
833 Ok(std::fs::read_dir(path)?)
834 }
835
836 /// Removes a file from the filesystem.
837 ///
838 /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::remove_file`].
839 /// See [`PrivateStorage::resolve_path`] for details.
840 ///
841 /// # Support
842 /// All Android version.
843 fn remove_file(
844 &self,
845 base_dir: PrivateDir,
846 relative_path: impl AsRef<str>,
847 ) -> crate::Result<()> {
848
849 let path = self.resolve_path_with(base_dir, relative_path)?;
850 Ok(std::fs::remove_file(path)?)
851 }
852
853 /// Removes an empty directory.
854 /// If you want to remove a directory that is not empty, as well as all of its contents recursively, consider using [`PrivateStorage::remove_dir_all`] instead.
855 ///
856 /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::remove_dir`].
857 /// See [`PrivateStorage::resolve_path`] for details.
858 ///
859 /// # Support
860 /// All Android version.
861 fn remove_dir(
862 &self,
863 base_dir: PrivateDir,
864 relative_path: Option<&str>,
865 ) -> crate::Result<()> {
866
867 let path = match relative_path {
868 Some(relative_path) => self.resolve_path_with(base_dir, relative_path)?,
869 None => self.resolve_path(base_dir)?,
870 };
871
872 std::fs::remove_dir(path)?;
873 Ok(())
874 }
875
876 /// Removes a directory at this path, after removing all its contents. Use carefully!
877 ///
878 /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::remove_dir_all`].
879 /// See [`PrivateStorage::resolve_path`] for details.
880 ///
881 /// # Support
882 /// All Android version.
883 fn remove_dir_all(
884 &self,
885 base_dir: PrivateDir,
886 relative_path: Option<&str>,
887 ) -> crate::Result<()> {
888
889 let path = match relative_path {
890 Some(relative_path) => self.resolve_path_with(base_dir, relative_path)?,
891 None => self.resolve_path(base_dir)?,
892 };
893
894 std::fs::remove_dir_all(path)?;
895 Ok(())
896 }
897
898 /// Returns Ok(true) if the path points at an existing entity.
899 ///
900 /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::exists`].
901 /// See [`PrivateStorage::resolve_path`] for details.
902 ///
903 /// # Support
904 /// All Android version.
905 fn exists(
906 &self,
907 base_dir: PrivateDir,
908 relative_path: impl AsRef<str>
909 ) -> crate::Result<bool> {
910
911 let path = self.resolve_path_with(base_dir, relative_path)?;
912 Ok(std::fs::exists(path)?)
913 }
914
915 /// Queries the file system to get information about a file, directory.
916 ///
917 /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::metadata`].
918 /// See [`PrivateStorage::resolve_path`] for details.
919 ///
920 /// # Support
921 /// All Android version.
922 fn metadata(
923 &self,
924 base_dir: PrivateDir,
925 relative_path: Option<&str>,
926 ) -> crate::Result<std::fs::Metadata> {
927
928 let path = match relative_path {
929 Some(relative_path) => self.resolve_path_with(base_dir, relative_path)?,
930 None => self.resolve_path(base_dir)?,
931 };
932
933 Ok(std::fs::metadata(path)?)
934 }
935}
936
937/// Convert string to [`FilePath`].
938pub fn convert_file_path_to_string(path: &FilePath) -> String {
939 path.to_string()
940}
941
942/// Convert [`FilePath`] to string.
943///
944/// # Note
945/// This does not validate the value and will not cause an error or panic if an incorrect value is provided.
946pub fn convert_string_to_file_path(string: impl AsRef<str>) -> FilePath {
947 let result: std::result::Result<_, std::convert::Infallible> = string.as_ref().parse();
948
949 // This will not cause panic. Because result is infallible.
950 result.unwrap()
951}