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