diff --git a/android-activity/src/game_activity/mod.rs b/android-activity/src/game_activity/mod.rs index 2974d1b9..47e4912a 100644 --- a/android-activity/src/game_activity/mod.rs +++ b/android-activity/src/game_activity/mod.rs @@ -320,6 +320,32 @@ impl AndroidAppInner { } } + // TODO: move into a trait + pub fn show_soft_input(&self, show_implicit: bool) { + unsafe { + let activity = (*self.native_app.as_ptr()).activity; + let flags = if show_implicit { + ffi::ShowImeFlags_SHOW_IMPLICIT + } else { + 0 + }; + ffi::GameActivity_showSoftInput(activity, flags); + } + } + + // TODO: move into a trait + pub fn hide_soft_input(&self, hide_implicit_only: bool) { + unsafe { + let activity = (*self.native_app.as_ptr()).activity; + let flags = if hide_implicit_only { + ffi::HideImeFlags_HIDE_IMPLICIT_ONLY + } else { + 0 + }; + ffi::GameActivity_hideSoftInput(activity, flags); + } + } + pub fn enable_motion_axis(&mut self, axis: Axis) { unsafe { ffi::GameActivityPointerAxes_enableAxis(axis as i32) } } diff --git a/android-activity/src/lib.rs b/android-activity/src/lib.rs index da1c6282..8e559d08 100644 --- a/android-activity/src/lib.rs +++ b/android-activity/src/lib.rs @@ -442,10 +442,35 @@ impl AndroidApp { self.inner.write().unwrap().enable_motion_axis(axis); } + /// Disable input axis + /// + /// To reduce overhead, by default only [`input::Axis::X`] and [`input::Axis::Y`] are enabled + /// and other axis should be enabled explicitly. pub fn disable_motion_axis(&self, axis: input::Axis) { self.inner.write().unwrap().disable_motion_axis(axis); } + /// Explicitly request that the current input method's soft input area be + /// shown to the user, if needed. + /// + /// Call this if the user interacts with your view in such a way that they + /// have expressed they would like to start performing input into it. + pub fn show_soft_input(&self, show_implicit: bool) { + self.inner.read().unwrap().show_soft_input(show_implicit); + } + + /// Request to hide the soft input window from the context of the window + /// that is currently accepting input. + /// + /// This should be called as a result of the user doing some action that + /// fairly explicitly requests to have the input window hidden. + pub fn hide_soft_input(&self, hide_implicit_only: bool) { + self.inner + .read() + .unwrap() + .hide_soft_input(hide_implicit_only); + } + /// Query and process all out-standing input event /// /// Applications are generally either expected to call this in-sync with their rendering or diff --git a/android-activity/src/native_activity/mod.rs b/android-activity/src/native_activity/mod.rs index 99cd9c94..af2cbf2e 100644 --- a/android-activity/src/native_activity/mod.rs +++ b/android-activity/src/native_activity/mod.rs @@ -27,9 +27,19 @@ mod ffi; pub mod input { pub use ndk::event::{ - Axis, ButtonState, EdgeFlags, InputEvent, KeyAction, KeyEvent, KeyEventFlags, Keycode, - MetaState, MotionAction, MotionEvent, MotionEventFlags, Pointer, Source, + Axis, ButtonState, EdgeFlags, KeyAction, KeyEvent, KeyEventFlags, Keycode, MetaState, + MotionAction, MotionEvent, MotionEventFlags, Pointer, Source, }; + + // We use our own wrapper type for input events to have better consistency + // with GameActivity and ensure the enum can be extended without needing a + // semver bump + #[derive(Debug)] + #[non_exhaustive] + pub enum InputEvent { + MotionEvent(self::MotionEvent), + KeyEvent(self::KeyEvent), + } } // The only time it's safe to update the android_app->savedState pointer is @@ -369,6 +379,32 @@ impl AndroidAppInner { } } + // TODO: move into a trait + pub fn show_soft_input(&self, show_implicit: bool) { + let na = self.native_activity(); + unsafe { + let flags = if show_implicit { + ffi::ANATIVEACTIVITY_SHOW_SOFT_INPUT_IMPLICIT + } else { + 0 + }; + ffi::ANativeActivity_showSoftInput(na as *mut _, flags); + } + } + + // TODO: move into a trait + pub fn hide_soft_input(&self, hide_implicit_only: bool) { + let na = self.native_activity(); + unsafe { + let flags = if hide_implicit_only { + ffi::ANATIVEACTIVITY_HIDE_SOFT_INPUT_IMPLICIT_ONLY + } else { + 0 + }; + ffi::ANativeActivity_hideSoftInput(na as *mut _, flags); + } + } + pub fn enable_motion_axis(&self, _axis: input::Axis) { // NOP - The InputQueue API doesn't let us optimize which axis values are read } @@ -403,16 +439,25 @@ impl AndroidAppInner { // ref: https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/jni/android_view_InputQueue.cpp // while let Ok(Some(event)) = queue.get_event() { - if let Some(event) = queue.pre_dispatch(event) { + if let Some(ndk_event) = queue.pre_dispatch(event) { + let event = match ndk_event { + ndk::event::InputEvent::MotionEvent(e) => input::InputEvent::MotionEvent(e), + ndk::event::InputEvent::KeyEvent(e) => input::InputEvent::KeyEvent(e), + }; callback(&event); + let ndk_event = match event { + input::InputEvent::MotionEvent(e) => ndk::event::InputEvent::MotionEvent(e), + input::InputEvent::KeyEvent(e) => ndk::event::InputEvent::KeyEvent(e), + }; + // Always report events as 'handled'. This means we won't get // so called 'fallback' events generated (such as converting trackball // events into emulated keypad events), but we could conceivably // implement similar emulation somewhere else in the stack if // necessary, and this will be more consistent with the GameActivity // input handling that doesn't do any kind of emulation. - queue.finish_event(event, true); + queue.finish_event(ndk_event, true); } } }