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}