diff --git a/Cargo.toml b/Cargo.toml index e858087eb5..928cb1aa80 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -105,6 +105,7 @@ softbuffer = { version = "0.4.0", default-features = false, features = [ [target.'cfg(target_os = "android")'.dependencies] android-activity = "0.6.0" ndk = { version = "0.9.0", default-features = false } +jni = "0.21" [target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies] core-foundation = "0.9.3" diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index 9350bb2f1f..ee46ec3ea6 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -43,6 +43,7 @@ changelog entry. ### Added - Reexport `raw-window-handle` versions 0.4 and 0.5 as `raw_window_handle_04` and `raw_window_handle_05`. +- On Android, `set_ime_allowed` now opens and closes the soft keyboard. ### Removed diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 499ad67e2e..c8972b5f61 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -12,6 +12,7 @@ use android_activity::input::{InputEvent, KeyAction, Keycode, MotionAction}; use android_activity::{ AndroidApp, AndroidAppWaker, ConfigurationRef, InputStatus, MainEvent, Rect, }; +use jni::{objects::JObject, JavaVM}; use tracing::{debug, trace, warn}; use crate::application::ApplicationHandler; @@ -183,6 +184,7 @@ impl EventLoop { &redraw_flag, android_app.create_waker(), ), + show_keyboard_on_resume: Arc::new(AtomicBool::new(false)), }, _marker: PhantomData, }, @@ -267,6 +269,10 @@ impl EventLoop { MainEvent::Resume { .. } => { debug!("App Resumed - is running"); self.running = true; + show_hide_keyboard( + self.window_target.p.app.clone(), + self.window_target.p.show_keyboard_on_resume.load(Ordering::SeqCst), + ); }, MainEvent::SaveState { .. } => { // XXX: how to forward this state to applications? @@ -635,6 +641,7 @@ pub struct ActiveEventLoop { control_flow: Cell, exit: Cell, redraw_requester: RedrawRequester, + show_keyboard_on_resume: Arc, } impl ActiveEventLoop { @@ -747,9 +754,32 @@ impl DeviceId { #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] pub struct PlatformSpecificWindowAttributes; +fn show_hide_keyboard_fallible(app: AndroidApp, show: bool) -> Result<(), jni::errors::Error> { + // After Android R, it is no longer possible to show the soft keyboard + // with `showSoftInput` alone. + // Here we use `WindowInsetsController`, which is the other way. + let vm = unsafe { JavaVM::from_raw(app.vm_as_ptr() as _)? }; + let activity = unsafe { JObject::from_raw(app.activity_as_ptr() as _) }; + let mut env = vm.attach_current_thread()?; + let window = env.call_method(&activity, "getWindow", "()Landroid/view/Window;", &[])?.l()?; + let wic = env + .call_method(window, "getInsetsController", "()Landroid/view/WindowInsetsController;", &[])? + .l()?; + let window_insets_types = env.find_class("android/view/WindowInsets$Type")?; + let ime_type = env.call_static_method(&window_insets_types, "ime", "()I", &[])?.i()?; + env.call_method(&wic, if show { "show" } else { "hide" }, "(I)V", &[ime_type.into()])?.v() +} + +fn show_hide_keyboard(app: AndroidApp, show: bool) { + if let Err(e) = show_hide_keyboard_fallible(app, show) { + tracing::error!("Showing or hiding the soft keyboard failed: {e:?}"); + }; +} + pub(crate) struct Window { app: AndroidApp, redraw_requester: RedrawRequester, + show_keyboard_on_resume: Arc, } impl Window { @@ -759,7 +789,11 @@ impl Window { ) -> Result { // FIXME this ignores requested window attributes - Ok(Self { app: el.app.clone(), redraw_requester: el.redraw_requester.clone() }) + Ok(Self { + app: el.app.clone(), + redraw_requester: el.redraw_requester.clone(), + show_keyboard_on_resume: el.show_keyboard_on_resume.clone(), + }) } pub(crate) fn maybe_queue_on_main(&self, f: impl FnOnce(&Self) + Send + 'static) { @@ -888,7 +922,10 @@ impl Window { pub fn set_ime_cursor_area(&self, _position: Position, _size: Size) {} - pub fn set_ime_allowed(&self, _allowed: bool) {} + pub fn set_ime_allowed(&self, allowed: bool) { + self.show_keyboard_on_resume.store(allowed, Ordering::SeqCst); + show_hide_keyboard(self.app.clone(), allowed); + } pub fn set_ime_purpose(&self, _purpose: ImePurpose) {} diff --git a/src/window.rs b/src/window.rs index a8888c9f75..9b019fab92 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1278,7 +1278,8 @@ impl Window { /// /// - **macOS:** IME must be enabled to receive text-input where dead-key sequences are /// combined. - /// - **iOS / Android / Web / Orbital:** Unsupported. + /// - **Android:** Enabling IME only summons the soft keyboard, does not enable IME + /// - **iOS / Web / Orbital:** Unsupported. /// - **X11**: Enabling IME will disable dead keys reporting during compose. /// /// [`Ime`]: crate::event::WindowEvent::Ime