Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android: Change default implementation to ignore volume keys and let operating system handle them #2748

Merged
Merged
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
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