diff --git a/crates/bevy_asset/src/io/android.rs b/crates/bevy_asset/src/io/android.rs index 18daac5949645..6f9feaf5a15e6 100644 --- a/crates/bevy_asset/src/io/android.rs +++ b/crates/bevy_asset/src/io/android.rs @@ -1,15 +1,15 @@ -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 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 @@ -45,18 +45,53 @@ impl AssetReader for AndroidAssetReader { async fn read_directory<'a>( &'a self, - _path: &'a Path, + path: &'a Path, ) -> Result, AssetReaderError> { - let stream: Box = Box::new(EmptyPathStream); - error!("Reading directories is not supported with the AndroidAssetReader"); - Ok(stream) + 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::>(); + + let read_dir: Box = Box::new(stream::iter(mapped_stream)); + Ok(read_dir) } async fn is_directory<'a>( &'a self, - _path: &'a Path, + path: &'a Path, ) -> std::result::Result { - error!("Reading directories is not supported with the AndroidAssetReader"); - Ok(false) + 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()) } }