Skip to content
Open
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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions src/changelog/unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
41 changes: 39 additions & 2 deletions src/platform_impl/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -183,6 +184,7 @@ impl<T: 'static> EventLoop<T> {
&redraw_flag,
android_app.create_waker(),
),
show_keyboard_on_resume: Arc::new(AtomicBool::new(false)),
},
_marker: PhantomData,
},
Expand Down Expand Up @@ -267,6 +269,10 @@ impl<T: 'static> EventLoop<T> {
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?
Expand Down Expand Up @@ -635,6 +641,7 @@ pub struct ActiveEventLoop {
control_flow: Cell<ControlFlow>,
exit: Cell<bool>,
redraw_requester: RedrawRequester,
show_keyboard_on_resume: Arc<AtomicBool>,
}

impl ActiveEventLoop {
Expand Down Expand Up @@ -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<AtomicBool>,
}

impl Window {
Expand All @@ -759,7 +789,11 @@ impl Window {
) -> Result<Self, error::OsError> {
// 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) {
Expand Down Expand Up @@ -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) {}

Expand Down
3 changes: 2 additions & 1 deletion src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down