Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add directory related functions to AndroidAssetReader #11495

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
64 changes: 52 additions & 12 deletions crates/bevy_asset/src/io/android.rs
@@ -1,16 +1,16 @@
use crate::io::{
get_meta_path, AssetReader, AssetReaderError, EmptyPathStream, PathStream, Reader, VecReader,
};
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:
///
/// - [`load_path`](AssetIo::load_path) uses the [`AssetManager`] to load files.
/// - [`read_directory`](AssetIo::read_directory) always returns an empty iterator.
/// - 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
Expand Down Expand Up @@ -56,18 +56,58 @@ impl AssetReader for AndroidAssetReader {

fn read_directory<'a>(
&'a self,
_path: &'a Path,
path: &'a Path,
) -> BoxedFuture<'a, Result<Box<PathStream>, AssetReaderError>> {
let stream: Box<PathStream> = Box::new(EmptyPathStream);
error!("Reading directories is not supported with the AndroidAssetReader");
Box::pin(async move { Ok(stream) })
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())
esensar marked this conversation as resolved.
Show resolved Hide resolved
.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,
path: &'a Path,
) -> BoxedFuture<'a, std::result::Result<bool, AssetReaderError>> {
error!("Reading directories is not supported with the AndroidAssetReader");
Box::pin(async move { Ok(false) })
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 _ = asset_manager
.open_dir(&CString::new(path.to_str().unwrap()).unwrap())
.ok_or(AssetReaderError::NotFound(path.to_path_buf()))?;
Ok(asset_manager
.open(&CString::new(path.to_str().unwrap()).unwrap())
.is_none())
esensar marked this conversation as resolved.
Show resolved Hide resolved
})
}
}