Skip to content

Commit

Permalink
On Android, change default implementation to ignore volume keys and l…
Browse files Browse the repository at this point in the history
…et operating system handle them (#2748)
  • Loading branch information
Hoodad authored Jun 6, 2023
1 parent ab46aa5 commit 4a36741
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 57 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
10 changes: 10 additions & 0 deletions src/platform/android.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,23 @@ 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<T> EventLoopBuilderExtAndroid for EventLoopBuilder<T> {
fn with_android_app(&mut self, app: AndroidApp) -> &mut Self {
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
Expand Down
137 changes: 80 additions & 57 deletions src/platform_impl/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,22 @@ pub struct EventLoop<T: 'static> {
user_events_sender: mpsc::Sender<T>,
user_events_receiver: PeekableReceiver<T>, //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<AndroidApp>,
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<T, F>(
Expand Down Expand Up @@ -192,6 +203,7 @@ impl<T: 'static> EventLoop<T> {
user_events_sender,
user_events_receiver: PeekableReceiver::from_recv(user_events_receiver),
running: false,
ignore_volume_keys: attributes.ignore_volume_keys,
}
}

Expand Down Expand Up @@ -327,8 +339,8 @@ impl<T: 'static> EventLoop<T> {
}

// 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);
Expand Down Expand Up @@ -395,67 +407,78 @@ impl<T: 'static> EventLoop<T> {
}
}
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::<ndk::event::KeyEvent>();
// 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::<ndk::event::KeyEvent>();
// 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
Expand Down

0 comments on commit 4a36741

Please sign in to comment.