/
android.rs
112 lines (105 loc) · 4.67 KB
/
android.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
use crate::io::{get_meta_path, AssetReader, AssetReaderError, PathStream, Reader, VecReader};
use bevy_utils::tracing::error;
use bevy_utils::BoxedFuture;
use futures_lite::stream;
use std::{ffi::CString, path::Path};
/// [`AssetReader`] implementation for Android devices, built on top of Android's [`AssetManager`].
///
/// Implementation details:
///
/// - All functions use the [`AssetManager`] to load files.
/// - [`is_directory`](AssetReader::is_directory) tries to open the path
/// as a normal file and treats an error as if the path is a directory.
/// - Watching for changes is not supported. The watcher method will do nothing.
///
/// [AssetManager]: https://developer.android.com/reference/android/content/res/AssetManager
pub struct AndroidAssetReader;
impl AssetReader for AndroidAssetReader {
fn read<'a>(
&'a self,
path: &'a Path,
) -> BoxedFuture<'a, Result<Box<Reader<'a>>, AssetReaderError>> {
Box::pin(async move {
let asset_manager = bevy_winit::ANDROID_APP
.get()
.expect("Bevy must be setup with the #[bevy_main] macro on Android")
.asset_manager();
let mut opened_asset = asset_manager
.open(&CString::new(path.to_str().unwrap()).unwrap())
.ok_or(AssetReaderError::NotFound(path.to_path_buf()))?;
let bytes = opened_asset.buffer()?;
let reader: Box<Reader> = Box::new(VecReader::new(bytes.to_vec()));
Ok(reader)
})
}
fn read_meta<'a>(
&'a self,
path: &'a Path,
) -> BoxedFuture<'a, Result<Box<Reader<'a>>, AssetReaderError>> {
Box::pin(async move {
let meta_path = get_meta_path(path);
let asset_manager = bevy_winit::ANDROID_APP
.get()
.expect("Bevy must be setup with the #[bevy_main] macro on Android")
.asset_manager();
let mut opened_asset = asset_manager
.open(&CString::new(meta_path.to_str().unwrap()).unwrap())
.ok_or(AssetReaderError::NotFound(meta_path))?;
let bytes = opened_asset.buffer()?;
let reader: Box<Reader> = Box::new(VecReader::new(bytes.to_vec()));
Ok(reader)
})
}
fn read_directory<'a>(
&'a self,
path: &'a Path,
) -> BoxedFuture<'a, Result<Box<PathStream>, AssetReaderError>> {
Box::pin(async move {
let asset_manager = bevy_winit::ANDROID_APP
.get()
.expect("Bevy must be setup with the #[bevy_main] macro on Android")
.asset_manager();
let opened_assets_dir = asset_manager
.open_dir(&CString::new(path.to_str().unwrap()).unwrap())
.ok_or(AssetReaderError::NotFound(path.to_path_buf()))?;
let mapped_stream = opened_assets_dir
.filter_map(move |f| {
let file_path = path.join(Path::new(f.to_str().unwrap()));
// filter out meta files as they are not considered assets
if let Some(ext) = file_path.extension().and_then(|e| e.to_str()) {
if ext.eq_ignore_ascii_case("meta") {
return None;
}
}
Some(file_path.to_owned())
})
.collect::<Vec<_>>();
let read_dir: Box<PathStream> = Box::new(stream::iter(mapped_stream));
Ok(read_dir)
})
}
fn is_directory<'a>(
&'a self,
path: &'a Path,
) -> BoxedFuture<'a, std::result::Result<bool, AssetReaderError>> {
Box::pin(async move {
let asset_manager = bevy_winit::ANDROID_APP
.get()
.expect("Bevy must be setup with the #[bevy_main] macro on Android")
.asset_manager();
// HACK: `AssetManager` does not provide a way to check if path
// points to a directory or a file
// `open_dir` succeeds for both files and directories and will only
// fail if the path does not exist at all
// `open` will fail for directories, but it will work for files
// The solution here was to first use `open_dir` to eliminate the case
// when the path does not exist at all, and then to use `open` to
// see if that path is a file or a directory
let path = CString::new(path.to_str().unwrap()).unwrap();
let _ = asset_manager
.open_dir(&path)
.ok_or(AssetReaderError::NotFound(path.to_path_buf()))?;
Ok(asset_manager.open(&path).is_none())
})
}
}