From b63b5e15fa39ab35eb9db557228f1ff38f6febb2 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Sun, 3 Jul 2022 23:34:25 +0100 Subject: [PATCH] Update READMEs --- README.md | 88 +++++++++++++++--------------- android-activity/README.md | 40 -------------- examples/agdk-egui/README.md | 16 ++---- examples/agdk-mainloop/README.md | 18 +++--- examples/agdk-winit-wgpu/README.md | 11 ++-- examples/na-mainloop/README.md | 25 +++------ examples/na-subclass-jni/README.md | 7 +-- examples/na-winit-wgpu/README.md | 9 +-- 8 files changed, 78 insertions(+), 136 deletions(-) delete mode 100644 android-activity/README.md diff --git a/README.md b/README.md index f351d5c..025bc9d 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,23 @@ -This project includes a number of Android "glue" crates for native Rust development -(comparable to [`android_native_app_glue.c`][ndk_concepts] which supports C/C++ -applications). +# Overview -These glue crates provide a way to load a `cdylib` via the `onCreate` method of +`android-activity` provides a "glue" layer for building native Rust +applications on Android, supporting multiple [`Activity`] base classes. +It's comparable to [`android_native_app_glue.c`][ndk_concepts] +for C/C++ applications. + +`android-activity` supports [`NativeActivity`] or [`GameActivity`] from the +Android Game Development Kit and can be extended to support additional base +classes. + +`android-activity` provides a way to load a `cdylib` via the `onCreate` method of your `Activity` class; run an `android_main()` function in a separate thread from the Java main thread and marshal events (such as lifecycle events and input events) between Java and your native thread. +[`Activity`]: https://developer.android.com/reference/android/app/Activity +[`NativeActivity`]: https://developer.android.com/reference/android/app/NativeActivity [ndk_concepts]: https://developer.android.com/ndk/guides/concepts#naa +[`GameActivity`]: https://developer.android.com/games/agdk/integrate-game-activity ### Example @@ -20,7 +30,7 @@ Cargo.toml [dependencies] log = "0.4" android_logger = "0.11" -native-activity = { git = "https://github.com/rib/agdk-rust/" } +android-activity = { git = "https://github.com/rib/android-activity/", features = [ "native-activity" ] } [lib] crate_type = ["cdylib"] @@ -29,15 +39,14 @@ crate_type = ["cdylib"] lib.rs ```rust use log::info; -use native_activity::{PollEvent, MainEvent}; +use android_activity::{PollEvent, MainEvent}; #[no_mangle] -extern "C" fn android_main() { +fn android_main(app: AndroidApp) { android_logger::init_once( android_logger::Config::default().with_min_level(log::Level::Info) ); - let app = native_activity::android_app(); loop { app.poll_events(Some(std::time::Duration::from_millis(500)) /* timeout */, |event| { match event { @@ -71,8 +80,8 @@ adb logcat example:V *:S # Game Activity Originally the aim was to enable support for building Rust applications based on the -[GameActivity] based class provided by [Google's Android Game Development Kit][agdk] -which should also facilitate integration with additional AGDK libraries including: +[GameActivity] class provided by [Google's Android Game Development Kit][agdk] +which can also facilitate integration with additional AGDK libraries including: 1. [Game Text Input](https://developer.android.com/games/agdk/add-support-for-text-input): a library to help fullscreen native applications utilize the Android soft keyboard. 2. [Game Controller Library, aka 'Paddleboat'](https://developer.android.com/games/sdk/game-controller): @@ -98,7 +107,7 @@ This project also supports [`NativeActivity`][NativeActivity] based applications NativeActivity is more limited than `GameActivity` and does not derive from `AppCompatActivity` it can sometimes still be convenient to build on `NativeActivity` in situations where you are using a limited/minimal build system that is not able to compile Java or Kotlin code or fetch from Maven -repositories. This is because `NativeActivity` is included as part of the Android platform. +repositories - this is because `NativeActivity` is included as part of the Android platform. [NativeActivity]: https://developer.android.com/reference/android/app/NativeActivity @@ -106,18 +115,12 @@ repositories. This is because `NativeActivity` is included as part of the Androi ## Compatibility -Both the [game-activity] glue crate and the [native-activity] glue crate support a common API that allows -them to be used interchangably, depending on which base class your application decides to use. +All `Activity` classes are supported via a common API that enables you to write +`Activity` subclass agnostic code wherever you don't depend on features that are +specific to a particular subclass. -Although it's expected that the `game-activity` crate will gain features that aren't possible with `native-activity` -those should be covered by optional features that allow downstream crates, such as Winit to practically be -able to support alternative glue layers. - -The hope is that the core, common API can be supported via any Activity subclass that your -application needs to use. - -[game-activity]: https://github.com/rib/agdk-rust/tree/main/game-activity -[native-activity]: https://github.com/rib/agdk-rust/tree/main/native-activity +For example, it makes it possible to have a [Winit backend](https://github.com/rib/winit/tree/agdk-game-activity) +that supports Android applications running with different `Activity` classes. ## API Summary @@ -126,48 +129,45 @@ application needs to use. The glue crates define a standard entrypoint ABI for your `cdylib` that looks like: ```rust +use android_activity::AndroidApp; + #[no_mangle] -extern "C" fn android_main() { +fn android_main(app: AndroidApp) { ... } ``` -There's currently no high-level macro provided for things like initializing logging or allowing the -main function to return a `Result<>` since it's expected that different downstream frameworks may -each have differing oppinions on the details and may want to provide their own macros. +There's currently no high-level macro provided for things like initializing +logging or allowing the main function to return a `Result<>` since it's expected +that different downstream frameworks may each have differing opinions on the +details and may want to provide their own macros. -### Global `AndroidApp` -The glue layer provides a `'static` `AndroidApp` API to access state about your running application -and handle synchronized interaction between your native Rust application and the `Activity` running -on the Java main thread. +### `AndroidApp` + +Your `android_main()` function is passed an `AndroidApp` struct to access state +about your running application and handle synchronized interaction between your +native Rust application and the `Activity` running on the Java main thread. For example, the `AndroidApp` API enables: 1. Access to Android lifecycle events 2. Notifications of SurfaceView lifecycle events 3. Access to input events 4. Ability to save and restore state each time your process stops and starts +5. Access application [`Configuration`] state +6. internal/external/obb filesystem paths -For example: -```rust -#[no_mangle] -extern "C" fn android_main() { - let app = game_activity::android_app(); - ... -} -``` - -_Note: that some of the `AndroidApp` APIs (such as for polling events) are only deemed safe to use -from the application's main thread_ +_Note: that some of the `AndroidApp` APIs (such as for polling events) are only +deemed safe to use from the application's main thread_ +[`Configuration`]: https://developer.android.com/reference/android/content/res/Configuration ### Synchronized event callbacks The `AndroidApp::poll_events()` API is similar to the Winit `EventLoop::run` API in that it takes a `FnMut` closure that is called for each outstanding event (such as for lifecycle events). -This is modeled on the original `android_native_app_glue` design for C/C++ that reserves the -ability for the glue layer to insert "pre-" and "-post" hooks around the application's event -callback that can handle any required synchronization with the Java main thread. +This design ensures the glue layer can transparently handle any required synchronization with +Java before and after each callback. For example, when the Java main thread notifies the glue layer that its `SurfaceView` is being destroyed the Java thread will then block until it gets an explicit acknowledgement that the diff --git a/android-activity/README.md b/android-activity/README.md deleted file mode 100644 index e2db5f6..0000000 --- a/android-activity/README.md +++ /dev/null @@ -1,40 +0,0 @@ -This crate provides a "glue" layer for building native Rust applications on Android, supporting multiple `Activity` base classes that the application can choose. - -Currently the crate supports `NativeActivity` or [`GameActivity`](https://developer.android.com/games/agdk/integrate-game-activity) from the Android Game Development Kit. - -This serves a similar purpose to [`android_native_app_glue.c`](https://android.googlesource.com/platform/development/+/4948c163663ecc343c97e4c2a2139234f1d3273f/ndk/sources/android/native_app_glue) for `C/C++` applications. - -Here's a minimal illustration of an Android main function and main loop based on this crate: - -```rust -use log::info; -use android_activity::{AndroidApp, PollEvent, MainEvent}; - -#[no_mangle] -fn android_main(app: AndroidApp) { - android_logger::init_once( - android_logger::Config::default().with_min_level(log::Level::Info) - ); - - loop { - app.poll_events(Some(std::time::Duration::from_millis(500)) /* timeout */, |event| { - match event { - PollEvent::Wake => { info!("Early wake up"); }, - PollEvent::Timeout => { info!("Timed out"); }, - PollEvent::Main(main_event) => { - info!("Main event: {:?}", main_event); - match main_event { - MainEvent::Destroy => { return; } - _ => {} - } - }, - _ => {} - } - - app.input_events(|event| { - info!("Input Event: {event:?}"); - }); - }); - } -} -``` diff --git a/examples/agdk-egui/README.md b/examples/agdk-egui/README.md index 056a990..2bed759 100644 --- a/examples/agdk-egui/README.md +++ b/examples/agdk-egui/README.md @@ -1,21 +1,17 @@ -This tests using the game_activity crate with egui, winit and wgpu. +This tests using `GameActivity` with egui, winit and wgpu. This is based on a re-worked winit backend here: -https://github.com/rib/winit/tree/agdk-game-activity - -and based on updated egui-winit and egui-wgpu crates here: -https://github.com/rib/egui/tree/android-deferred-winit-wgpu +https://github.com/rib/winit/tree/android-activity ``` -rustup target add aarch64-linux-android +export ANDROID_NDK_HOME="path/to/ndk" +export ANDROID_HOME="path/to/sdk" +rustup target add aarch64-linux-android cargo install cargo-ndk -export ANDROID_NDK_HOME="path/to/ndk" cargo ndk -t arm64-v8a -o app/src/main/jniLibs/ build - -export ANDROID_HOME="path/to/sdk" ./gradlew build ./gradlew installDebug adb shell am start -n co.realfit.agdkegui/.MainActivity -``` \ No newline at end of file +``` diff --git a/examples/agdk-mainloop/README.md b/examples/agdk-mainloop/README.md index 27d66fd..bdedbbf 100644 --- a/examples/agdk-mainloop/README.md +++ b/examples/agdk-mainloop/README.md @@ -1,19 +1,17 @@ -This is a minimal test application that just runs a mainloop based -on game_activity::poll_events() and traces the events received -without doing any rendering. It also saves and restores some -minimal application state. - +This is a minimal test application based on `GameActivity` that just +runs a mainloop based on android_activity::poll_events() and traces +the events received without doing any rendering. It also saves and +restores some minimal application state. ``` -rustup target add aarch64-linux-android +export ANDROID_NDK_HOME="path/to/ndk" +export ANDROID_HOME="path/to/sdk" +rustup target add aarch64-linux-android cargo install cargo-ndk -export ANDROID_NDK_HOME="path/to/ndk" cargo ndk -t arm64-v8a -o app/src/main/jniLibs/ build - -export ANDROID_HOME="path/to/sdk" ./gradlew build ./gradlew installDebug adb shell am start -n co.realfit.agdkmainloop/.MainActivity -``` \ No newline at end of file +``` diff --git a/examples/agdk-winit-wgpu/README.md b/examples/agdk-winit-wgpu/README.md index fc434fe..be2fc9f 100644 --- a/examples/agdk-winit-wgpu/README.md +++ b/examples/agdk-winit-wgpu/README.md @@ -1,7 +1,7 @@ -This tests using the game_activity crate with winit and wgpu. +This tests using `GameActivity` with winit and wgpu. This is based on a re-worked winit backend here: -https://github.com/rib/winit/tree/agdk-game-activity +https://github.com/rib/winit/tree/android-activity Although it would have been possible to handle the suspend/resume lifecycle events with a simpler approach of destroying and @@ -16,14 +16,13 @@ applications (that need to be portable) can work. (enable "desktop" feature to build binary) ``` -rustup target add aarch64-linux-android +export ANDROID_NDK_HOME="path/to/ndk" +export ANDROID_HOME="path/to/sdk" +rustup target add aarch64-linux-android cargo install cargo-ndk -export ANDROID_NDK_HOME="path/to/ndk" cargo ndk -t arm64-v8a -o app/src/main/jniLibs/ build - -export ANDROID_HOME="path/to/sdk" ./gradlew build ./gradlew installDebug adb shell am start -n co.realfit.agdkwinitwgpu/.MainActivity diff --git a/examples/na-mainloop/README.md b/examples/na-mainloop/README.md index 5eccc1d..0e73eec 100644 --- a/examples/na-mainloop/README.md +++ b/examples/na-mainloop/README.md @@ -1,31 +1,22 @@ -This is a minimal test application similar to `agdk-mainloop` that -is based on `NativeActivity` instead of `GameActivity`. - -It shows how to run a mainloop based on `native_activity::poll_events()` and -traces the events received without doing any rendering. It also saves and +This is a minimal test application based on `NativeActivity` that just +runs a mainloop based on android_activity::poll_events() and traces +the events received without doing any rendering. It also saves and restores some minimal application state. Since this test doesn't require a custom `Activity` subclass it's optionally possible to build this example with `cargo apk`. -The idea here is to test whether we can have a `NativeActivity` based -glue layer which is API compatible with `game-activity`, which can -optionally be used for _very_ basic tests/demos that want the convenience -of not needing to compile any Java code. - # Gradle Build ``` -rustup target add aarch64-linux-android +export ANDROID_NDK_HOME="path/to/ndk" +export ANDROID_HOME="path/to/sdk" +rustup target add aarch64-linux-android cargo install cargo-ndk -export ANDROID_NDK_HOME="path/to/ndk" cargo ndk -t arm64-v8a -o app/src/main/jniLibs/ build - -export ANDROID_HOME="path/to/sdk" ./gradlew build ./gradlew installDebug -adb shell am start -n co.realfit.namainloop/.MainActivity ``` # Cargo APK Build @@ -33,7 +24,9 @@ adb shell am start -n co.realfit.namainloop/.MainActivity export ANDROID_NDK_HOME="path/to/ndk" export ANDROID_SDK_HOME="path/to/sdk" +rustup target add aarch64-linux-android cargo install cargo-apk + cargo apk build cargo apk run -``` \ No newline at end of file +``` diff --git a/examples/na-subclass-jni/README.md b/examples/na-subclass-jni/README.md index b87e604..796299a 100644 --- a/examples/na-subclass-jni/README.md +++ b/examples/na-subclass-jni/README.md @@ -7,14 +7,13 @@ Note: unlike the `na-mainloop` example, this one can't be built via # Gradle Build ``` -rustup target add aarch64-linux-android +export ANDROID_NDK_HOME="path/to/ndk" +export ANDROID_HOME="path/to/sdk" +rustup target add aarch64-linux-android cargo install cargo-ndk -export ANDROID_NDK_HOME="path/to/ndk" cargo ndk -t arm64-v8a -o app/src/main/jniLibs/ build - -export ANDROID_HOME="path/to/sdk" ./gradlew build ./gradlew installDebug adb shell am start -n co.realfit.nasubclassjni/.MainActivity diff --git a/examples/na-winit-wgpu/README.md b/examples/na-winit-wgpu/README.md index efa0fe7..95def4e 100644 --- a/examples/na-winit-wgpu/README.md +++ b/examples/na-winit-wgpu/README.md @@ -1,8 +1,5 @@ -This is the same as agdk-winit-wgpu except it enables the "native-activity" -feature for winit, for running with `NativeActivity` instead of `GameActivity` - -This is to see if it's practical to support multiple glue implementations with -a common API. +This is the same as agdk-winit-wgpu except it runs with `NativeActivity` +instead of `GameActivity` # Gradle Build ``` @@ -23,7 +20,7 @@ export ANDROID_NDK_HOME="path/to/ndk" export ANDROID_SDK_HOME="path/to/sdk" rustup target add aarch64-linux-android - cargo install cargo-apk + cargo apk run ``` \ No newline at end of file