diff --git a/CHANGELOG.md b/CHANGELOG.md index ecfe9442..605763eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased +- On Android, changed default behavior of Android to ignore volume keys letting the operating system handle them. +- On Android, added `EventLoopBuilderExtAndroid::handle_volume_keys` to indicate that the application will handle the volume keys manually. - **Breaking:** Rename `DeviceEventFilter` to `DeviceEvents` reversing the behavior of variants. - **Breaking:** Rename `EventLoopWindowTarget::set_device_event_filter` to `listen_device_events`. - On X11, fix `EventLoopWindowTarget::listen_device_events` effect being reversed. diff --git a/src/platform/android.rs b/src/platform/android.rs index a7cc7c8e..cb0528ef 100644 --- a/src/platform/android.rs +++ b/src/platform/android.rs @@ -42,6 +42,11 @@ pub trait EventLoopBuilderExtAndroid { /// /// This must be called on Android since the `AndroidApp` is not global state. fn with_android_app(&mut self, app: AndroidApp) -> &mut Self; + + /// Calling this will mark the volume keys to be manually handled by the application + /// + /// Default is to let the operating system handle the volume keys + fn handle_volume_keys(&mut self) -> &mut Self; } impl EventLoopBuilderExtAndroid for EventLoopBuilder { @@ -49,6 +54,11 @@ impl EventLoopBuilderExtAndroid for EventLoopBuilder { self.platform_specific.android_app = Some(app); self } + + fn handle_volume_keys(&mut self) -> &mut Self { + self.platform_specific.ignore_volume_keys = false; + self + } } /// Re-export of the `android_activity` API diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 228a9510..1f8dd8ea 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -138,11 +138,22 @@ pub struct EventLoop { user_events_sender: mpsc::Sender, user_events_receiver: PeekableReceiver, //must wake looper whenever something gets sent running: bool, + ignore_volume_keys: bool, } -#[derive(Default, Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub(crate) struct PlatformSpecificEventLoopAttributes { pub(crate) android_app: Option, + pub(crate) ignore_volume_keys: bool, +} + +impl Default for PlatformSpecificEventLoopAttributes { + fn default() -> Self { + Self { + android_app: Default::default(), + ignore_volume_keys: true, + } + } } fn sticky_exit_callback( @@ -192,6 +203,7 @@ impl EventLoop { user_events_sender, user_events_receiver: PeekableReceiver::from_recv(user_events_receiver), running: false, + ignore_volume_keys: attributes.ignore_volume_keys, } } @@ -327,8 +339,8 @@ impl EventLoop { } // Process input events - self.android_app.input_events(|event| { + let mut input_status = InputStatus::Handled; match event { InputEvent::MotionEvent(motion_event) => { let window_id = window::WindowId(WindowId); @@ -395,67 +407,78 @@ impl EventLoop { } } InputEvent::KeyEvent(key) => { - let state = match key.action() { - KeyAction::Down => event::ElementState::Pressed, - KeyAction::Up => event::ElementState::Released, - _ => event::ElementState::Released, - }; - - #[cfg(feature = "android-native-activity")] - let (keycode_u32, scancode_u32) = unsafe { - // We abuse the fact that `android_activity`'s `KeyEvent` is `repr(transparent)` - let event = (key as *const android_activity::input::KeyEvent<'_>).cast::(); - // We use the unsafe function directly because we want to forward the - // keycode value even if it doesn't have a variant defined in the ndk - // crate. - ( - AKeyEvent_getKeyCode((*event).ptr().as_ptr()) as u32, - (*event).scan_code() as u32 - ) - }; - #[cfg(feature = "android-game-activity")] - let (keycode_u32, scancode_u32) = (key.keyCode as u32, key.scanCode as u32); - let keycode = keycode_u32 - .try_into() - .unwrap_or(ndk::event::Keycode::Unknown); - let physical_key = KeyCode::Unidentified( - NativeKeyCode::Android(scancode_u32), - ); - let native = NativeKey::Android(keycode_u32); - let logical_key = keycode_to_logical(keycode, native); - // TODO: maybe use getUnicodeChar to get the logical key - - let event = event::Event::WindowEvent { - window_id: window::WindowId(WindowId), - event: event::WindowEvent::KeyboardInput { - device_id: event::DeviceId(DeviceId), - event: event::KeyEvent { - state, - physical_key, - logical_key, - location: keycode_to_location(keycode), - repeat: key.repeat_count() > 0, - text: None, - platform_specific: KeyEventExtra {}, - }, - is_synthetic: false, + match key.key_code() { + // Flagg keys related to volume as unhandled. While winit does not have a way for applications + // to configure what keys to flag as handled, this appears to be a good default until winit + // can be configured. + ndk::event::Keycode::VolumeUp | + ndk::event::Keycode::VolumeDown | + ndk::event::Keycode::VolumeMute => { + if self.ignore_volume_keys { + input_status = InputStatus::Unhandled + } }, - }; - sticky_exit_callback( - event, - self.window_target(), - control_flow, - callback, - ); + _ => { + let state = match key.action() { + KeyAction::Down => event::ElementState::Pressed, + KeyAction::Up => event::ElementState::Released, + _ => event::ElementState::Released, + }; + + #[cfg(feature = "android-native-activity")] + let (keycode_u32, scancode_u32) = unsafe { + // We abuse the fact that `android_activity`'s `KeyEvent` is `repr(transparent)` + let event = (key as *const android_activity::input::KeyEvent<'_>).cast::(); + // We use the unsafe function directly because we want to forward the + // keycode value even if it doesn't have a variant defined in the ndk + // crate. + ( + AKeyEvent_getKeyCode((*event).ptr().as_ptr()) as u32, + (*event).scan_code() as u32 + ) + }; + #[cfg(feature = "android-game-activity")] + let (keycode_u32, scancode_u32) = (key.keyCode as u32, key.scanCode as u32); + let keycode = keycode_u32 + .try_into() + .unwrap_or(ndk::event::Keycode::Unknown); + let physical_key = KeyCode::Unidentified( + NativeKeyCode::Android(scancode_u32), + ); + let native = NativeKey::Android(keycode_u32); + let logical_key = keycode_to_logical(keycode, native); + // TODO: maybe use getUnicodeChar to get the logical key + + let event = event::Event::WindowEvent { + window_id: window::WindowId(WindowId), + event: event::WindowEvent::KeyboardInput { + device_id: event::DeviceId(DeviceId), + event: event::KeyEvent { + state, + physical_key, + logical_key, + location: keycode_to_location(keycode), + repeat: key.repeat_count() > 0, + text: None, + platform_specific: KeyEventExtra {}, + }, + is_synthetic: false, + }, + }; + sticky_exit_callback( + event, + self.window_target(), + control_flow, + callback, + ); + } + } } _ => { warn!("Unknown android_activity input event {event:?}") } } - - // Assume all events are handled, while Winit doesn't currently give a way for - // applications to report whether they handled an input event. - InputStatus::Handled + input_status }); // Empty the user event buffer