tauri_plugin_android_fs/
models.rs

1use serde::{Deserialize, Serialize};
2use crate::{Error, Result};
3
4/// Path to represent a file or directory.
5/// 
6/// # Note
7/// For compatibility, an interconversion to [`tauri_plugin_fs::FilePath`] is implemented, such as follwing.  
8/// This is lossy and also not guaranteed to work properly with other plugins.  
9/// However, reading and writing files by official [`tauri_plugin_fs`] etc. should work well.  
10/// ```no_run
11/// use tauri_plugin_android_fs::FileUri;
12/// use tauri_plugin_fs::FilePath;
13/// 
14/// let uri: FileUri = unimplemented!();
15/// let path: FilePath = uri.into();
16/// let uri: FileUri = path.into();
17/// ```
18/// 
19/// # Typescript type
20/// ```typescript
21/// type FileUri = {
22///     uri: string, // This can use as path for official tauri_plugin_fs
23///     documentTopTreeUri: string | null
24/// }
25/// ```
26#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)]
27#[serde(rename_all = "camelCase")]
28pub struct FileUri {
29    /// `file://` or `content://` URI of file or directory.
30    pub uri: String,
31
32    /// Only files/directories under the directory obtained by `FilePicker::pick_dir` will own this.
33    pub document_top_tree_uri: Option<String>,
34}
35
36impl FileUri {
37
38    pub fn to_string(&self) -> crate::Result<String> {
39        serde_json::to_string(self).map_err(Into::into)
40    }
41
42    pub fn from_str(s: &str) -> crate::Result<Self> {
43        serde_json::from_str(s).map_err(Into::into)
44    }
45
46    pub fn from_path(path: impl AsRef<std::path::Path>) -> Self {
47        Self { uri: format!("file://{}", path.as_ref().to_string_lossy()), document_top_tree_uri: None }
48    }
49
50    #[allow(unused)]
51    pub(crate) fn as_path(&self) -> Option<&std::path::Path> {
52        if self.uri.starts_with("file://") {
53            return Some(std::path::Path::new(self.uri.trim_start_matches("file://")))
54        }
55        None
56    }
57}
58
59impl From<&std::path::Path> for FileUri {
60
61    fn from(path: &std::path::Path) -> Self {
62        Self::from_path(path)
63    }
64}
65
66impl From<&std::path::PathBuf> for FileUri {
67
68    fn from(path: &std::path::PathBuf) -> Self {
69        Self::from_path(path)
70    }
71}
72
73impl From<std::path::PathBuf> for FileUri {
74
75    fn from(path: std::path::PathBuf) -> Self {
76        Self::from_path(path)
77    }
78}
79
80impl From<tauri_plugin_fs::FilePath> for FileUri {
81
82    fn from(value: tauri_plugin_fs::FilePath) -> Self {
83        match value {
84            tauri_plugin_fs::FilePath::Url(url) => Self { uri: url.to_string(), document_top_tree_uri: None },
85            tauri_plugin_fs::FilePath::Path(path_buf) => path_buf.into(),
86        }
87    }
88}
89
90impl From<FileUri> for tauri_plugin_fs::FilePath {
91
92    fn from(value: FileUri) -> Self {
93        type NeverErr<T> = std::result::Result::<T, std::convert::Infallible>;
94        NeverErr::unwrap(value.uri.parse())
95    }
96}
97
98#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)]
99#[serde(rename_all = "camelCase")]
100pub struct PublicStorageVolume {
101
102    /// A user-visible description of the volume.  
103    /// This can be determined by the manufacturer and is often localized according to the user’s language.
104    ///
105    /// e.g.
106    /// - `Internal shared storage`
107    /// - `SDCARD`
108    /// - `SD card`
109    /// - `Virtual SD card`
110    pub description: String,
111
112    /// Indicates whether this is primary storage volume. 
113    /// A device always has one (and one only) primary storage volume.  
114    pub is_primary: bool,
115
116    pub id: PublicStorageVolumeId,
117}
118
119#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)]
120#[serde(rename_all = "camelCase")]
121pub struct PublicStorageVolumeId(pub String);
122
123#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)]
124#[serde(rename_all = "camelCase")]
125pub enum Entry {
126
127    #[non_exhaustive]
128    File {
129        uri: FileUri,
130        name: String,
131        last_modified: std::time::SystemTime,
132        len: u64,
133        mime_type: String,
134    },
135
136    #[non_exhaustive]
137    Dir {
138        uri: FileUri,
139        name: String,
140        last_modified: std::time::SystemTime,
141    }
142}
143
144#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)]
145#[serde(rename_all = "camelCase")]
146pub enum EntryType {
147    File {
148        mime_type: String
149    },
150    Dir,
151}
152
153impl EntryType {
154
155    pub fn is_file(&self) -> bool {
156        matches!(self, Self::File { .. })
157    }
158
159    pub fn is_dir(&self) -> bool {
160        matches!(self, Self::Dir)
161    }
162
163    /// If file, this is no None.  
164    /// If directory, this is None.  
165    pub fn mime_type(&self) -> Option<&str> {
166        match self {
167            EntryType::File { mime_type } => Some(&mime_type),
168            EntryType::Dir => None,
169        }
170    }
171
172    /// If file, this is no None.  
173    /// If directory, this is None.  
174    pub fn into_mime_type(self) -> Option<String> {
175        match self {
176            EntryType::File { mime_type } => Some(mime_type),
177            EntryType::Dir => None,
178        }
179    }
180}
181
182/// Access mode
183#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Deserialize, Serialize)]
184pub enum PersistableAccessMode {
185
186    /// Read access.
187    Read,
188
189    /// Write access.
190    Write,
191
192    /// Read-write access.
193    ReadAndWrite,
194}
195
196#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)]
197pub enum PersistedUriPermission {
198    File {
199        uri: FileUri,
200        can_read: bool,
201        can_write: bool,
202    },
203    Dir {
204        uri: FileUri,
205        can_read: bool,
206        can_write: bool,
207    }
208}
209
210impl PersistedUriPermission {
211
212    pub fn uri(&self) -> &FileUri {
213        match self {
214            PersistedUriPermission::File { uri, .. } => uri,
215            PersistedUriPermission::Dir { uri, .. } => uri,
216        }
217    }
218
219    pub fn into_uri(self) -> FileUri {
220        match self {
221            PersistedUriPermission::File { uri, .. } => uri,
222            PersistedUriPermission::Dir { uri, .. } => uri,
223        }
224    }
225
226    pub fn can_read(&self) -> bool {
227        match self {
228            PersistedUriPermission::File { can_read, .. } => *can_read,
229            PersistedUriPermission::Dir { can_read, .. } => *can_read,
230        }
231    }
232
233    pub fn can_write(&self) -> bool {
234        match self {
235            PersistedUriPermission::File { can_write, .. } => *can_write,
236            PersistedUriPermission::Dir { can_write, .. } => *can_write,
237        }
238    }
239
240    pub fn is_file(&self) -> bool {
241        matches!(self, PersistedUriPermission::File { .. })
242    }
243
244    pub fn is_dir(&self) -> bool {
245        matches!(self, PersistedUriPermission::Dir { .. })
246    }
247}
248
249#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Deserialize, Serialize)]
250pub struct Size {
251    pub width: u32,
252    pub height: u32
253}
254
255#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
256#[non_exhaustive]
257pub enum ImageFormat {
258
259    /// - Loss less
260    /// - Support transparency
261    Png,
262
263    /// - Lossy
264    /// - Unsupport transparency
265    Jpeg,
266
267    /// - Lossy (**Not loss less**)
268    /// - Support transparency
269    Webp,
270
271    /// - Lossy
272    /// - Unsupport transparency
273    JpegWith {
274
275        /// Range is `0.0 ~ 1.0`  
276        /// 0.0 means compress for the smallest size.  
277        /// 1.0 means compress for max visual quality.  
278        quality: f32
279    },
280
281    /// - Lossy
282    /// - Support transparency
283    WebpWith {
284        
285        /// Range is `0.0 ~ 1.0`  
286        /// 0.0 means compress for the smallest size.  
287        /// 1.0 means compress for max visual quality.  
288        quality: f32
289    }
290}
291
292/// Access mode
293#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Deserialize, Serialize)]
294#[non_exhaustive]
295pub enum FileAccessMode {
296
297    /// Opens the file in read-only mode.
298    /// 
299    /// FileDescriptor mode: "r"
300    Read,
301
302    /// Opens the file in write-only mode.  
303    /// 
304    /// Since Android 10, this may or may not truncate existing contents. 
305    /// If the new file is smaller than the old one, **this may cause the file to become corrupted**.
306    /// <https://issuetracker.google.com/issues/180526528>
307    /// 
308    /// The reason this is marked as deprecated is because of that behavior, 
309    /// and it is not scheduled to be removed in the future. 
310    /// 
311    /// FileDescriptor mode: "w"
312    #[deprecated(note = "This may or may not truncate existing contents. If the new file is smaller than the old one, this may cause the file to become corrupted.")]
313    Write,
314
315    /// Opens the file in write-only mode.
316    /// The existing content is truncated (deleted), and new data is written from the beginning.
317    ///
318    /// FileDescriptor mode: "wt"
319    WriteTruncate,
320
321    /// Opens the file in write-only mode.
322    /// The existing content is preserved, and new data is appended to the end of the file.
323    /// 
324    /// FileDescriptor mode: "wa"
325    WriteAppend,
326
327    /// Opens the file in read-write mode.  
328    /// 
329    /// FileDescriptor mode: "rw"
330    ReadWrite,
331
332    /// Opens the file in read-write mode.
333    /// The existing content is truncated (deleted), and new data is written from the beginning.
334    ///
335    /// FileDescriptor mode: "rwt"
336    ReadWriteTruncate,
337}
338
339#[allow(unused)]
340#[allow(deprecated)]
341impl FileAccessMode {
342 
343    pub(crate) fn to_mode(&self) -> &'static str {
344        match self {
345            FileAccessMode::Read => "r",
346            FileAccessMode::Write => "w",
347            FileAccessMode::WriteTruncate => "wt",
348            FileAccessMode::WriteAppend => "wa",
349            FileAccessMode::ReadWriteTruncate => "rwt",
350            FileAccessMode::ReadWrite => "rw",
351        }
352    }
353
354    pub(crate) fn from_mode(mode: &str) -> Result<Self> {
355        match mode {
356            "r" => Ok(Self::Read),
357            "w" => Ok(Self::Write),
358            "wt" => Ok(Self::WriteTruncate),
359            "wa" => Ok(Self::WriteAppend),
360            "rwt" => Ok(Self::ReadWriteTruncate),
361            "rw" => Ok(Self::ReadWrite),
362            mode => Err(Error { msg: format!("Illegal mode: {mode}").into() })
363        }
364    }
365}
366
367/// Filters for VisualMediaPicker.
368#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Deserialize, Serialize)]
369#[non_exhaustive]
370pub enum VisualMediaTarget<'a> {
371
372    /// Allow only images to be selected.  
373    ImageOnly,
374
375    /// Allow only videos to be selected.  
376    VideoOnly,
377
378    /// Allow only images and videos to be selected.  
379    ImageAndVideo,
380
381    /// Allow only images or videos of specified single Mime type to be selected.  
382    ImageOrVideo {
383        mime_type: &'a str
384    }
385}
386
387/// The application specific directory.  
388#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Deserialize, Serialize)]
389#[non_exhaustive]
390pub enum PrivateDir {
391
392    /// The application specific persistent-data directory.  
393    /// 
394    /// The system prevents other apps and user from accessing these locations. 
395    /// In cases where the device is rooted or the user has special permissions, the user may be able to access this.   
396    ///  
397    /// These files will be deleted when the app is uninstalled and may also be deleted at the user’s request.  
398    /// 
399    /// e.g. `/data/user/0/{app-package-name}/files`
400    Data,
401
402    /// The application specific cache directory.  
403    /// 
404    /// The system prevents other apps and user from accessing these locations. 
405    /// In cases where the device is rooted or the user has special permissions, the user may be able to access this.   
406    /// 
407    /// These files will be deleted when the app is uninstalled and may also be deleted at the user’s request. 
408    /// In addition, the system will automatically delete files in this directory as disk space is needed elsewhere on the device.  
409    /// 
410    /// e.g. `/data/user/0/{app-package-name}/cache`
411    Cache,
412}
413
414#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Deserialize, Serialize)]
415#[non_exhaustive]
416pub enum PublicDir {
417    
418    #[serde(untagged)]
419    Image(PublicImageDir),
420
421    #[serde(untagged)]
422    Video(PublicVideoDir),
423
424    #[serde(untagged)]
425    Audio(PublicAudioDir),
426
427    #[serde(untagged)]
428    GeneralPurpose(PublicGeneralPurposeDir),
429}
430
431/// Directory in which to place images that are available to other applications and users.  
432#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Deserialize, Serialize)]
433#[non_exhaustive]
434pub enum PublicImageDir {
435
436    /// Standard directory in which to place pictures that are available to the user.  
437    /// 
438    /// e.g. `~/Pictures`
439    Pictures,
440
441    /// The traditional location for pictures and videos when mounting the device as a camera.  
442    /// 
443    /// e.g. `~/DCIM`
444    DCIM,
445}
446
447/// Directory in which to place videos that are available to other applications and users.  
448#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Deserialize, Serialize)]
449#[non_exhaustive]
450pub enum PublicVideoDir {
451
452	/// Standard directory in which to place movies that are available to the user.  
453	/// 
454	/// e.g. `~/Movies`
455	Movies,
456
457	/// The traditional location for pictures and videos when mounting the device as a camera.  
458	/// 
459	/// e.g. `~/DCIM`
460	DCIM,
461}
462
463/// Directory in which to place audios that are available to other applications and users.  
464#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Deserialize, Serialize)]
465#[non_exhaustive]
466pub enum PublicAudioDir {
467
468    /// Standard directory in which to place movies that are available to the user.  
469    /// 
470    /// e.g. `~/Music`
471    Music,
472
473    /// Standard directory in which to place any audio files that should be in the list of alarms that the user can select (not as regular music).  
474    /// 
475    /// e.g. `~/Alarms`
476    Alarms,
477
478    /// Standard directory in which to place any audio files that should be in the list of audiobooks that the user can select (not as regular music).  
479    /// 
480    /// This is not available on Android 9 (API level 28) and lower.  
481    /// 
482    /// e.g. `~/Audiobooks`  
483    Audiobooks,
484
485    /// Standard directory in which to place any audio files that should be in the list of notifications that the user can select (not as regular music).  
486    /// 
487    /// e.g. `~/Notifications`
488    Notifications,
489
490    /// Standard directory in which to place any audio files that should be in the list of podcasts that the user can select (not as regular music).  
491    /// 
492    /// e.g. `~/Podcasts`
493    Podcasts,
494
495    /// Standard directory in which to place any audio files that should be in the list of ringtones that the user can select (not as regular music).  
496    /// 
497    /// e.g. `~/Ringtones`
498    Ringtones,
499
500    /// Standard directory in which to place any audio files that should be in the list of voice recordings recorded by voice recorder apps that the user can select (not as regular music).   
501    /// 
502    /// This is not available on Android 11 (API level 30) and lower.  
503    /// 
504    /// e.g. `~/Recordings`
505    Recordings,
506}
507
508/// Directory in which to place files that are available to other applications and users.  
509#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Deserialize, Serialize)]
510#[non_exhaustive]
511pub enum PublicGeneralPurposeDir {
512
513    /// Standard directory in which to place documents that have been created by the user.  
514    /// 
515    /// e.g. `~/Documents`
516    Documents,
517
518    /// Standard directory in which to place files that have been downloaded by the user.  
519    /// 
520    /// e.g. `~/Download`  
521    ///
522    /// This is not the plural "Downloads", but the singular "Download".
523    /// <https://developer.android.com/reference/android/os/Environment#DIRECTORY_DOWNLOADS>
524    Download,
525}
526
527impl std::fmt::Display for PublicImageDir {
528    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
529        match self {
530            PublicImageDir::Pictures => write!(f, "Pictures"),
531            PublicImageDir::DCIM => write!(f, "DCIM"),
532        }
533    }
534}
535
536impl std::fmt::Display for PublicVideoDir {
537    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
538        match self {
539            PublicVideoDir::Movies => write!(f, "Movies"),
540            PublicVideoDir::DCIM => write!(f, "DCIM"),
541        }
542    }
543}
544
545impl std::fmt::Display for PublicAudioDir {
546    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
547        match self {
548            PublicAudioDir::Music => write!(f, "Music"),
549            PublicAudioDir::Alarms => write!(f, "Alarms"),
550            PublicAudioDir::Audiobooks => write!(f, "Audiobooks"),
551            PublicAudioDir::Notifications => write!(f, "Notifications"),
552            PublicAudioDir::Podcasts => write!(f, "Podcasts"),
553            PublicAudioDir::Ringtones => write!(f, "Ringtones"),
554            PublicAudioDir::Recordings => write!(f, "Recordings"),
555        }
556    }
557}
558
559impl std::fmt::Display for PublicGeneralPurposeDir {
560    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
561        match self {
562            PublicGeneralPurposeDir::Documents => write!(f, "Documents"),
563            PublicGeneralPurposeDir::Download => write!(f, "Download"),
564        }
565    }
566}
567
568impl std::fmt::Display for PublicDir {
569    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
570        match self {
571            PublicDir::Image(p) => p.fmt(f),
572            PublicDir::Video(p) => p.fmt(f),
573            PublicDir::Audio(p) => p.fmt(f),
574            PublicDir::GeneralPurpose(p) => p.fmt(f),
575        }
576    }
577}
578
579macro_rules! impl_into_pubdir {
580    ($target: ident, $wrapper: ident) => {
581        impl From<$target> for PublicDir {
582            fn from(value: $target) -> Self {
583                Self::$wrapper(value)
584            }
585        }
586    };
587}
588impl_into_pubdir!(PublicImageDir, Image);
589impl_into_pubdir!(PublicVideoDir, Video);
590impl_into_pubdir!(PublicAudioDir, Audio);
591impl_into_pubdir!(PublicGeneralPurposeDir, GeneralPurpose);