Skip to main content

ferroui_android/platform/input/
android_keyboard_device.rs

1//! The keyboard device of the Android platform and the key codes of the system as keys of the framework.
2//!
3//! The reference derives its device from the keyboard device of the framework and adds nothing to it but the
4//! table of the key codes, so here [`AndroidKeyboardDevice::new`] gives the keyboard device of the framework
5//! itself. The reference takes a `Keycode` of the binding of the system; here the key code is the number
6//! `KeyEvent.getKeyCode()` returns, which the Java view passes as it is.
7
8use ferroui_base::input::{IKeyboardDevice, Key, KeyboardDevice};
9use ferroui_base::{FerroLocator, LocatorExtensions};
10use std::rc::Rc;
11
12/// `KeyEvent.KEYCODE_*`: the key codes the table below knows.
13pub(crate) mod keycode {
14    pub const BACK: i32 = 4;
15    pub const NUM_0: i32 = 7;
16    pub const NUM_1: i32 = 8;
17    pub const NUM_2: i32 = 9;
18    pub const NUM_3: i32 = 10;
19    pub const NUM_4: i32 = 11;
20    pub const NUM_5: i32 = 12;
21    pub const NUM_6: i32 = 13;
22    pub const NUM_7: i32 = 14;
23    pub const NUM_8: i32 = 15;
24    pub const NUM_9: i32 = 16;
25    pub const DPAD_UP: i32 = 19;
26    pub const DPAD_DOWN: i32 = 20;
27    pub const DPAD_LEFT: i32 = 21;
28    pub const DPAD_RIGHT: i32 = 22;
29    pub const DPAD_CENTER: i32 = 23;
30    pub const VOLUME_UP: i32 = 24;
31    pub const VOLUME_DOWN: i32 = 25;
32    pub const CLEAR: i32 = 28;
33    pub const A: i32 = 29;
34    pub const B: i32 = 30;
35    pub const C: i32 = 31;
36    pub const D: i32 = 32;
37    pub const E: i32 = 33;
38    pub const F: i32 = 34;
39    pub const G: i32 = 35;
40    pub const H: i32 = 36;
41    pub const I: i32 = 37;
42    pub const J: i32 = 38;
43    pub const K: i32 = 39;
44    pub const L: i32 = 40;
45    pub const M: i32 = 41;
46    pub const N: i32 = 42;
47    pub const O: i32 = 43;
48    pub const P: i32 = 44;
49    pub const Q: i32 = 45;
50    pub const R: i32 = 46;
51    pub const S: i32 = 47;
52    pub const T: i32 = 48;
53    pub const U: i32 = 49;
54    pub const V: i32 = 50;
55    pub const W: i32 = 51;
56    pub const X: i32 = 52;
57    pub const Y: i32 = 53;
58    pub const Z: i32 = 54;
59    pub const COMMA: i32 = 55;
60    pub const PERIOD: i32 = 56;
61    pub const ALT_LEFT: i32 = 57;
62    pub const ALT_RIGHT: i32 = 58;
63    pub const SHIFT_LEFT: i32 = 59;
64    pub const SHIFT_RIGHT: i32 = 60;
65    pub const TAB: i32 = 61;
66    pub const SPACE: i32 = 62;
67    pub const ENTER: i32 = 66;
68    pub const DEL: i32 = 67;
69    pub const GRAVE: i32 = 68;
70    pub const MINUS: i32 = 69;
71    pub const LEFT_BRACKET: i32 = 71;
72    pub const RIGHT_BRACKET: i32 = 72;
73    pub const BACKSLASH: i32 = 73;
74    pub const SEMICOLON: i32 = 74;
75    pub const APOSTROPHE: i32 = 75;
76    pub const SLASH: i32 = 76;
77    pub const PLUS: i32 = 81;
78    pub const MEDIA_PLAY_PAUSE: i32 = 85;
79    pub const MEDIA_STOP: i32 = 86;
80    pub const MEDIA_NEXT: i32 = 87;
81    pub const MEDIA_PREVIOUS: i32 = 88;
82    pub const PAGE_UP: i32 = 92;
83    pub const PAGE_DOWN: i32 = 93;
84    pub const ESCAPE: i32 = 111;
85    pub const FORWARD_DEL: i32 = 112;
86    pub const CTRL_LEFT: i32 = 113;
87    pub const CTRL_RIGHT: i32 = 114;
88    pub const CAPS_LOCK: i32 = 115;
89    pub const SCROLL_LOCK: i32 = 116;
90    pub const MOVE_HOME: i32 = 122;
91    pub const MOVE_END: i32 = 123;
92    pub const INSERT: i32 = 124;
93    pub const MEDIA_PLAY: i32 = 126;
94    pub const MEDIA_PAUSE: i32 = 127;
95    pub const F1: i32 = 131;
96    pub const F2: i32 = 132;
97    pub const F3: i32 = 133;
98    pub const F4: i32 = 134;
99    pub const F5: i32 = 135;
100    pub const F6: i32 = 136;
101    pub const F7: i32 = 137;
102    pub const F8: i32 = 138;
103    pub const F9: i32 = 139;
104    pub const F10: i32 = 140;
105    pub const F11: i32 = 141;
106    pub const F12: i32 = 142;
107    pub const NUM_LOCK: i32 = 143;
108    pub const NUMPAD_0: i32 = 144;
109    pub const NUMPAD_1: i32 = 145;
110    pub const NUMPAD_2: i32 = 146;
111    pub const NUMPAD_3: i32 = 147;
112    pub const NUMPAD_4: i32 = 148;
113    pub const NUMPAD_5: i32 = 149;
114    pub const NUMPAD_6: i32 = 150;
115    pub const NUMPAD_7: i32 = 151;
116    pub const NUMPAD_8: i32 = 152;
117    pub const NUMPAD_9: i32 = 153;
118    pub const NUMPAD_DIVIDE: i32 = 154;
119    pub const NUMPAD_MULTIPLY: i32 = 155;
120    pub const NUMPAD_SUBTRACT: i32 = 156;
121    pub const NUMPAD_ADD: i32 = 157;
122    pub const NUMPAD_DOT: i32 = 158;
123    pub const NUMPAD_COMMA: i32 = 159;
124    pub const SLEEP: i32 = 223;
125    pub const HELP: i32 = 259;
126}
127
128/// The keyboard device of the Android platform.
129pub struct AndroidKeyboardDevice;
130
131impl AndroidKeyboardDevice {
132    /// The keyboard device that is registered with the locator, if there is one.
133    pub fn instance() -> Option<Rc<dyn IKeyboardDevice>> {
134        FerroLocator::current().get_service::<dyn IKeyboardDevice>()
135    }
136
137    /// A keyboard device: the one of the framework, to which the reference adds nothing.
138    #[allow(clippy::new_ret_no_self)]
139    pub fn new() -> Rc<dyn IKeyboardDevice> {
140        KeyboardDevice::new()
141    }
142
143    /// The key of the framework for a key code of the system (`KeyEvent.KEYCODE_*`), [`Key::None`] for a key
144    /// code the table does not have.
145    pub fn convert_key(key: i32) -> Key {
146        match key {
147            //   keycode::CANCEL? => Key::Cancel,
148            keycode::DEL => Key::Back,
149            keycode::TAB => Key::Tab,
150            //  keycode::LINEFEED? => Key::LineFeed,
151            keycode::CLEAR => Key::Clear,
152            keycode::ENTER => Key::Return,
153            keycode::MEDIA_PAUSE => Key::Pause,
154            keycode::CAPS_LOCK => Key::CapsLock,
155            // ? => Key::HangulMode
156            // ? => Key::JunjaMode
157            // ? => Key::FinalMode
158            // ? => Key::KanjiMode
159            keycode::ESCAPE => Key::Escape,
160            // ? => Key::ImeConvert
161            // ? => Key::ImeNonConvert
162            // ? => Key::ImeAccept
163            // ? => Key::ImeModeChange
164            keycode::SPACE => Key::Space,
165            keycode::PAGE_UP => Key::Prior,
166            keycode::PAGE_DOWN => Key::PageDown,
167            keycode::MOVE_END => Key::End,
168            keycode::MOVE_HOME => Key::Home,
169            // keycode::BUTTON_SELECT? => Key::Select,
170            // keycode::print? => Key::Print,
171            // keycode::execute? => Key::Execute,
172            // keycode::snap? => Key::Snapshot
173            keycode::INSERT => Key::Insert,
174            keycode::FORWARD_DEL => Key::Delete,
175            keycode::HELP => Key::Help,
176            keycode::NUM_0 => Key::D0,
177            keycode::NUM_1 => Key::D1,
178            keycode::NUM_2 => Key::D2,
179            keycode::NUM_3 => Key::D3,
180            keycode::NUM_4 => Key::D4,
181            keycode::NUM_5 => Key::D5,
182            keycode::NUM_6 => Key::D6,
183            keycode::NUM_7 => Key::D7,
184            keycode::NUM_8 => Key::D8,
185            keycode::NUM_9 => Key::D9,
186            keycode::A => Key::A,
187            keycode::B => Key::B,
188            keycode::C => Key::C,
189            keycode::D => Key::D,
190            keycode::E => Key::E,
191            keycode::F => Key::F,
192            keycode::G => Key::G,
193            keycode::H => Key::H,
194            keycode::I => Key::I,
195            keycode::J => Key::J,
196            keycode::K => Key::K,
197            keycode::L => Key::L,
198            keycode::M => Key::M,
199            keycode::N => Key::N,
200            keycode::O => Key::O,
201            keycode::P => Key::P,
202            keycode::Q => Key::Q,
203            keycode::R => Key::R,
204            keycode::S => Key::S,
205            keycode::T => Key::T,
206            keycode::U => Key::U,
207            keycode::V => Key::V,
208            keycode::W => Key::W,
209            keycode::X => Key::X,
210            keycode::Y => Key::Y,
211            keycode::Z => Key::Z,
212            // keycode::a => Key::A,
213            // keycode::b => Key::B,
214            // keycode::c => Key::C,
215            // keycode::d => Key::D,
216            // keycode::e => Key::E,
217            // keycode::f => Key::F,
218            // keycode::g => Key::G,
219            // keycode::h => Key::H,
220            // keycode::i => Key::I,
221            // keycode::j => Key::J,
222            // keycode::k => Key::K,
223            // keycode::l => Key::L,
224            // keycode::m => Key::M,
225            // keycode::n => Key::N,
226            // keycode::o => Key::O,
227            // keycode::p => Key::P,
228            // keycode::q => Key::Q,
229            // keycode::r => Key::R,
230            // keycode::s => Key::S,
231            // keycode::t => Key::T,
232            // keycode::u => Key::U,
233            // keycode::v => Key::V,
234            // keycode::w => Key::W,
235            // keycode::x => Key::X,
236            // keycode::y => Key::Y,
237            // keycode::z => Key::Z,
238            // ? => Key::LWin
239            // ? => Key::RWin
240            // ? => Key::Apps
241            keycode::SLEEP => Key::Sleep,
242            keycode::NUMPAD_0 => Key::NumPad0,
243            keycode::NUMPAD_1 => Key::NumPad1,
244            keycode::NUMPAD_2 => Key::NumPad2,
245            keycode::NUMPAD_3 => Key::NumPad3,
246            keycode::NUMPAD_4 => Key::NumPad4,
247            keycode::NUMPAD_5 => Key::NumPad5,
248            keycode::NUMPAD_6 => Key::NumPad6,
249            keycode::NUMPAD_7 => Key::NumPad7,
250            keycode::NUMPAD_8 => Key::NumPad8,
251            keycode::NUMPAD_9 => Key::NumPad9,
252            keycode::NUMPAD_MULTIPLY => Key::Multiply,
253            keycode::NUMPAD_ADD => Key::Add,
254            keycode::NUMPAD_COMMA => Key::Separator,
255            keycode::NUMPAD_SUBTRACT => Key::Subtract,
256            keycode::NUMPAD_DOT => Key::Decimal,
257            keycode::NUMPAD_DIVIDE => Key::Divide,
258            keycode::F1 => Key::F1,
259            keycode::F2 => Key::F2,
260            keycode::F3 => Key::F3,
261            keycode::F4 => Key::F4,
262            keycode::F5 => Key::F5,
263            keycode::F6 => Key::F6,
264            keycode::F7 => Key::F7,
265            keycode::F8 => Key::F8,
266            keycode::F9 => Key::F9,
267            keycode::F10 => Key::F10,
268            keycode::F11 => Key::F11,
269            keycode::F12 => Key::F12,
270            // keycode::f13 => Key::F13,
271            // keycode::F14 => Key::F14,
272            // keycode::L5 => Key::F15,
273            // keycode::F16 => Key::F16,
274            // keycode::F17 => Key::F17,
275            // keycode::L8 => Key::F18,
276            // keycode::L9 => Key::F19,
277            // keycode::L10 => Key::F20,
278            // keycode::R1 => Key::F21,
279            // keycode::R2 => Key::F22,
280            // keycode::F23 => Key::F23,
281            // keycode::R4 => Key::F24,
282            keycode::NUM_LOCK => Key::NumLock,
283            keycode::SCROLL_LOCK => Key::Scroll,
284            keycode::SHIFT_LEFT => Key::LeftShift,
285            keycode::SHIFT_RIGHT => Key::RightShift,
286            keycode::CTRL_LEFT => Key::LeftCtrl,
287            keycode::CTRL_RIGHT => Key::RightCtrl,
288            keycode::ALT_LEFT => Key::LeftAlt,
289            keycode::ALT_RIGHT => Key::RightAlt,
290            // ? => Key::BrowserBack
291            // ? => Key::BrowserForward
292            // ? => Key::BrowserRefresh
293            // ? => Key::BrowserStop
294            // ? => Key::BrowserSearch
295            // ? => Key::BrowserFavorites
296            // ? => Key::BrowserHome
297            // ? => Key::VolumeMute
298            keycode::VOLUME_DOWN => Key::VolumeDown,
299            keycode::VOLUME_UP => Key::VolumeUp,
300            keycode::MEDIA_NEXT => Key::MediaNextTrack,
301            keycode::MEDIA_PREVIOUS => Key::MediaPreviousTrack,
302            keycode::MEDIA_STOP => Key::MediaStop,
303            keycode::MEDIA_PLAY_PAUSE => Key::MediaPlayPause,
304            // ? => Key::LaunchMail
305            // ? => Key::SelectMedia
306            // ? => Key::LaunchApplication1
307            // ? => Key::LaunchApplication2
308            keycode::SEMICOLON => Key::OemSemicolon,
309            keycode::PLUS => Key::OemPlus,
310            keycode::COMMA => Key::OemComma,
311            keycode::MINUS => Key::OemMinus,
312            keycode::PERIOD => Key::OemPeriod,
313            // ? => Key::Oem2
314            keycode::GRAVE => Key::OemTilde,
315            // ? => Key::AbntC1
316            // ? => Key::AbntC2
317            // ? => Key::OemPipe
318            keycode::APOSTROPHE => Key::OemQuotes,
319            keycode::SLASH => Key::OemQuestion,
320            keycode::LEFT_BRACKET => Key::OemOpenBrackets,
321            keycode::RIGHT_BRACKET => Key::OemCloseBrackets,
322            // ? => Key::Oem7
323            // ? => Key::Oem8
324            // ? => Key::Oem102
325            // ? => Key::ImeProcessed
326            // ? => Key::System
327            // ? => Key::OemAttn
328            // ? => Key::OemFinish
329            // ? => Key::DbeHiragana
330            // ? => Key::OemAuto
331            // ? => Key::DbeDbcsChar
332            // ? => Key::OemBackTab
333            // ? => Key::Attn
334            // ? => Key::DbeEnterWordRegisterMode
335            // ? => Key::DbeEnterImeConfigureMode
336            // ? => Key::EraseEof
337            keycode::MEDIA_PLAY => Key::Play,
338            // ? => Key::Zoom
339            // ? => Key::NoName
340            // ? => Key::DbeEnterDialogConversionMode
341            // ? => Key::OemClear
342            // ? => Key::DeadCharProcessed
343            keycode::BACKSLASH => Key::OemBackslash,
344
345            // Loosely mapping DPad keys to the keys of the framework
346            keycode::BACK => Key::Escape,
347            keycode::DPAD_CENTER => Key::Space,
348            keycode::DPAD_LEFT => Key::Left,
349            keycode::DPAD_UP => Key::Up,
350            keycode::DPAD_RIGHT => Key::Right,
351            keycode::DPAD_DOWN => Key::Down,
352            _ => Key::None,
353        }
354    }
355}
356
357#[cfg(test)]
358mod tests {
359    use super::*;
360
361    #[test]
362    fn letters_are_the_letter_keys() {
363        let letters = [
364            Key::A, Key::B, Key::C, Key::D, Key::E, Key::F, Key::G, Key::H, Key::I, Key::J, Key::K, Key::L, Key::M,
365            Key::N, Key::O, Key::P, Key::Q, Key::R, Key::S, Key::T, Key::U, Key::V, Key::W, Key::X, Key::Y, Key::Z,
366        ];
367        // KEYCODE_A is 29 and the letters follow it in the order of the alphabet.
368        for (index, letter) in letters.iter().enumerate() {
369            assert_eq!(AndroidKeyboardDevice::convert_key(29 + index as i32), *letter);
370        }
371    }
372
373    #[test]
374    fn digits_are_the_digit_keys() {
375        let digits = [Key::D0, Key::D1, Key::D2, Key::D3, Key::D4, Key::D5, Key::D6, Key::D7, Key::D8, Key::D9];
376        // KEYCODE_0 is 7, KEYCODE_NUMPAD_0 is 144.
377        for (index, digit) in digits.iter().enumerate() {
378            assert_eq!(AndroidKeyboardDevice::convert_key(7 + index as i32), *digit);
379        }
380        assert_eq!(AndroidKeyboardDevice::convert_key(144), Key::NumPad0);
381        assert_eq!(AndroidKeyboardDevice::convert_key(153), Key::NumPad9);
382    }
383
384    #[test]
385    fn named_keys() {
386        assert_eq!(AndroidKeyboardDevice::convert_key(66), Key::Return);
387        assert_eq!(AndroidKeyboardDevice::convert_key(66), Key::Enter);
388        assert_eq!(AndroidKeyboardDevice::convert_key(67), Key::Back);
389        assert_eq!(AndroidKeyboardDevice::convert_key(112), Key::Delete);
390        assert_eq!(AndroidKeyboardDevice::convert_key(111), Key::Escape);
391        assert_eq!(AndroidKeyboardDevice::convert_key(61), Key::Tab);
392        assert_eq!(AndroidKeyboardDevice::convert_key(62), Key::Space);
393        assert_eq!(AndroidKeyboardDevice::convert_key(92), Key::PageUp);
394        assert_eq!(AndroidKeyboardDevice::convert_key(93), Key::PageDown);
395        assert_eq!(AndroidKeyboardDevice::convert_key(122), Key::Home);
396        assert_eq!(AndroidKeyboardDevice::convert_key(123), Key::End);
397        assert_eq!(AndroidKeyboardDevice::convert_key(131), Key::F1);
398        assert_eq!(AndroidKeyboardDevice::convert_key(142), Key::F12);
399        assert_eq!(AndroidKeyboardDevice::convert_key(59), Key::LeftShift);
400        assert_eq!(AndroidKeyboardDevice::convert_key(114), Key::RightCtrl);
401    }
402
403    #[test]
404    fn the_back_key_and_the_directional_pad() {
405        assert_eq!(AndroidKeyboardDevice::convert_key(4), Key::Escape);
406        assert_eq!(AndroidKeyboardDevice::convert_key(23), Key::Space);
407        assert_eq!(AndroidKeyboardDevice::convert_key(21), Key::Left);
408        assert_eq!(AndroidKeyboardDevice::convert_key(19), Key::Up);
409        assert_eq!(AndroidKeyboardDevice::convert_key(22), Key::Right);
410        assert_eq!(AndroidKeyboardDevice::convert_key(20), Key::Down);
411    }
412
413    #[test]
414    fn an_unknown_key_code_is_no_key() {
415        // KEYCODE_UNKNOWN, KEYCODE_HOME (which the table does not have), and numbers that are no key code.
416        assert_eq!(AndroidKeyboardDevice::convert_key(0), Key::None);
417        assert_eq!(AndroidKeyboardDevice::convert_key(3), Key::None);
418        assert_eq!(AndroidKeyboardDevice::convert_key(-1), Key::None);
419        assert_eq!(AndroidKeyboardDevice::convert_key(100_000), Key::None);
420    }
421}