Skip to content

Commit

Permalink
Add features to switch NativeActivity and GameActivity usage (#12095
Browse files Browse the repository at this point in the history
)

# Objective

Add two features to switch bevy to use `NativeActivity` or
`GameActivity` on Android, use `GameActivity` by default.

Also close  #12058 and probably #12026 .

## Solution

Add two features to the corresponding crates so you can toggle it, like
what `winit` and `android-activity` crate did.

---

## Changelog

Removed default `NativeActivity` feature implementation for Android,
added two new features to enable `NativeActivity` and `GameActivity`,
and use `GameActivity` by default.

## Migration Guide

Because `cargo-apk` is not compatible with `GameActivity`,
building/running using `cargo apk build/run -p bevy_mobile_example` is
no longer possible.
Users should follow the new workflow described in document.

---------

Co-authored-by: François Mockers <francois.mockers@vleue.com>
Co-authored-by: BD103 <59022059+BD103@users.noreply.github.com>
Co-authored-by: Rich Churcher <rich.churcher@gmail.com>
  • Loading branch information
4 people authored Oct 1, 2024
1 parent f53af28 commit e924df0
Show file tree
Hide file tree
Showing 38 changed files with 1,219 additions and 69 deletions.
21 changes: 15 additions & 6 deletions .github/workflows/daily.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,23 +46,32 @@ jobs:

- uses: dtolnay/rust-toolchain@stable

- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'

- name: Add Android targets
run: rustup target add aarch64-linux-android armv7-linux-androideabi
run: rustup target add aarch64-linux-android

- name: Install Cargo APK
run: cargo install --force cargo-apk
- name: Install Cargo NDK
run: cargo install --force cargo-ndk

- name: Build app for Android
run: ANDROID_NDK_ROOT=$ANDROID_NDK_LATEST_HOME cargo apk build --package bevy_mobile_example
- name: Build .so file
run: cargo ndk -t arm64-v8a -o android_example/app/src/main/jniLibs build --package bevy_mobile_example
env:
# This will reduce the APK size from 1GB to ~200MB
CARGO_PROFILE_DEV_DEBUG: false

- name: Build app for Android
run: cd examples/mobile/android_example && chmod +x gradlew && ./gradlew build

- name: Upload to Browser Stack
run: |
curl -u "${{ secrets.BROWSERSTACK_USERNAME }}:${{ secrets.BROWSERSTACK_ACCESS_KEY }}" \
-X POST "https://api-cloud.browserstack.com/app-automate/upload" \
-F "file=@target/debug/apk/bevyexample.apk" \
-F "file=@app/build/outputs/apk/debug/app-debug.apk" \
-F "custom_id=$GITHUB_RUN_ID"
nonce:
Expand Down
19 changes: 14 additions & 5 deletions .github/workflows/validation-jobs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ jobs:

- uses: dtolnay/rust-toolchain@stable

- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'

- uses: actions/cache@v4
with:
path: |
Expand All @@ -60,13 +66,16 @@ jobs:
key: ${{ runner.os }}-cargo-build-android-${{ hashFiles('**/Cargo.toml') }}

- name: Install Android targets
run: rustup target add aarch64-linux-android armv7-linux-androideabi
run: rustup target add aarch64-linux-android

- name: Install Cargo NDK
run: cargo install --force cargo-ndk

- name: Install Cargo APK
run: cargo install --force cargo-apk
- name: Build .so file
run: cargo ndk -t arm64-v8a -o android_example/app/src/main/jniLibs build --package bevy_mobile_example

- name: Build APK
run: ANDROID_NDK_ROOT=$ANDROID_NDK_LATEST_HOME cargo apk build --package bevy_mobile_example
- name: Build app for Android
run: cd examples/mobile/android_example && chmod +x gradlew && ./gradlew build

run-examples-linux-vulkan:
if: ${{ github.event_name == 'merge_group' }}
Expand Down
7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ default = [
"default_font",
"webgl2",
"sysinfo_plugin",
"android-game-activity",
]

# Provides an implementation for picking sprites
Expand Down Expand Up @@ -327,6 +328,12 @@ wayland = ["bevy_internal/wayland"]
# X11 display server support
x11 = ["bevy_internal/x11"]

# Android NativeActivity support. Legacy, should be avoided for most new Android games.
android-native-activity = ["bevy_internal/android-native-activity"]

