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

implement read_directory and is_directory for android asset reader #9969

Closed
wants to merge 4 commits into from

Conversation

deltanedas
Copy link

@deltanedas deltanedas commented Sep 29, 2023

Objective

Fixes #9968

Solution

Uses open_dir to read files.
It's collected in a vector since the AssetDir cant be sent between threads.

For is_directory it uses a silly hack (trying to open it then if it succeeds it must be a directory) seems there is no better way.

It also ignores meta like the filesystem implementation does, no idea if this is needed on android.


Changelog

  • Fixed: Android's AssetReader implementation now supports read_directory and is_directory.

@github-actions
Copy link
Contributor

Welcome, new contributor!

Please make sure you've read our contributing guide and we look forward to reviewing your pull request shortly ✨

@alice-i-cecile alice-i-cecile added A-Assets Load files from disk to use for things like images, models, and sounds O-Android Specific to the Android mobile operating system C-Usability A targeted quality-of-life change that makes Bevy easier to use labels Sep 29, 2023
@alice-i-cecile
Copy link
Member

Do we have a way to unit test this? It's surprisingly complex for a "read files" operation, and I'd like to ensure it keeps working.

@deltanedas
Copy link
Author

deltanedas commented Sep 29, 2023

I don't know how bevy does tests but I imagine assets.load_folder("/") then get its LoadedFolder and assert!(!folder.handles.is_empty(), "Loading folder failed") would do the job (might also automatically see if loading assets fails im not sure)

@deltanedas
Copy link
Author

also this will require an is_directory implementation of some kind, it seems that it still refuses to work without it

@mockersf
Copy link
Member

Do we have a way to unit test this? It's surprisingly complex for a "read files" operation, and I'd like to ensure it keeps working.

probably not easily, you need to be in an Android environment...

also this will require an is_directory implementation of some kind, it seems that it still refuses to work without it

Yup, you need to implement it... looking at the asset manager doc, it seems your "silly hack" is the best we can do

@deltanedas deltanedas changed the title implement read_directory for android asset reader implement read_directory and is_directory for android asset reader Sep 30, 2023
@deltanedas
Copy link
Author

ok this will definitely require a test, on my device apparently after the folder's loadstate is loaded it has 0 handles???

@alice-i-cecile alice-i-cecile added the D-Complex Quite challenging from either a design or technical perspective. Ask for help! label Sep 30, 2023
@esensar
Copy link
Contributor

esensar commented Jan 23, 2024

ok this will definitely require a test, on my device apparently after the folder's loadstate is loaded it has 0 handles???

