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

Merged
merged 13 commits into from
Oct 1, 2024
Merged
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 52 additions & 13 deletions crates/bevy_asset/src/io/android.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
use crate::io::{
get_meta_path, AssetReader, AssetReaderError, EmptyPathStream, PathStream, Reader, VecReader,
};
use bevy_log::error;
use crate::io::{get_meta_path, AssetReader, AssetReaderError, PathStream, Reader, VecReader};
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 +55,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())
.ok_or(AssetReaderError::NotFound(path.to_path_buf()))?;
esensar marked this conversation as resolved.
Show resolved Hide resolved

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())
.map_or(true, |_| false))
})
esensar marked this conversation as resolved.
Show resolved Hide resolved
esensar marked this conversation as resolved.
Show resolved Hide resolved
}
}
Loading