# Android GameActivity support. Default, choose between this and `android-native-activity`.
android-game-activity = ["bevy_internal/android-game-activity"]

# Enable systems that allow for automated testing on CI
bevy_ci_testing = ["bevy_internal/bevy_ci_testing"]

Expand Down
4 changes: 4 additions & 0 deletions crates/bevy_internal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ async-io = ["bevy_tasks/async-io"]
wayland = ["bevy_winit/wayland"]
x11 = ["bevy_winit/x11"]

# Android activity support (choose one)
android-native-activity = ["bevy_winit/android-native-activity"]
android-game-activity = ["bevy_winit/android-game-activity"]

# Transmission textures in `StandardMaterial`:
pbr_transmission_textures = [
"bevy_pbr?/pbr_transmission_textures",
Expand Down
10 changes: 4 additions & 6 deletions crates/bevy_winit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ trace = []
wayland = ["winit/wayland", "winit/wayland-csd-adwaita"]
x11 = ["winit/x11"]
accesskit_unix = ["accesskit_winit/accesskit_unix", "accesskit_winit/async-io"]

serialize = ["serde", "bevy_input/serialize", "bevy_window/serialize"]
android-native-activity = ["winit/android-native-activity"]
android-game-activity = ["winit/android-game-activity"]


[dependencies]
# bevy
Expand Down Expand Up @@ -41,12 +45,6 @@ cfg-if = "1.0"
raw-window-handle = "0.6"
serde = { version = "1.0", features = ["derive"], optional = true }

[target.'cfg(target_os = "android")'.dependencies]
winit = { version = "0.30", default-features = false, features = [
"android-native-activity",
"rwh_06",
] }

[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = { version = "0.2" }
web-sys = "0.3"
Expand Down
60 changes: 42 additions & 18 deletions docs-template/EXAMPLE_README.md.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ git checkout v0.4.0
- [Android](#android)
- [Setup](#setup)
- [Build & Run](#build--run)
- [About `libc++_shared.so`](#about-libc_sharedso)
- [Old phones](#old-phones)
- [About `cargo-apk`](#about-cargo-apk)
- [iOS](#ios)
- [Setup](#setup-1)
- [Build & Run](#build--run-1)
Expand Down Expand Up @@ -89,33 +91,51 @@ Example | Description
### Setup

```sh
rustup target add aarch64-linux-android armv7-linux-androideabi
cargo install cargo-apk
rustup target add aarch64-linux-android
cargo install cargo-ndk
```

The Android SDK must be installed, and the environment variable `ANDROID_SDK_ROOT` set to the root Android `sdk` folder.

When using `NDK (Side by side)`, the environment variable `ANDROID_NDK_ROOT` must also be set to one of the NDKs in `sdk\ndk\[NDK number]`.

Alternatively, you can install Android Studio.

### Build & Run

To run on a device setup for Android development, run:
To build an Android app, you first need to build shared object files for the target architecture with `cargo-ndk`:

```sh
cargo apk run -p bevy_mobile_example
cargo ndk -t <target_name> build -o <project_name>/app/src/main/jniLibs
```

When using Bevy as a library, the following fields must be added to `Cargo.toml`:
For example, to compile to a 64-bit ARM platform:

```toml
[package.metadata.android]
build_targets = ["aarch64-linux-android", "armv7-linux-androideabi"]
```sh
cargo ndk -t arm64-v8a build -o android_example/app/src/main/jniLibs
```

[package.metadata.android.sdk]
target_sdk_version = 31
Setting the output path ensures the shared object files can be found in target-specific directories under `jniLibs` where the JNI can find them.

See the `cargo-ndk` [README](https://crates.io/crates/cargo-ndk) for other options.

After this you can build it with `gradlew`:

```sh
./gradlew build
```

Please reference `cargo-apk` [README](https://crates.io/crates/cargo-apk) for other Android Manifest fields.
Or build it with Android Studio.

Then you can test it in your Android project.

#### About `libc++_shared.so`

Bevy may require `libc++_shared.so` to run on Android, as it is needed by the `oboe` crate, but typically `cargo-ndk` does not copy this file automatically.

To include it, you can manually obtain it from NDK source or use a `build.rs` script for automation, as described in the `cargo-ndk` [README](https://github.com/bbqsrc/cargo-ndk?tab=readme-ov-file#linking-against-and-copying-libc_sharedso-into-the-relevant-places-in-the-output-directory).

Alternatively, you can modify project files to include it when building an APK. To understand the specific steps taken in this project, please refer to the comments within the project files for detailed instructions(`app/CMakeList.txt`, `app/build.gradle`, `app/src/main/cpp/dummy.cpp`).

### Debugging

Expand All @@ -135,18 +155,22 @@ adb uninstall org.bevyengine.example

### Old phones

Bevy by default targets Android API level 31 in its examples which is the <!-- markdown-link-check-disable -->
[Play Store's minimum API to upload or update apps](https://developer.android.com/distribute/best-practices/develop/target-sdk). <!-- markdown-link-check-enable -->
Users of older phones may want to use an older API when testing.
In its examples, Bevy targets the minimum Android API that Play Store <!-- markdown-link-check-disable -->
[requires](https://developer.android.com/distribute/best-practices/develop/target-sdk) to upload and update apps. <!-- markdown-link-check-enable -->
Users of older phones may want to use an older API when testing. By default, Bevy uses [`GameAvtivity`](https://developer.android.com/games/agdk/game-activity), which only works for Android API level 31 and higher, so if you want to use older API, you need to switch to `NativeActivity`.

To use a different API, the following fields must be updated in Cargo.toml:
To use `NativeActivity`, you need to edit it in `cargo.toml` manually like this:

```toml
[package.metadata.android.sdk]
target_sdk_version = >>API<<
min_sdk_version = >>API or less<<
bevy = { version = "0.14", default-features = false, features = ["android-native-activity", ...] }
```

Then build it as the [Build & Run](#build--run) section stated above.

#### About `cargo-apk`

You can also build an APK with `cargo-apk`, a simpler and deprecated tool which doesn't support `GameActivity`. If you want to use this, there is a [folder](./mobile/android_basic) inside the mobile example with instructions.

Example | File | Description
--- | --- | ---
`android` | [`mobile/src/lib.rs`](./mobile/src/lib.rs) | A 3d Scene with a button and playing sound
Expand Down
2 changes: 2 additions & 0 deletions docs/cargo_features.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ The default feature set enables most of the expected features of a game engine,

|feature name|description|
|-|-|
|android-game-activity|Android GameActivity support. Default, choose between this and `android-native-activity`.|
|android_shared_stdcxx|Enable using a shared stdlib for cxx on Android|
|animation|Enable animation support, and glTF animation loading|
|bevy_animation|Provides animation functionality|
Expand Down Expand Up @@ -51,6 +52,7 @@ The default feature set enables most of the expected features of a game engine,
|feature name|description|
|-|-|
|accesskit_unix|Enable AccessKit on Unix backends (currently only works with experimental screen readers and forks.)|
|android-native-activity|Android NativeActivity support. Legacy, should be avoided for most new Android games.|
|asset_processor|Enables the built-in asset processor for processed assets.|
|async-io|Use async-io's implementation of block_on instead of futures-lite's implementation. This is preferred if your application uses async-io.|
|basis-universal|Basis Universal compressed texture support|
Expand Down
60 changes: 42 additions & 18 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ git checkout v0.4.0
- [Android](#android)
- [Setup](#setup)
- [Build & Run](#build--run)
- [About `libc++_shared.so`](#about-libc_sharedso)
- [Old phones](#old-phones)
- [About `cargo-apk`](#about-cargo-apk)
- [iOS](#ios)
- [Setup](#setup-1)
- [Build & Run](#build--run-1)
Expand Down Expand Up @@ -543,33 +545,51 @@ Example | Description
### Setup

```sh
rustup target add aarch64-linux-android armv7-linux-androideabi
cargo install cargo-apk
rustup target add aarch64-linux-android
cargo install cargo-ndk
```

The Android SDK must be installed, and the environment variable `ANDROID_SDK_ROOT` set to the root Android `sdk` folder.

When using `NDK (Side by side)`, the environment variable `ANDROID_NDK_ROOT` must also be set to one of the NDKs in `sdk\ndk\[NDK number]`.

Alternatively, you can install Android Studio.

### Build & Run

To run on a device setup for Android development, run:
To build an Android app, you first need to build shared object files for the target architecture with `cargo-ndk`:

```sh
cargo apk run -p bevy_mobile_example
cargo ndk -t <target_name> build -o <project_name>/app/src/main/jniLibs
```

When using Bevy as a library, the following fields must be added to `Cargo.toml`:
For example, to compile to a 64-bit ARM platform:

```toml
[package.metadata.android]
build_targets = ["aarch64-linux-android", "armv7-linux-androideabi"]
```sh
cargo ndk -t arm64-v8a build -o android_example/app/src/main/jniLibs
```

[package.metadata.android.sdk]
target_sdk_version = 31
Setting the output path ensures the shared object files can be found in target-specific directories under `jniLibs` where the JNI can find them.

See the `cargo-ndk` [README](https://crates.io/crates/cargo-ndk) for other options.

After this you can build it with `gradlew`:

```sh
./gradlew build
```

Please reference `cargo-apk` [README](https://crates.io/crates/cargo-apk) for other Android Manifest fields.
Or build it with Android Studio.

Then you can test it in your Android project.

#### About `libc++_shared.so`

Bevy may require `libc++_shared.so` to run on Android, as it is needed by the `oboe` crate, but typically `cargo-ndk` does not copy this file automatically.

To include it, you can manually obtain it from NDK source or use a `build.rs` script for automation, as described in the `cargo-ndk` [README](https://github.com/bbqsrc/cargo-ndk?tab=readme-ov-file#linking-against-and-copying-libc_sharedso-into-the-relevant-places-in-the-output-directory).

Alternatively, you can modify project files to include it when building an APK. To understand the specific steps taken in this project, please refer to the comments within the project files for detailed instructions(`app/CMakeList.txt`, `app/build.gradle`, `app/src/main/cpp/dummy.cpp`).

### Debugging

Expand All @@ -589,18 +609,22 @@ adb uninstall org.bevyengine.example

### Old phones

Bevy by default targets Android API level 31 in its examples which is the <!-- markdown-link-check-disable -->
[Play Store's minimum API to upload or update apps](https://developer.android.com/distribute/best-practices/develop/target-sdk). <!-- markdown-link-check-enable -->
Users of older phones may want to use an older API when testing.
In its examples, Bevy targets the minimum Android API that Play Store <!-- markdown-link-check-disable -->
[requires](https://developer.android.com/distribute/best-practices/develop/target-sdk) to upload and update apps. <!-- markdown-link-check-enable -->
Users of older phones may want to use an older API when testing. By default, Bevy uses [`GameAvtivity`](https://developer.android.com/games/agdk/game-activity), which only works for Android API level 31 and higher, so if you want to use older API, you need to switch to `NativeActivity`.

To use a different API, the following fields must be updated in Cargo.toml:
To use `NativeActivity`, you need to edit it in `cargo.toml` manually like this:

```toml
[package.metadata.android.sdk]
target_sdk_version = >>API<<
min_sdk_version = >>API or less<<
bevy = { version = "0.14", default-features = false, features = ["android-native-activity", ...] }
```

Then build it as the [Build & Run](#build--run) section stated above.

#### About `cargo-apk`

You can also build an APK with `cargo-apk`, a simpler and deprecated tool which doesn't support `GameActivity`. If you want to use this, there is a [folder](./mobile/android_basic) inside the mobile example with instructions.

Example | File | Description
--- | --- | ---
`android` | [`mobile/src/lib.rs`](./mobile/src/lib.rs) | A 3d Scene with a button and playing sound
Expand Down
6 changes: 6 additions & 0 deletions examples/mobile/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
/build
.gradle
.idea
.DS_Store
build
.cxx
local.properties
16 changes: 0 additions & 16 deletions examples/mobile/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,5 @@ bevy = { path = "../../" }
[target.aarch64-apple-ios-sim.dependencies]
bevy = { path = "../../", features = ["ios_simulator"] }

[package.metadata.android]
package = "org.bevyengine.example"
apk_name = "bevyexample"
assets = "../../assets"
resources = "../../assets/android-res"
# This strips debug symbols from the shared libraries, drastically reducing APK size. If you need them, remove the option.
strip = "strip"
build_targets = ["aarch64-linux-android", "armv7-linux-androideabi"]

[package.metadata.android.sdk]
target_sdk_version = 31

[package.metadata.android.application]
icon = "@mipmap/ic_launcher"
label = "Bevy Example"

[lints]
workspace = true
Loading

0 comments on commit e924df0

Please sign in to comment.