Skip to content

Commit 9ad8fb2

Browse files
committed
examples: Use single up-to-date android example
Objective - Only use a single modern and better documented android example - Fix #10945 - Fix #17122 Problems with current examples - We are currently using deprecated functions like for example [setSystemUiVisibility(int)](https://developer.android.com/reference/android/view/View#setSystemUiVisibility(int)). - Most Android dependencies are very out of date. - Kotlin is the [preffered language](https://developer.android.com/kotlin/first) for Android and we should migrate the examples to it. - Most of the code in the examples is not inherently clear and there are very few comments especially in the build scripts. - We should only have a single Android example using `games-activity` and `cargo-ndk`. It is confusing and unnecessary to do it any other way in my opinion. - The crash from #10945 is fixed by migrating to `games-activity` 4. Before this PR, we were using `games-activity` 2. #17122 is apparently also fixed because of this. Solution - Replace all android examples with `./examples/mobile/android` - Update dependencies in `libs.versions.toml` - Add `gradle-daemon-jvm.properties` generated with `./gradlew updateDaemonJvm` - Update gradle wrapper - Migrate everything to kotlin/kotlin build scripts - Use supported functions instead of deprecated ones - Move `/assets/ic_launcher.png` to `/examples/mobile/android/app/src/main/res/mipmap/ic_launcher.png` because that is where it is [meant to be stored](https://developer.android.com/guide/topics/resources/providing-resources). - Use `--link-libcxx-shared` flag for `cargo-ndk` in `examples/README.md` - Use `targetSdk = 36` and `compileSdk = 36` and explain in `examples/README.md`
1 parent 8581bcf commit 9ad8fb2

40 files changed

Lines changed: 586 additions & 1097 deletions

.github/workflows/validation-jobs.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,11 @@ jobs:
9999
run: cargo install --force cargo-ndk
100100

101101
- name: Build .so file
102-
run: cargo ndk -t arm64-v8a -o android_example/app/src/main/jniLibs build --package bevy_mobile_example
102+
# FIXME: Remove --release flag after the issue has been resolved.
103+
run: cargo ndk build --link-libcxx-shared -t aarch64-linux-android -p bevy_mobile_example --release -o ./examples/mobile/android/app/src/main/jniLibs
103104

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

107108
run-examples-on-wasm:
108109
if: ${{ github.event_name == 'merge_group' }}

