- Pen vs. finger (#44): the patched winit hands Android pointers with tool type stylus/eraser to the app separately (take_pen_samples, with hover and button state) instead of as touch events. src/pen.rs turns them, like octotablet's events on the desktop, into one PenInput stream, so pen buttons, the eraser end and palm rejection work the same everywhere. Hover exit right before touch-down does not count as leaving; a pen without hover leaves 150 ms after lifting. - Soft keyboard: shown via AndroidApp::show_soft_input when a text field gets focus, hidden when it loses it. The patched winit now fills KeyEvent::text, so typed characters reach egui. - Exports (PDF, PNG, SVG) are written to the app folder and copied to Download/ink-notes through the MediaStore (Android 10+, via JNI). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
129 lines
4.3 KiB
Rust
129 lines
4.3 KiB
Rust
use crate::{
|
|
event_loop::{EventLoop, EventLoopBuilder, EventLoopWindowTarget},
|
|
window::{Window, WindowBuilder},
|
|
};
|
|
|
|
use android_activity::{AndroidApp, ConfigurationRef, Rect};
|
|
|
|
/// Additional methods on [`EventLoop`] that are specific to Android.
|
|
pub trait EventLoopExtAndroid {}
|
|
|
|
impl<T> EventLoopExtAndroid for EventLoop<T> {}
|
|
|
|
/// Additional methods on [`EventLoopWindowTarget`] that are specific to Android.
|
|
pub trait EventLoopWindowTargetExtAndroid {}
|
|
|
|
/// Additional methods on [`Window`] that are specific to Android.
|
|
pub trait WindowExtAndroid {
|
|
fn content_rect(&self) -> Rect;
|
|
|
|
fn config(&self) -> ConfigurationRef;
|
|
}
|
|
|
|
impl WindowExtAndroid for Window {
|
|
fn content_rect(&self) -> Rect {
|
|
self.window.content_rect()
|
|
}
|
|
|
|
fn config(&self) -> ConfigurationRef {
|
|
self.window.config()
|
|
}
|
|
}
|
|
|
|
impl<T> EventLoopWindowTargetExtAndroid for EventLoopWindowTarget<T> {}
|
|
|
|
/// Additional methods on [`WindowBuilder`] that are specific to Android.
|
|
pub trait WindowBuilderExtAndroid {}
|
|
|
|
impl WindowBuilderExtAndroid for WindowBuilder {}
|
|
|
|
pub trait EventLoopBuilderExtAndroid {
|
|
/// Associates the `AndroidApp` that was passed to `android_main()` with the event loop
|
|
///
|
|
/// 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
|
|
///
|
|
/// Winit re-exports the `android_activity` API for convenience so that most
|
|
/// applications can rely on the Winit crate to resolve the required version of
|
|
/// `android_activity` and avoid any chance of a conflict between Winit and the
|
|
/// application crate.
|
|
///
|
|
/// Unlike most libraries there can only be a single implementation
|
|
/// of the `android_activity` glue crate linked with an application because
|
|
/// it is responsible for the application's `android_main()` entry point.
|
|
///
|
|
/// Since Winit depends on a specific version of `android_activity` the simplest
|
|
/// way to avoid creating a conflict is for applications to avoid explicitly
|
|
/// depending on the `android_activity` crate, and instead consume the API that
|
|
/// is re-exported by Winit.
|
|
///
|
|
/// ink-notes patch: what a pen sample reports (Issue #44).
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum PenAction {
|
|
/// Pen comes near the screen, moves above it or leaves it
|
|
HoverEnter,
|
|
HoverMove,
|
|
HoverExit,
|
|
/// Pen touches the screen, moves on it, is lifted or the gesture is cancelled
|
|
Down,
|
|
Move,
|
|
Up,
|
|
Cancel,
|
|
}
|
|
|
|
/// ink-notes patch: one sample of a pen (tool type stylus or eraser). Such pointers are not
|
|
/// reported as `WindowEvent::Touch`; the app collects them with [`take_pen_samples`].
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
pub struct PenSample {
|
|
pub action: PenAction,
|
|
/// Position in physical pixels
|
|
pub x: f32,
|
|
pub y: f32,
|
|
/// Usually 0..=1
|
|
pub pressure: f32,
|
|
/// Eraser end of the pen (tool type eraser)
|
|
pub eraser: bool,
|
|
/// `MotionEvent.getButtonState()`; the pen buttons are 0x20 (primary) and 0x40 (secondary)
|
|
pub buttons: u32,
|
|
}
|
|
|
|
/// ink-notes patch: the pen samples received since the last call, oldest first.
|
|
pub fn take_pen_samples() -> Vec<PenSample> {
|
|
crate::platform_impl::take_pen_samples()
|
|
}
|
|
|
|
/// For compatibility applications should then import the `AndroidApp` type for
|
|
/// their `android_main(app: AndroidApp)` function like:
|
|
/// ```rust
|
|
/// #[cfg(target_os="android")]
|
|
/// use winit::platform::android::activity::AndroidApp;
|
|
/// ```
|
|
pub mod activity {
|
|
// We enable the `"native-activity"` feature just so that we can build the
|
|
// docs, but it'll be very confusing for users to see the docs with that
|
|
// feature enabled, so we avoid inlining it so that they're forced to view
|
|
// it on the crate's own docs.rs page.
|
|
#[doc(no_inline)]
|
|
pub use android_activity::*;
|
|
}
|