I have tried working on this, not knowing about this PR (#11495). That happened to me when I used open_dir for is_directory, since open_dir works on files too for some reason (and treats them as empty directories). I changed it to open and I tested it in mobile example and it returns all assets in the directory as expected.

@deltanedas
Copy link
Author

yeah closing in favour of your one

@deltanedas deltanedas closed this Jan 23, 2024
@deltanedas deltanedas deleted the android-read-directory branch January 23, 2024 15:56
github-merge-queue bot pushed a commit that referenced this pull request Oct 1, 2024
# Objective

- Fixes #9968

## Solution

- Uses
[open_dir](https://docs.rs/ndk/latest/ndk/asset/struct.AssetManager.html#method.open_dir)
to read directories and collects child list, since it can't be shared
across threads.
- For `is_directory`, uses result of
[open](https://docs.rs/ndk/latest/ndk/asset/struct.AssetManager.html#method.open),
which will fail for directories. I tried using the result of `open_dir`
for this, but it was successful for files too, which made loading
folders return empty lists, since `open_dir` was successful and treated
all files as empty directories.
- Ignoring `meta` files was copied from filesystem implementation

---

## Changelog

- Fixed: Android's AssetReader implementation now supports
read_directory and is_directory.

## Notes

I noticed late that there was the #9968 issue (I only noticed #9591), so
I have also missed that a PR was already open (#9969). Feel free to copy
over the fixes from this one over there.
The only difference I notice between these 2, is that I have used `open`
instead of `open_dir` for `is_directory` implementation. I have tried
with `open_dir` too, but unfortunately that didn't work. I tested this
on an actual device, using the mobile example, by making some minor
changes:

```rust
#[derive(Resource)]
struct FolderAssets(Handle<LoadedFolder>);

// the `bevy_main` proc_macro generates the required boilerplate for iOS and Android
#[bevy_main]
fn main() {
    // ...
    .add_systems(Startup, (setup_scene, load_music_files))
    .add_systems(
        Update,
        // Removed the handle_lifetime since AudioBundle is added later
        (touch_camera, button_handler, setup_music),
    );
   // ...
}

fn load_music_files(asset_server: Res<AssetServer>, mut commands: Commands) {
    let sounds = asset_server.load_folder("sounds");
    commands.insert_resource(FolderAssets(sounds));
}

fn setup_music(
    mut commands: Commands,
    folders: Res<Assets<LoadedFolder>>,
    mut loaded_event: EventReader<AssetEvent<LoadedFolder>>,
) {
    for event in loaded_event.read() {
        if let AssetEvent::LoadedWithDependencies { id } = event {
            if let Some(folder) = folders.get(*id) {
                warn!("Folder items: {:?}", folder.handles);
                if let Some(source) = folder.handles.first() {
                    commands.spawn(AudioBundle {
                        source: source.clone().typed::<AudioSource>(),
                        settings: PlaybackSettings::LOOP,
                    });
                }
            }
        }
    }
}
```

---------

Co-authored-by: Kanabenki <[email protected]>
Co-authored-by: François Mockers <[email protected]>
robtfm pushed a commit to robtfm/bevy that referenced this pull request Oct 4, 2024
…1495)

# Objective

- Fixes bevyengine#9968

## Solution

- Uses
[open_dir](https://docs.rs/ndk/latest/ndk/asset/struct.AssetManager.html#method.open_dir)
to read directories and collects child list, since it can't be shared
across threads.
- For `is_directory`, uses result of
[open](https://docs.rs/ndk/latest/ndk/asset/struct.AssetManager.html#method.open),
which will fail for directories. I tried using the result of `open_dir`
for this, but it was successful for files too, which made loading
folders return empty lists, since `open_dir` was successful and treated
all files as empty directories.
- Ignoring `meta` files was copied from filesystem implementation

---

## Changelog

- Fixed: Android's AssetReader implementation now supports
read_directory and is_directory.

## Notes

I noticed late that there was the bevyengine#9968 issue (I only noticed bevyengine#9591), so
I have also missed that a PR was already open (bevyengine#9969). Feel free to copy
over the fixes from this one over there.
The only difference I notice between these 2, is that I have used `open`
instead of `open_dir` for `is_directory` implementation. I have tried
with `open_dir` too, but unfortunately that didn't work. I tested this
on an actual device, using the mobile example, by making some minor
changes:

```rust
#[derive(Resource)]
struct FolderAssets(Handle<LoadedFolder>);

// the `bevy_main` proc_macro generates the required boilerplate for iOS and Android
#[bevy_main]
fn main() {
    // ...
    .add_systems(Startup, (setup_scene, load_music_files))
    .add_systems(
        Update,
        // Removed the handle_lifetime since AudioBundle is added later
        (touch_camera, button_handler, setup_music),
    );
   // ...
}

fn load_music_files(asset_server: Res<AssetServer>, mut commands: Commands) {
    let sounds = asset_server.load_folder("sounds");
    commands.insert_resource(FolderAssets(sounds));
}

fn setup_music(
    mut commands: Commands,
    folders: Res<Assets<LoadedFolder>>,
    mut loaded_event: EventReader<AssetEvent<LoadedFolder>>,
) {
    for event in loaded_event.read() {
        if let AssetEvent::LoadedWithDependencies { id } = event {
            if let Some(folder) = folders.get(*id) {
                warn!("Folder items: {:?}", folder.handles);
                if let Some(source) = folder.handles.first() {
                    commands.spawn(AudioBundle {
                        source: source.clone().typed::<AudioSource>(),
                        settings: PlaybackSettings::LOOP,
                    });
                }
            }
        }
    }
}
```

---------

Co-authored-by: Kanabenki <[email protected]>
Co-authored-by: François Mockers <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Assets Load files from disk to use for things like images, models, and sounds C-Usability A targeted quality-of-life change that makes Bevy easier to use D-Complex Quite challenging from either a design or technical perspective. Ask for help! O-Android Specific to the Android mobile operating system
Projects
None yet
Development

Successfully merging this pull request may close these issues.

directories support on android
4 participants