docs-template/EXAMPLE_README.md.tpl

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,8 @@ git checkout v0.4.0
4545
- [Android](#android)
4646
- [Setup](#setup)
4747
- [Build & Run](#build--run)
48-
- [About `libc++_shared.so`](#about-libc_sharedso)
48+
- [Debugging](#debugging)
4949
- [Old phones](#old-phones)
50-
- [About `cargo-apk`](#about-cargo-apk)
5150
- [iOS](#ios)
5251
- [Setup](#setup-1)
5352
- [Build & Run](#build--run-1)
@@ -104,16 +103,20 @@ Alternatively, you can install Android Studio.
104103

105104
#### Build & Run
106105

106+
<!-- FIXME: Remove --release flag and note after the issue has been resolved.-->
107+
108+
**⚠️ Note:** The `--release` flag is currently necessary for the example. You might be able to omit it when building your own crate.
109+
107110
To build an Android app, you first need to build shared object files for the target architecture with `cargo-ndk`:
108111

109112
```sh
110-
cargo ndk -t <target_name> -o <project_name>/app/src/main/jniLibs build
113+
cargo ndk build --link-libcxx-shared -t <target_name> --release -o <project_path>/android/app/src/main/jniLibs
111114
```
112115

113116
For example, to compile to a 64-bit ARM platform:
114117

115118
```sh
116-
cargo ndk -t arm64-v8a -o android_example/app/src/main/jniLibs build
119+
cargo ndk build --link-libcxx-shared -t aarch64-linux-android --release -o ./android/app/src/main/jniLibs
117120
```
118121

119122
Setting the output path ensures the shared object files can be found in target-specific directories under `jniLibs` where the JNI can find them.
@@ -123,21 +126,14 @@ See the `cargo-ndk` [README](https://crates.io/crates/cargo-ndk) for other optio
123126
After this you can build it with `gradlew`:
124127

125128
```sh
129+
cd ./android
126130
./gradlew build
127131
```
128132

129133
Or build it with Android Studio.
130134

131135
Then you can test it in your Android project.
132136

133-
##### About `libc++_shared.so`
134-
135-
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.
136-
137-
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).
138-
139-
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`).
140-
141137
#### Debugging
142138

143139
You can view the logs with the following command:
@@ -156,26 +152,18 @@ adb uninstall org.bevyengine.example
156152

157153
#### Old phones
158154

159-
In its examples, Bevy targets the minimum Android API that Play Store <!-- markdown-link-check-disable -->
160-
[requires](https://developer.android.com/distribute/best-practices/develop/target-sdk) to upload and update apps. <!-- markdown-link-check-enable -->
161-
Users of older phones may want to use an older API when testing. By default, Bevy uses [`GameActivity`](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`.
155+
In its example, Bevy targets the newest stable Android API to be able to benefit from security and performance improvements. The example also specifies Android API 31 as minimum SDK to support older devices which is recommended on [Android Developers](https://developer.android.com/google/play/requirements/target-sdk#why-target).
162156

163-
To use `NativeActivity`, you need to edit it in `cargo.toml` manually like this:
157+
Users of older phones may want to use an older API when testing. By default, Bevy uses [`GameActivity`](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`](https://developer.android.com/reference/android/app/NativeActivity).
158+
159+
To use `NativeActivity`, you need to write a custom `MainActivity.kt` using `NativeActivity` and add the `android-native-activity` feature to Bevy in your `Cargo.toml` like this:
164160

165161
```toml
166-
bevy = { version = "0.14", default-features = false, features = ["android-native-activity", ...] }
162+
bevy = { version = "*", default-features = false, features = ["android-native-activity", <...>] }
167163
```
168164

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

171-
##### About `cargo-apk`
172-
173-
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.
174-
175-
Example | File | Description
176-
--- | --- | ---
177-
`android` | [`mobile/src/lib.rs`](./mobile/src/lib.rs) | A 3d Scene with a button and playing sound
178-
179167
### iOS
180168

181169
#### Setup

examples/README.md

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,8 @@ git checkout v0.4.0
7777
- [Android](#android)
7878
- [Setup](#setup)
7979
- [Build & Run](#build--run)
80-
- [About `libc++_shared.so`](#about-libc_sharedso)
80+
- [Debugging](#debugging)
8181
- [Old phones](#old-phones)
82-
- [About `cargo-apk`](#about-cargo-apk)
8382
- [iOS](#ios)
8483
- [Setup](#setup-1)
8584
- [Build & Run](#build--run-1)
@@ -701,16 +700,20 @@ Alternatively, you can install Android Studio.
701700

702701
#### Build & Run
703702

703+
<!-- FIXME: Remove --release flag and note after the issue has been resolved.-->
704+
705+
**⚠️ Note:** The `--release` flag is currently necessary for the example. You might be able to omit it when building your own crate.
706+
704707
To build an Android app, you first need to build shared object files for the target architecture with `cargo-ndk`:
705708

706709
```sh
707-
cargo ndk -t <target_name> -o <project_name>/app/src/main/jniLibs build
710+
cargo ndk build --link-libcxx-shared -t <target_name> --release -o <project_path>/android/app/src/main/jniLibs
708711
```
709712

710713
For example, to compile to a 64-bit ARM platform:
711714

712715
```sh
713-
cargo ndk -t arm64-v8a -o android_example/app/src/main/jniLibs build
716+
cargo ndk build --link-libcxx-shared -t aarch64-linux-android --release -o ./android/app/src/main/jniLibs
714717
```
715718

716719
Setting the output path ensures the shared object files can be found in target-specific directories under `jniLibs` where the JNI can find them.
@@ -720,21 +723,14 @@ See the `cargo-ndk` [README](https://crates.io/crates/cargo-ndk) for other optio
720723
After this you can build it with `gradlew`:
721724

722725
```sh
726+
cd ./android
723727
./gradlew build
724728
```
725729

726730
Or build it with Android Studio.
727731

728732
Then you can test it in your Android project.
729733

730-
##### About `libc++_shared.so`
731-
732-
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.
733-
734-
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).
735-
736-
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`).
737-
738734
#### Debugging
739735

740736
You can view the logs with the following command:
@@ -753,26 +749,18 @@ adb uninstall org.bevyengine.example
753749

754750
#### Old phones
755751

756-
In its examples, Bevy targets the minimum Android API that Play Store <!-- markdown-link-check-disable -->
757-
[requires](https://developer.android.com/distribute/best-practices/develop/target-sdk) to upload and update apps. <!-- markdown-link-check-enable -->
758-
Users of older phones may want to use an older API when testing. By default, Bevy uses [`GameActivity`](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`.
752+
In its example, Bevy targets the newest stable Android API to be able to benefit from security and performance improvements. The example also specifies Android API 31 as minimum SDK to support older devices which is recommended on [Android Developers](https://developer.android.com/google/play/requirements/target-sdk#why-target).
759753

760-
To use `NativeActivity`, you need to edit it in `cargo.toml` manually like this:
754+
Users of older phones may want to use an older API when testing. By default, Bevy uses [`GameActivity`](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`](https://developer.android.com/reference/android/app/NativeActivity).
755+
756+
To use `NativeActivity`, you need to write a custom `MainActivity.kt` using `NativeActivity` and add the `android-native-activity` feature to Bevy in your `Cargo.toml` like this:
761757

762758
```toml
763-
bevy = { version = "0.14", default-features = false, features = ["android-native-activity", ...] }
759+
bevy = { version = "*", default-features = false, features = ["android-native-activity", <...>] }
764760
```
765761

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

768-
##### About `cargo-apk`
769-
770-
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.
771-
772-
Example | File | Description
773-
--- | --- | ---
774-
`android` | [`mobile/src/lib.rs`](./mobile/src/lib.rs) | A 3d Scene with a button and playing sound
775-
776764
### iOS
777765

778766
#### Setup
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#
2+
# https://help.github.com/articles/dealing-with-line-endings/
3+
#
4+
# Linux start script should use lf
5+
/gradlew text eol=lf
6+
7+
# These are Windows script files and should use crlf
8+
*.bat text eol=crlf
9+
10+
# Binary files should be left untouched
11+
*.jar binary
12+
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
# This is part of a hack to convince gradle to insert libc++_shared.so
2-
cmake_minimum_required(VERSION 3.4.1)
3-
project(cppshared_dummy)
4-
set (SRC_DIR ./src/main/cpp)
5-
add_library (dummy SHARED ${SRC_DIR}/dummy.cpp)
1+
# This is part of a hack to convince gradle to insert libc++_shared.so
2+
cmake_minimum_required(VERSION 3.4.1)
3+
project(cppshared_dummy)
4+
set(SRC_DIR ./src/main/cpp)
5+
add_library(dummy SHARED ${SRC_DIR}/dummy.cpp)
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
plugins {
2+
alias(libs.plugins.android.application)
3+
}
4+
5+
kotlin {
6+
compilerOptions {
7+
languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_3
8+
jvmToolchain(8)
9+
}
10+
}
11+
12+
android {
13+
namespace = "org.bevyengine.example"
14+
compileSdk = 36
15+
16+
// https://developer.android.com/reference/tools/gradle-api/9.1/com/android/build/api/dsl/DefaultConfig
17+
defaultConfig {
18+
applicationId = "org.bevyengine.example"
19+
minSdk = 31
20+
targetSdk = 36
21+
// NOTE: Increase by 1 on each release
22+
versionCode = 1
23+
// NOTE: Update with full semantic version on each release
24+
versionName = "0.0.0"
25+
// https://developer.android.com/reference/tools/gradle-api/9.1/com/android/build/api/variant/ExternalNativeBuild
26+
// NOTE: We need this, otherwise libc++_shared.so will not be inserted
27+
@Suppress("UnstableApiUsage")
28+
externalNativeBuild {
29+
cmake {
30+
arguments("-DANDROID_STL=c++_shared")
31+
}
32+
}
33+
// https://developer.android.com/reference/tools/gradle-api/9.1/com/android/build/api/dsl/Ndk
34+
ndk {
35+
abiFilters.addAll(listOf("arm64-v8a", "armeabi-v7a", "x86_64"))
36+
}
37+
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
38+
}
39+
// https://developer.android.com/reference/tools/gradle-api/9.1/com/android/build/api/dsl/ExternalNativeBuild
40+
externalNativeBuild {
41+
cmake {
42+
path = file("CMakeLists.txt")
43+
}
44+
}
45+
// https://developer.android.com/reference/tools/gradle-api/9.1/com/android/build/api/dsl/BuildType
46+
buildTypes {
47+
getByName("release") {
48+
// https://developer.android.com/topic/performance/app-optimization/enable-app-optimization
49+
isMinifyEnabled = true
50+
isShrinkResources = true
51+
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
52+
}
53+
}
54+
// https://developer.android.com/reference/tools/gradle-api/9.1/com/android/build/api/dsl/CompileOptions
55+
compileOptions {
56+
sourceCompatibility = JavaVersion.VERSION_1_8
57+
targetCompatibility = JavaVersion.VERSION_1_8
58+
}
59+
// https://developer.android.com/reference/tools/gradle-api/9.1/com/android/build/api/dsl/BuildFeatures
60+
buildFeatures {
61+
prefab = true
62+
}
63+
packaging {
64+
// https://developer.android.com/reference/tools/gradle-api/9.1/com/android/build/api/dsl/JniLibsPackaging
65+
jniLibs.excludes.add("lib/*/libdummy.so")
66+
jniLibs.pickFirsts.add("lib/*/libc++_shared.so")
67+
}
68+
// https://developer.android.com/reference/tools/gradle-api/9.1/com/android/build/api/dsl/AndroidSourceSet
69+
sourceSets {
70+
getByName("main") {
71+
assets {
72+
directories += "../../../../assets"
73+
}
74+
}
75+
}
76+
}
77+
78+
dependencies {
79+
implementation(libs.appcompat)
80+
implementation(libs.core)
81+
implementation(libs.material)
82+
implementation(libs.games.activity)
83+
implementation(libs.core.ktx)
84+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:tools="http://schemas.android.com/tools">
4+
5+
<application android:icon="@mipmap/ic_launcher" android:label="Bevy Example" android:roundIcon="@mipmap/ic_launcher" android:theme="@style/Theme.AppCompat.NoActionBar" tools:targetApi="36">
6+
<activity android:name=".MainActivity" android:exported="true" android:configChanges="layoutDirection|locale|orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode" android:theme="@style/Theme.AppCompat.NoActionBar">
7+
<meta-data android:name="android.app.lib_name" android:value="bevy_mobile_example" />
8+
<intent-filter>
9+
<action android:name="android.intent.action.MAIN" />
10+
<category android:name="android.intent.category.LAUNCHER" />
11+
</intent-filter>
12+
</activity>
13+
</application>
14+
15+
</manifest>

examples/mobile/android_example/app/src/main/cpp/dummy.cpp renamed to examples/mobile/android/app/src/main/cpp/dummy.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
// Intentionally blank -- this is a dummy code!
2-
// This is part of a hack to convince gradle to insert libc++_shared.so
1+
// Intentionally blank -- this is a dummy code!
2+
// This is part of a hack to convince gradle to insert libc++_shared.so
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package org.bevyengine.example
2+
3+
import androidx.core.view.WindowCompat
4+
import androidx.core.view.WindowInsetsCompat
5+
import androidx.core.view.WindowInsetsControllerCompat
6+
import com.google.androidgamesdk.GameActivity
7+
8+
/**
9+
* Loads the rust library and handles android specific to integrate with it.
10+
*
11+
*
12+
* The library is loaded at class initialization and provided by jniLibs.
13+
*/
14+
class MainActivity : GameActivity() {
15+
/**
16+
* Called when the current Window of the activity gains or loses focus.
17+
*
18+
*
19+
* This just hides the system UI if the app window is focused.
20+
*/
21+
override fun onWindowFocusChanged(hasFocus: Boolean) {
22+
// Call parent class implementation of onWindowFocusChanged to make sure that we are updating correctly.
23+
super.onWindowFocusChanged(hasFocus)
24+
25+
// If the window has focus, hide system UI.
26+
if (hasFocus) {
27+
hideSystemUi()
28+
}
29+
}
30+
31+
/**
32+
* Hides system UI.
33+
*
34+
*
35+
* This will make the app content fill the entire screen.
36+
*/
37+
private fun hideSystemUi() {
38+
val windowInsetsController =
39+
WindowCompat.getInsetsController(window, window.decorView)
40+
41+
// Show bars if swiping
42+
windowInsetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
43+
// Hide both the status bar and the navigation bar.
44+
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
45+
}
46+
47+
companion object {
48+
// Load rust library
49+
init {
50+
System.loadLibrary("bevy_mobile_example")
51+
}
52+
}
53+
}

assets/android-res/mipmap-mdpi/ic_launcher.png renamed to examples/mobile/android/app/src/main/res/mipmap/ic_launcher.png

File renamed without changes.

0 commit comments

Comments
 (0)