Open
Conversation
game_activity: Fix `pointer_index()` always returning `0`
Calling Activity.finish() is what ensures the Activity will get gracefully destroyed, including calling the Activity's onDestroy method. Fixes: #67
I was trying to quickly get to the documentation of this crate and had the GitHub page open... but there was no link on the front-page: let's fix that.
README: Add badges to CI, crates.io, docs.rs and show the MSRV
Call Activity.finish() when android_main returns
Cargo complains:
warning: android-activity/Cargo.toml: unused manifest key: package.rust_version
Solve this by replacing the underscore with a hyphen.
cargo: Fix `rust_version` -> `rust-version` property typo
Release 0.4.2
Fix a deadlock that occurs when an activity is destroyed without process termination, such as when an activity is destroyed and recreated due to a configuration change. The deadlock occurs because `notify_destroyed` blocks until `destroyed` is set to `true`. This only occurs when `WaitableNativeActivityState` is dropped, but the `WaitableNativeActivityState` instance is the very thing being used to await for destruction, resulting in a deadlock. Instead of waiting for the `WaitableNativeActivityState` to be dropped we now wait until the native `android_main` thread has stopped. So we can tell the difference between the thread not running because it hasn't started or because it has finished (in case `android_main` returns immediately) this replaces the `running` boolean with a tri-state enum. Co-authored-by: Robert Bragg <robert@sixbynine.org>
Fix deadlock on activity onDestroy
This ensures that any java Activity callbacks take into account the possibility that the `android_app` may have already been marked destroyed if `android_main` has returned - and so they mustn't block and wait for a thread that is no longer running.
…lock GameActivity PATH: fix deadlocks in java callbacks after app destroyed
Release 0.4.3
Give C symbols that need to be exported a `_C` suffix so that they can be linked into a Rust symbol with the correct name (Since we can't directly export from C/C++ with Rust+Cargo) See: rust-lang/rfcs#2771
The real `android_main` is going to be written in Rust and android-activity needs to handle its own initialization before calling the application's `android_main` and so the C/C++ code calls an intermediate `_rust_glue_entry` function.
This makes a small change to the C glue code for GameActivity to send looper wake ups when new input is received (only sending a single wake up, until the application next handles input). This makes it possible to recognise that new input is available and send an `InputAvailable` event to the application - consistent with how NativeActivity can deliver `InputAvailable` events. This addresses a significant feature disparity between GameActivity and NativeActivity that meant GameActivity was not practically usable for GUI applications that wouldn't want to render continuously like a game.
This ensures that any java Activity callbacks take into account the possibility that the `android_app` may have already been marked destroyed if `android_main` has returned - and so they mustn't block and wait for a thread that is no longer running.
Updated To Games-Activity 2.0.2
This imports the SDK from commit 8fa58b0e145ec28e726fa2b1c7e7a52af925ca35, from: https://github.com/rust-mobile/android-games-sdk/commits/android-activity-4.0.0 This includes one "notify android_main of editor actions" patch which will make it possible to forward editor actions and support IME Commit events in Winit) # notify android_main of editor actions This adds a pendingEditorActions member to android_app that is set via onEditorAction and the android_main thread is notified via notifyInput instead of re-instating APP_CMD_EDITOR_ACTION. The idea is that the android_main thread should check for android_app->pendingEditorActions whenever input events are polled/iterated. # FFI bindings update Also updates the FFI bindings via generate-bindings.sh
APP_CMD_SOFTWARE_KB_VIS_CHANGED in the GameActivity backend is intended for notifying the android_main thread that the soft keyboard visibility has changed. There's currently no Rust event / API for this, and so it wasn't being handled in poll_events but that was leading to a unreachable panic when GameActivity would send this APP_CMD when showing soft keyboards. We don't currently plan to expose any public API / event for this since it's based on monitoring IME insets and applications should instead be able to check insets after getting InsetsChanged events. For the sake of minimizing patches to the upstream GameActivity code this makes it so poll_events can ignore this APP_CMD as a NOOP.
This exposes IME actions via an InputEvent::TextAction event so that it's possible to recognise when text entry via an input method is finished. This adds a `TextInputAction` enum to represent the action key on a soft keyboard, such as "Done". For example, this makes it possible to emit Ime::Commit events in Winit.
This re-generates the FFI bindings with `--rust-target '1.85.0'`
This adds a common init_android_main_thread() utility that's called by both backends in order to get the ClassLoader from the Activity and associate that with the thread via `JThreadthread::set_context_class_loader` (which jni 0.22 can use automatically when loading classes). This change notably starts to use the `jni::bind_java_type!` macro for the `KeyCharacterMap` and `InputDevice` Java SDK API bindings in `src/input/sdk.rs` which is a nice simplification.
This was replaced by `AttachConfig::thread_name` in jni 0.22.2, which takes a `&JNIStr` and doesn't require an extra allocation for the name to be mutf8 encoded.
The `cesu8` crate hasn't been updated for 10 years where as the `simd_cesu8` crate is more actively maintained and is expected to have better performance in all conditions: <https://docs.rs/simd_cesu8/latest/simd_cesu8/#benchmarks> This change is consistent with the `jni` crate which switched to using `simd_cesu8` in the 0.22.2 release - so this avoids needing to build two separate crates for mutf8 encoding. This also addresses the current CI issue that comes from incorrectly depending on `cesu8 = "1"` instead of `"1.1.0"` (which adds the java/mutf8 APIs that we used). Previously this went unnoticed because of the `jni` crate pulling in the correct minimum version.
This corresponds to the GameActivity_setImeEditorInfo function on GameActivity. This is not supported on NativeActivity. Signed-off-by: William Casarin <jb55@jb55.com>
This adds and documents the remaining `ImeOptions` (addressing TODO comment) `ImeOptions` now has a getter/setter for the action, based on the `TextInputAction` enum added in #216 There is also a separate `InputTypeClass` that lets you query the mutually-exclusive type class bits from an `InputType`
Makes it possible to configure the action of the IME enter key via `set_ime_editor_info()`
The `ALooper_pollAll` API is deprecated and considering that `poll_events` never promised the behaviour of `pollAll` we simply change the implementation to use `ALoooper_pollOnce` and assume the caller is going to anyway be calling `poll_events` within its own loop. Note: `pollOnce` can still deliver multiple callback events, and the "once" effectively just refers to only calling `epoll_wait` at most once. Considering winit for example, this should have no effect since winit will be calling `poll_events` in a loop with no assumption about how concurrent events could potentially be batched. Fixes: #170
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@v4...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
This ensures we call `ALooper_acquire` before `create_waker()` wraps the Looper pointer with `AndroidAppWaker` and it also ensures that `::clone()` and `::drop()` call `ALooper_acquire()` and `ALooper_release()` respectively. Contrary to what the comment for the `looper` member said previously, it was not safe to assume that the application's looper pointer had a `'static` lifetime. The looper pointer would only be valid up until `android_main` returns, but unlike a traditional `main()` function an `android_main()` runs with respect to an `Activity` lifecycle and not a process lifecycle. It's technically possible for `android_main()` to return (at which point any looper stored in `'static` storage would have previously become an invalid pointer) and then JNI could be used to re-enter Rust and potentially try and dereference that invalid pointer. This adds a shared implementation of `AndroidAppWaker` to `src/waker.rs` instead of having each backend implement `AndroidAppWaker`. Fixes: #226
Instead of initializing `ndk-context` with an `Activity` reference (for the `android.context.Context` subclass) we now initialize with an `android.app.Application` reference (also an `android.context.Context` subclass). The benefit of this is that we can strictly initialize `ndk-context` once (via a `OnceLock`) so there's no risk of a panic in case an application starts more than one Activity within the same process. Fixes: #58 Fixes: #228
This imports the SDK from commit 30b8bfcc9a12942d1268820e8a83d7643e99ee92, from: https://github.com/rust-mobile/android-games-sdk/commits/android-activity-4.0.0 this includes these patches: # PATCH: Add mainLooper to android_app Track the Looper for the Java main/UI thread in the android_app. This makes it possible to add file descriptors and callbacks to the Java UI Looper from the android_main thread. This needs to be initialized by the android_native_app_glue before spawning the android_main thread because the looper needs to be discovered via `ALooper_forThread` while still running on the Java main thread (in the onCreate callback). # PATCH: Enable Rust glue to hook into onCreate This declares an extern `_rust_glue_on_create_hook` that is called from `GameActivity` `onCreate` native method callback, before the `android_main` thread is spawned. This gives Rust code an opportunity to run code and initialize state while still running on the Java main/UI thread. For example, this could be used to initialize JNI bindings while we can assume that the current thread has an associated ClassLoader that will be able to find application classes. It may also be a convenient place to make some initial JNI calls into Android SDK APIs that can only be used from the Java main thread. # Updated import-games-sdk.sh to remove symlinks While updating the SDK the import script has been updated to remove any symlinks which make it difficult to build android-activity from Git on Windows. Note: the symlinks were redundant based on how the include paths were already configured in `build.rs`
This makes it possible to register file descriptors that can wake up the Java main / UI thread as well as callbacks that will run on the Java main / UI thread. Although it can be common to refer to this thread as the "main" thread, we choose to explicitly refer to it as the "java main" thread thread in the API to avoid confusion with the Rust thread that runs "android_main". Co-authored-by: Robert Bragg <robert@sixbynine.org>
This simplifies the `clear_and_map_exception_to_err` utility so it's based on `jni::Env::exception_catch`
This makes it easy to schedule boxed closures to be run on the Java main / ui thread. When the closure is run then: - Any panic will be caught, so we don't unwind into the Looper and abort the process - The JVM will be attached (for JNI) and any exceptions that are thrown will be caught and logged as errors. - A JNI stack frame will be pushed and popped before running your closure (so you don't have to worry about leaking local JNI references) This bumps the jni dependency to 0.22.4 because that version adds a `JCharSequence` binding that we use in the `Toast` example in the documentation.
This makes sure that the `AssetManager` we return from `AndroidApp::asset_manager` can be retained with a static lifetime and never become a wrapper for an invalid pointer. The key change here is that we now return the Application AssetManager (i.e. from Application.getAssets()) instead of the Activity AssetManager. Theoretically there could be some applications that could associate an Activity AssetManager with unique resources but that's not expected to be common (and at least no expected to affect anyone currently using `AndroidApp::asset_manager`). As part of the `APP_ONCE` initialization in `init_android_main_thread` we now get a global reference to the Application AssetManager and get the corresponding AAssetManager that we can trust will be valid for the lifetime of the process since we leak the global reference. Note: The Application `AssetManager` is logically a process-wide resource and so the leaked global is just a technical formality to ensure it can't be garbage collected, but that's assumed to be redundant. Note: If anyone _strictly_ needs the `Activity` `AssetManager` then they could at least resort to calling `Activity.getAssets()` via JNI manually, but perhaps we can later consider adding a separate `AndroidApp::activity_asset_manager()` that will pair an `AAssetManager` pointer with a JNI global reference to ensure the pointer remains valid. Fixes #161
Once `on_destroy()` returns then the `NativeActivity.java` code will call an `unloadNativeCode` native method that will `delete` the `ANativeActivity` and invalidate any pointers we hold. Considering the possibility that an `AndroidApp` could be retained beyond the lifetime of the original `NativeActivity`, this ensures we always hold the `WaitableNativeActivityState::mutex` before dereferencing this pointer and ensures we clear the pointer before returning from `on_destroy` so we're also able to perform `null` pointer checks before dereferencing. Considering that `AndroidApp::vm_as_ptr` previously depended on dereferencing the `ANativeActivity`, this updates it to instead use `JavaVM::singleton()` which we guarantee will be initialized before the `AndroidApp` is created. Considering that `AndroidApp::activity_as_ptr()` promises to return a global reference that remains valid for the lifetime of the `AndroidApp`, but the `ANativeActivity::clazz` reference is deleted after `on_destroy()` returns, we now create our own `Global` reference for the `Activity` that is owned by `AndroidAppInner`.
Most of the same issues found in the native-activity backend when working on #234 (to safely drop ANativeActivity via onDestroy callback) also apply to the game-activity backend, which this PR addresses. This ensures that the game-activity backend cleanly drops its `android_app` pointer once we're notified that the `GameActivity` is being destroyed and adds a mutex around the pointer that guarantees that it can't be freed while it's being dereferenced (because the same lock is required to respond to the onDestroy callback where the state gets freed). This makes a number of backend details consistent with the native-activity backend: - The backend retains its own Looper reference instead of relying on the android_app reference. - The backend allocates its own JNI global reference for the Activity, instead of relying on the android_app reference. Since this needed to add a hook to clear the android_app pointer after dispatching the callback for `MainEvent::Destroy` it also made sense to fix the MainEvent::TerminateWindow hook for clearing our `NativeWindow` so it also happens _after_ the callback (as the API docs state). Testing these changes with a minimal agdk-mainloop and agdk-egui example I see it's now possible to cleanly handle repeated activity start -> destroy -> start -> destroy cycles (e.g. due to config changes triggering a recreation of the activity). (When testing egui I did also have to patch Winit to ensure it exits the loop when receiving a Destroy event) Fixes: #235 Fixes: #162
When we know we're done with the `Weak` reference that is associated with the `NativeActivity` callbacks we make sure to drop the `Weak` reference so that the underlying allocation for the `WaitableNativeActivityState` can be freed. This also updates `try_with_waitable_activity_ref` to be more careful about converting the `Weak` ref back into a raw pointer _before_ calling the handler, just in case the handler triggers a panic and unwinds (where we wouldn't want to lose/Drop our weak ref).
This imports the SDK from commit 090732c3ca7d8b47ed39e028081d685e4097db7f, from: https://github.com/rust-mobile/android-games-sdk/commits/android-activity-4.0.0 This imports a patch to revert the recent addition of a `_rust_glue_on_create_hook` in favour of fixing the Rust wrapper for `GameActivity_onCreate` which is more consistent with the `ANativeActivity_onCreate` entrypoint that we have in the `native-activity` backend. This also: - Fixes a related rerun-if-changed path in build.rs - Removes the reference to _rust_glue_on_create_hook src/game_activity/mod.rs
This adds support for an optional `android_on_crate` entrypoint which is called from within the Activity.onCreate native method callback from the Java main / UI thread. This gives applications an opportunity initialize state while the `Activity`'s class loader is on the stack, so `FindClass` will be able to find application classes. This can be a more-convenient place to initialize JNI bindings, without needing to explicitly get the class loader from the Activity to be able to look up application classes from the android_main thread. This may also be convenient for initially using JNI to interact with your new Activity in case you need to use SDK APIs that are only safe to use from the Java main / UI thread. The moves the thread initialization functions out of util.rs into a new init.rs While adding documentation for this feature, this also does a more-general pass over the top-level crate documentation to try and ensure it's up-to-date. Fixes: #169 Addresses: #82
This updates `AndroidApp::show/hide_soft_input` to be implemented manually with JNI (instead of `ANativeActivity_show/hideSoftInput`) so that we can pass the root, decor view to `InputMethodManager.showSoftInput` instead of the private `mNativeContentView` created by `NativeActivity`. Unlike the private `mNativeContentView`, the root decor view is considered to be the current "served" view for a vanilla `NativeActivity`-based application. Co-authored-by: Robert Bragg <robert@sixbynine.org>
This exposes `MotionEvent` pointer history via a `PointerHistoryIter`, got via
`Pointer::history()`, that yields `HistoricPointer`'s that give access to
sub-sample timestamps and axis values between events.
This adds consistent pointer history support for both the `native-activity` and
`game-activity` backends.
For example, historical pointer samples can be iterated like:
```rust
let num_pointers = motion_event.pointer_count();
for i in 0..num_pointers {
let pointer = motion_event.pointer_at_index(i);
println!(
"Pointer[{i}]: id={}, time={}, x={}, y={}",
pointer.pointer_id(),
motion_event.event_time(),
pointer.x(),
pointer.y(),
);
for sample in pointer.history() {
println!(
" History[{}]: x={}, y={}, time={:?}",
sample.history_index(),
sample.x(),
sample.y(),
sample.event_time()
);
}
}
```
The documentation clarifies that each pointer will have the same number of
historic samples, and the timestamps for corresponding samples will match.
The `PointerHistoryIter` supports forwards and/or backwards iteration and can
be queried for its `.len()`.
Fixes: #207
This imports the Android Games SDK from: - repo: https://github.com/rust-mobile/android-games-sdk - branch: android-activity-4.4.0 - commit: 78daa4adfc4a619daeab9f96181190b145f1e544 This is based on the GameActivity 4.4.0 release from: - repo: https://android.googlesource.com/platform/frameworks/opt/gamesdk - branch: android-games-sdk-game-activity-release - commit: 541587a073871a9d2659f90335dcae345007eeed Our integration branch includes the following patches: - 78daa4ad Add mainLooper to android_app - 179eaa92 notify android_main of editor actions - 5102e14c Don't send (unused) APP_CMD_EDITOR_ACTION - 223936cf Don't send (unused) APP_CMD_KEY/TOUCH_EVENTs - a24b21a4 android-activity: don't read unicode via getUnicodeChar - 9e3926b1 android-activity: rename C symbols that need export - 32ac1c73 glue: support InputAvailable events - 69a1868c glue: fix deadlocks in java callbacks after app destroyed - b5a2df04 glue: remove unused variable This re-runs `generate-bindings.sh` Notes: Reviewing the upstream changes, it doesn't look like much has changed since the 4.0.0 release (at least in the glue layer). It was quite painful rebasing on the latest upstream release due to upstream running clang-format across their whole repo In this case I ended up using `git filter-branch` to reformat our patches before rebasing: ``` git filter-branch -f --tree-filter ' find game-activity/prefab-src/modules/game-activity \ \( -iname "*.c" -o -iname "*.h" -o -iname "*.cpp" \) \ -print0 | xargs -0 clang-format -i --style="{BasedOnStyle: Google, AccessModifierOffset: -4, AlignOperands: false, AllowShortFunctionsOnASingleLine: Empty, AlwaysBreakBeforeMultilineStrings: false, ColumnLimit: 100, CommentPragmas: \"NOLINT:.*\", ConstructorInitializerIndentWidth: 6, ContinuationIndentWidth: 8, IndentWidth: 4, PenaltyBreakBeforeFirstCallParameter: 100000, SpacesBeforeTrailingComments: 1}" ' cdf4eee808130cc007a6203904d1d6c9acbf53a3^..HEAD ``` Previously, we were apparently based on Google's `android-games-sdk-game-text-input-release` branch instead of their `android-games-sdk-game-activity-release` branch, which made it awkward to diff changes because both branches include the game-activity SDK but all of the same patches have different commit hashes between branches. Our previous base commit for GameActivity 4.0.0 was: 7f54c13ee549e4511dcdc15a8ca73864e87be605 which corresponds to: 65ee0100ead8cf73c851f150bffad2779dfa8704 on the game-activity-release branch Note: The upstream release notes are also confusing because where they list what commits are included in each release, then for the 4.0.0 release those commits only exist on the `game-text` release branch but for the 4.4.0 release the commits exist on the `game-activity` release branch. These are the upstream patches to the game-activity glue since 4.0.0: - c28257b2 Push new version of GameActivity 4.4.0 - no functional change - e32db80f Fixed formatting of gamesdk repo - no functional change - a7cdb8c6 Migrate from deprecate ALooper_pollAll to ALooper_pollOnce - only affects examples - 163d7fcb Improve android_app_set_activity_state ANR protection - this adds a timeout for how long android_app_set_activity_state will block waiting for the android_main thread to handle synchronous callbacks (such as onStart, onResume) - this is backwards compatible - d3fbe82a Improve version revision macro updating - no functional change - 2ae5d1f4 Release a new alpha version for AGDK components. - no functional change - 3e5fc4cd Add JNI_OnLoad function - an (optional) alternative means to call `GameActivity_register` which won't affect us since the `JNI_OnLoad` exported from C++ won't be exported when compiling with the Rust toolchain. - 044fd03c Release a new alpha version for AGDK components. - no functional change - 1198bb06 Fix GameActivity getLocale* functions. - this is a backwards compatible fix that doesn't interact without integration changes - 07eff729 Change GameActivity and GameTextInput to 4.1 alpha. - no functional change Based on this audit, our integration should be backwards compatible with GameActivity 4.0.0
Updating to `thiserror` 2 doesn't affect our public api
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.