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