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}