Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ resolver = "2"
members = ["android-activity"]

exclude = ["examples"]

[patch.crates-io]
jni = { path = "/home/rib/src/jni-rs/jni-git-dev0" }
48 changes: 20 additions & 28 deletions android-activity/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed
- input: Replaced custom types with their `ndk` crate equivalent.
> [!NOTE]
> These types existed because the `ndk` crate didn't provide them in an extensible way. Now that they have the `#[non_exhaustive]` flag and contain a `__Unknown(T)` variant to provide lossless conversions, and not to mention use an ABI type that matches how it is being used by most functions (when the original constants were defined in a "typeless" way), the `ndk` types are used and reexported once again.
> [!IMPORTANT]
> **Relevant breaking changes**:
> - `repr()` types for some `enum`s have changed to match the ABI type that is used by most functions that are returning or consuming this wrapper type.
> - `Source::is_xxx_class()` functions are replaced by querying `Source::class()` and comparing against variants from the returned `SourceClass` `bitflags` enum.
> - `SourceFlags::TRACKBALL` (from `Source::is_trackball_class()`) is named `SourceClass::NAVIGATION` in the `ndk`.
### Added
- The `ndk` and `ndk-sys` crates are now re-exported under `android_activity::ndk` and `android_activity::ndk_sys` ([#194](https://github.com/rust-mobile/android-activity/pull/194))

### Changed
- rust-version bumped to 1.73.0 ([#193](https://github.com/rust-mobile/android-activity/pull/193))
- The `ndk` and `ndk-sys` crates are now re-exported under `android_activity::ndk` and `android_activity::ndk_sys` ([#194](https://github.com/rust-mobile/android-activity/pull/194))
- GameActivity updated to 4.0.0 (requires the corresponding 4.0.0 `.aar` release from Google) ([#191](https://github.com/rust-mobile/android-activity/pull/191))

## [0.6.0] - 2024-04-26
Expand All @@ -40,32 +32,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Avoids depending on default features for `ndk` crate to avoid pulling in any `raw-window-handle` dependencies ([#142](https://github.com/rust-mobile/android-activity/pull/142))

**Note:** Technically, this could be observed as a breaking change in case you
were depending on the `rwh_06` feature that was enabled by default in the
`ndk` crate. This could be observed via the `NativeWindow` type (exposed via
`AndroidApp::native_window()`) no longer implementing `rwh_06::HasWindowHandle`.
**Note:** Technically, this could be observed as a breaking change in case you
were depending on the `rwh_06` feature that was enabled by default in the
`ndk` crate. This could be observed via the `NativeWindow` type (exposed via
`AndroidApp::native_window()`) no longer implementing `rwh_06::HasWindowHandle`.

In the unlikely case that you were depending on the `ndk`'s `rwh_06` API
being enabled by default via `android-activity`'s `ndk` dependency, your crate
should explicitly enable the `rwh_06` feature for the `ndk` crate.
In the unlikely case that you were depending on the `ndk`'s `rwh_06` API
being enabled by default via `android-activity`'s `ndk` dependency, your crate
should explicitly enable the `rwh_06` feature for the `ndk` crate.

As far as could be seen though, it's not expected that anything was
depending on this (e.g. anything based on Winit enables the `ndk` feature
based on an equivalent `winit` feature).
As far as could be seen though, it's not expected that anything was
depending on this (e.g. anything based on Winit enables the `ndk` feature
based on an equivalent `winit` feature).

The benefit of the change is that it can help avoid a redundant
`raw-window-handle 0.6` dependency in projects that still need to use older
(non-default) `raw-window-handle` versions. (Though note that this may be
awkward to achieve in practice since other crates that depend on the `ndk`
are still likely to use default features and also pull in
`raw-window-handles 0.6`)
The benefit of the change is that it can help avoid a redundant
`raw-window-handle 0.6` dependency in projects that still need to use older
(non-default) `raw-window-handle` versions. (Though note that this may be
awkward to achieve in practice since other crates that depend on the `ndk`
are still likely to use default features and also pull in
`raw-window-handles 0.6`)

- The IO thread now gets named `stdio-to-logcat` and main thread is named `android_main` ([#145](https://github.com/rust-mobile/android-activity/pull/145))
- Improved IO error handling in `stdio-to-logcat` IO loop. ([#133](https://github.com/rust-mobile/android-activity/pull/133))

## [0.5.0] - 2023-10-16
### Added
- Added `MotionEvent::action_button()` exposing the button associated with button press/release actions ([#138](https://github.com/rust-mobile/android-activity/pull/138))
- Added `MotionEvent::action_button()` exposing the button associated with button press/release actions ()

### Changed
- rust-version bumped to 0.68 ([#123](https://github.com/rust-mobile/android-activity/pull/123))
Expand Down
2 changes: 0 additions & 2 deletions android-activity/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,9 @@ default = []
game-activity = []
native-activity = []
api-level-30 = ["ndk/api-level-30"]
api-level-33 = ["api-level-30", "ndk/api-level-33"]

[dependencies]
log = "0.4"
jni-sys = "0.3"
cesu8 = "1"
jni = "0.21"
ndk-sys = "0.6.0"
Expand Down
2 changes: 1 addition & 1 deletion android-activity/src/game_activity/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#![allow(deref_nullptr)]
#![allow(dead_code)]

use jni_sys::*;
use jni::sys::*;
use libc::{pthread_cond_t, pthread_mutex_t, pthread_t};
use ndk_sys::{AAssetManager, AConfiguration, ALooper, ALooper_callbackFunc, ANativeWindow, ARect};

Expand Down
24 changes: 11 additions & 13 deletions android-activity/src/game_activity/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,10 @@
// The `Class` was also bound differently to `android-ndk-rs` considering how the class is defined
// by masking bits from the `Source`.

use ndk::event::ButtonState;

use crate::activity_impl::ffi::{GameActivityKeyEvent, GameActivityMotionEvent};
use crate::input::{
Axis, Button, EdgeFlags, KeyAction, KeyEventFlags, Keycode, MetaState, MotionAction,
MotionEventFlags, Pointer, PointersIter, Source, ToolType,
Axis, Button, ButtonState, EdgeFlags, KeyAction, KeyEventFlags, Keycode, MetaState,
MotionAction, MotionEventFlags, Pointer, PointersIter, Source, ToolType,
};

// Note: try to keep this wrapper API compatible with the AInputEvent API if possible
Expand Down Expand Up @@ -49,7 +47,7 @@ impl<'a> MotionEvent<'a> {
///
#[inline]
pub fn source(&self) -> Source {
let source = self.ga_event.source;
let source = self.ga_event.source as u32;
source.into()
}

Expand All @@ -65,7 +63,7 @@ impl<'a> MotionEvent<'a> {
/// See [the MotionEvent docs](https://developer.android.com/reference/android/view/MotionEvent#getActionMasked())
#[inline]
pub fn action(&self) -> MotionAction {
let action = self.ga_event.action & ndk_sys::AMOTION_EVENT_ACTION_MASK as i32;
let action = self.ga_event.action as u32 & ndk_sys::AMOTION_EVENT_ACTION_MASK;
action.into()
}

Expand Down Expand Up @@ -178,7 +176,6 @@ impl<'a> MotionEvent<'a> {
/// See [the NDK
/// docs](https://developer.android.com/ndk/reference/group/input#amotionevent_getbuttonstate)
#[inline]
// TODO: Button enum to signify only one bitflag can be set?
pub fn button_state(&self) -> ButtonState {
ButtonState(self.ga_event.buttonState as u32)
}
Expand Down Expand Up @@ -281,7 +278,7 @@ impl PointerImpl<'_> {
#[inline]
pub fn axis_value(&self, axis: Axis) -> f32 {
let pointer = &self.event.ga_event.pointers[self.index];
let axis: i32 = axis.into();
let axis: u32 = axis.into();
pointer.axisValues[axis as usize]
}

Expand All @@ -300,7 +297,8 @@ impl PointerImpl<'_> {
#[inline]
pub fn tool_type(&self) -> ToolType {
let pointer = &self.event.ga_event.pointers[self.index];
pointer.toolType.into()
let tool_type = pointer.toolType as u32;
tool_type.into()
}
}

Expand Down Expand Up @@ -667,7 +665,7 @@ impl<'a> KeyEvent<'a> {
///
#[inline]
pub fn source(&self) -> Source {
let source = self.ga_event.source;
let source = self.ga_event.source as u32;
source.into()
}

Expand All @@ -683,13 +681,13 @@ impl<'a> KeyEvent<'a> {
/// See [the KeyEvent docs](https://developer.android.com/reference/android/view/KeyEvent#getAction())
#[inline]
pub fn action(&self) -> KeyAction {
let action = self.ga_event.action;
let action = self.ga_event.action as u32;
action.into()
}

#[inline]
pub fn action_button(&self) -> KeyAction {
let action = self.ga_event.action;
let action = self.ga_event.action as u32;
action.into()
}

Expand Down Expand Up @@ -719,7 +717,7 @@ impl<'a> KeyEvent<'a> {
/// docs](https://developer.android.com/ndk/reference/group/input#akeyevent_getkeycode)
#[inline]
pub fn key_code(&self) -> Keycode {
let keycode = self.ga_event.keyCode;
let keycode = self.ga_event.keyCode as u32;
keycode.into()
}

Expand Down
89 changes: 52 additions & 37 deletions android-activity/src/game_activity/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::time::Duration;
use libc::c_void;
use log::{error, trace};

use jni_sys::*;
use jni::sys::*;

use ndk_sys::ALooper_wake;
use ndk_sys::{ALooper, ALooper_pollAll};
Expand All @@ -24,7 +24,7 @@ use ndk::native_window::NativeWindow;

use crate::error::InternalResult;
use crate::input::{Axis, KeyCharacterMap, KeyCharacterMapBinding};
use crate::jni_utils::{self, CloneJavaVM};
use crate::jni_utils;
use crate::util::{abort_on_panic, forward_stdio_to_logcat, log_panic, try_get_path_from_ptr};
use crate::{
AndroidApp, ConfigurationRef, InputStatus, MainEvent, PollEvent, Rect, WindowManagerFlags,
Expand Down Expand Up @@ -121,32 +121,35 @@ impl AndroidAppWaker {
}

impl AndroidApp {
pub(crate) unsafe fn from_ptr(ptr: NonNull<ffi::android_app>, jvm: CloneJavaVM) -> Self {
let mut env = jvm.get_env().unwrap(); // We attach to the thread before creating the AndroidApp

let key_map_binding = match KeyCharacterMapBinding::new(&mut env) {
Ok(b) => b,
Err(err) => {
panic!("Failed to create KeyCharacterMap JNI bindings: {err:?}");
}
};

// Note: we don't use from_ptr since we don't own the android_app.config
// and need to keep in mind that the Drop handler is going to call
// AConfiguration_delete()
let config = Configuration::clone_from_ptr(NonNull::new_unchecked((*ptr.as_ptr()).config));
pub(crate) unsafe fn from_ptr(ptr: NonNull<ffi::android_app>, jvm: jni::JavaVM) -> Self {
// We attach to the thread before creating the AndroidApp
jni::JavaVM::with_env::<_, _, jni::errors::Error>(|env| {
let key_map_binding = match KeyCharacterMapBinding::new(env) {
Ok(b) => b,
Err(err) => {
panic!("Failed to create KeyCharacterMap JNI bindings: {err:?}");
}
};

Self {
inner: Arc::new(RwLock::new(AndroidAppInner {
jvm,
native_app: NativeAppGlue { ptr },
config: ConfigurationRef::new(config),
native_window: Default::default(),
key_map_binding: Arc::new(key_map_binding),
key_maps: Mutex::new(HashMap::new()),
input_receiver: Mutex::new(None),
})),
}
// Note: we don't use from_ptr since we don't own the android_app.config
// and need to keep in mind that the Drop handler is going to call
// AConfiguration_delete()
let config =
Configuration::clone_from_ptr(NonNull::new_unchecked((*ptr.as_ptr()).config));

Ok(Self {
inner: Arc::new(RwLock::new(AndroidAppInner {
jvm,
native_app: NativeAppGlue { ptr },
config: ConfigurationRef::new(config),
native_window: Default::default(),
key_map_binding: Arc::new(key_map_binding),
key_maps: Mutex::new(HashMap::new()),
input_receiver: Mutex::new(None),
})),
})
})
.expect("Failed to create AndroidApp instance")
}
}

Expand Down Expand Up @@ -253,7 +256,7 @@ impl NativeAppGlue {

#[derive(Debug)]
pub struct AndroidAppInner {
pub(crate) jvm: CloneJavaVM,
pub(crate) jvm: jni::JavaVM,
native_app: NativeAppGlue,
config: ConfigurationRef,
native_window: RwLock<Option<NativeWindow>>,
Expand Down Expand Up @@ -544,11 +547,13 @@ impl AndroidAppInner {
}

pub fn enable_motion_axis(&mut self, axis: Axis) {
unsafe { ffi::GameActivityPointerAxes_enableAxis(axis.into()) }
let axis: u32 = axis.into();
unsafe { ffi::GameActivityPointerAxes_enableAxis(axis as i32) }
}

pub fn disable_motion_axis(&mut self, axis: Axis) {
unsafe { ffi::GameActivityPointerAxes_disableAxis(axis.into()) }
let axis: u32 = axis.into();
unsafe { ffi::GameActivityPointerAxes_disableAxis(axis as i32) }
}

pub fn create_waker(&self) -> AndroidAppWaker {
Expand Down Expand Up @@ -873,7 +878,7 @@ pub unsafe extern "C" fn Java_com_google_androidgamesdk_GameActivity_initializeN
jasset_mgr: jobject,
saved_state: jbyteArray,
java_config: jobject,
) -> jni_sys::jlong {
) -> jlong {
Java_com_google_androidgamesdk_GameActivity_initializeNativeCode_C(
env,
java_game_activity,
Expand Down Expand Up @@ -912,11 +917,17 @@ pub unsafe extern "C" fn _rust_glue_entry(native_app: *mut ffi::android_app) {
let activity: jobject = (*(*native_app).activity).javaGameActivity;
ndk_context::initialize_android_context(jvm.cast(), activity.cast());

let jvm = CloneJavaVM::from_raw(jvm).unwrap();
// Since this is a newly spawned thread then the JVM hasn't been attached
// to the thread yet. Attach before calling the applications main function
// so they can safely make JNI calls
jvm.attach_current_thread_permanently().unwrap();
let jvm = jni::JavaVM::from_raw(jvm);
// Since this is a newly spawned thread then the JVM hasn't been attached to the
// thread yet.
//
// For compatibility we attach before calling the applications main function to
// allow it to assume the thread is attached before making JNI calls.
jvm.attach_current_thread::<_, _, jni::errors::Error>(
jni::JNIVersion::V1_4,
|_| Ok(()),
)
.expect("Failed to attach thread to JVM");
jvm
};

Expand Down Expand Up @@ -951,7 +962,11 @@ pub unsafe extern "C" fn _rust_glue_entry(native_app: *mut ffi::android_app) {

// This should detach automatically but lets detach explicitly to avoid depending
// on the TLS trickery in `jni-rs`
jvm.detach_current_thread();
if let Err(err) = jvm.detach_current_thread() {
log::error!("Failed to detach thread from JVM: {}", err);
} else {
log::debug!("Detached thread from JVM");
}

ndk_context::release_android_context();
}
Expand Down
Loading
Loading