@@ -21,6 +21,11 @@ const ANDROID_TOUCH_IDENTIFIER: i32 = 1;
2121const RUNNING_EMULATOR_CACHE_TTL : Duration = Duration :: from_secs ( 2 ) ;
2222const AVD_GRPC_PORT_CACHE_TTL : Duration = Duration :: from_secs ( 60 ) ;
2323const SCREEN_SIZE_CACHE_TTL : Duration = Duration :: from_secs ( 60 ) ;
24+ const MODIFIER_SHIFT : u32 = 1 << 0 ;
25+ const MODIFIER_CONTROL : u32 = 1 << 1 ;
26+ const MODIFIER_OPTION : u32 = 1 << 2 ;
27+ const MODIFIER_COMMAND : u32 = 1 << 3 ;
28+ const MODIFIER_CAPS_LOCK : u32 = 1 << 4 ;
2429
2530type TimedMap < T > = Option < ( Instant , HashMap < String , T > ) > ;
2631type ScreenSizeCache = HashMap < String , ( Instant , ( f64 , f64 ) ) > ;
@@ -316,39 +321,60 @@ impl AndroidBridge {
316321 Ok ( ( ) )
317322 }
318323
319- pub fn send_key ( & self , id : & str , key_code : u16 , _modifiers : u32 ) -> Result < ( ) , AppError > {
320- if self
321- . send_key_grpc ( id, grpc:: KeyboardEvent :: usb_keypress ( i32:: from ( key_code) ) )
322- . is_ok ( )
323- {
324- return Ok ( ( ) ) ;
324+ pub fn send_key ( & self , id : & str , key_code : u16 , modifiers : u32 ) -> Result < ( ) , AppError > {
325+ if let Some ( text) = hid_text_for_key ( key_code, modifiers) {
326+ return self . type_text_adb ( id, & text) ;
325327 }
328+
326329 let serial = self . serial_for_id ( id) ?;
327330 let android_key = android_key_code ( key_code) ;
328- self . run_adb ( [
329- "-s" ,
330- & serial,
331- "shell" ,
332- "input" ,
333- "keyevent" ,
334- & android_key. to_string ( ) ,
335- ] ) ?;
336- Ok ( ( ) )
331+ if has_android_key_modifiers ( modifiers) {
332+ return self . press_android_key_combination ( & serial, android_key, modifiers) ;
333+ }
334+ self . press_android_key ( & serial, android_key)
337335 }
338336
339337 pub fn type_text ( & self , id : & str , text : & str ) -> Result < ( ) , AppError > {
340- if self
341- . send_key_grpc ( id, grpc:: KeyboardEvent :: text ( text. to_owned ( ) ) )
342- . is_ok ( )
343- {
344- return Ok ( ( ) ) ;
345- }
338+ self . type_text_adb ( id, text)
339+ }
340+
341+ pub fn dismiss_keyboard ( & self , id : & str ) -> Result < ( ) , AppError > {
346342 let serial = self . serial_for_id ( id) ?;
347- let escaped = text. replace ( '%' , "%25" ) . replace ( ' ' , "%s" ) ;
343+ self . press_android_key ( & serial, 4 )
344+ }
345+
346+ fn type_text_adb ( & self , id : & str , text : & str ) -> Result < ( ) , AppError > {
347+ let serial = self . serial_for_id ( id) ?;
348+ let escaped = android_input_text_arg ( text) ;
348349 self . run_adb ( [ "-s" , & serial, "shell" , "input" , "text" , & escaped] ) ?;
349350 Ok ( ( ) )
350351 }
351352
353+ fn press_android_key ( & self , serial : & str , key_code : u16 ) -> Result < ( ) , AppError > {
354+ let key_code = key_code. to_string ( ) ;
355+ self . run_adb ( [ "-s" , serial, "shell" , "input" , "keyevent" , & key_code] ) ?;
356+ Ok ( ( ) )
357+ }
358+
359+ fn press_android_key_combination (
360+ & self ,
361+ serial : & str ,
362+ key_code : u16 ,
363+ modifiers : u32 ,
364+ ) -> Result < ( ) , AppError > {
365+ let mut parts = vec ! [ "input" . to_owned( ) , "keycombination" . to_owned( ) ] ;
366+ parts. extend (
367+ android_modifier_key_codes ( modifiers)
368+ . into_iter ( )
369+ . map ( |key| key. to_string ( ) ) ,
370+ ) ;
371+ parts. push ( key_code. to_string ( ) ) ;
372+ match self . run_adb_shell ( serial, & parts. join ( " " ) ) {
373+ Ok ( _) => Ok ( ( ) ) ,
374+ Err ( _) => self . press_android_key ( serial, key_code) ,
375+ }
376+ }
377+
352378 pub fn press_home ( & self , id : & str ) -> Result < ( ) , AppError > {
353379 let serial = self . serial_for_id ( id) ?;
354380 self . run_adb ( [ "-s" , & serial, "shell" , "input" , "keyevent" , "3" ] ) ?;
@@ -610,19 +636,6 @@ impl AndroidBridge {
610636 self . send_touch_grpc ( id, end_x, end_y, "ended" )
611637 }
612638
613- fn send_key_grpc ( & self , id : & str , event : grpc:: KeyboardEvent ) -> Result < ( ) , AppError > {
614- self . block_on_grpc ( async {
615- let avd_name = avd_from_id ( id) ?;
616- self . grpc_unary_for_avd :: < grpc:: KeyboardEvent , grpc:: Empty > (
617- & avd_name,
618- "/android.emulation.control.EmulatorController/sendKey" ,
619- event,
620- )
621- . await ?;
622- Ok ( ( ) )
623- } )
624- }
625-
626639 fn block_on_grpc < F , T > ( & self , future : F ) -> Result < T , AppError >
627640 where
628641 F : Future < Output = Result < T , AppError > > ,
@@ -1189,11 +1202,41 @@ fn android_role(node: roxmltree::Node<'_, '_>, class_name: &str) -> &'static str
11891202
11901203fn android_key_code ( hid : u16 ) -> u16 {
11911204 match hid {
1205+ 4 ..=29 => 29 + ( hid - 4 ) ,
1206+ 30 => 8 ,
1207+ 31 => 9 ,
1208+ 32 => 10 ,
1209+ 33 => 11 ,
1210+ 34 => 12 ,
1211+ 35 => 13 ,
1212+ 36 => 14 ,
1213+ 37 => 15 ,
1214+ 38 => 16 ,
1215+ 39 => 7 ,
11921216 40 => 66 ,
11931217 41 => 111 ,
11941218 42 => 67 ,
11951219 43 => 61 ,
11961220 44 => 62 ,
1221+ 45 => 69 ,
1222+ 46 => 70 ,
1223+ 47 => 71 ,
1224+ 48 => 72 ,
1225+ 49 => 73 ,
1226+ 51 => 74 ,
1227+ 52 => 75 ,
1228+ 53 => 68 ,
1229+ 54 => 55 ,
1230+ 55 => 56 ,
1231+ 56 => 76 ,
1232+ 57 => 115 ,
1233+ 58 ..=69 => 131 + ( hid - 58 ) ,
1234+ 73 => 124 ,
1235+ 74 => 122 ,
1236+ 75 => 92 ,
1237+ 76 => 112 ,
1238+ 77 => 123 ,
1239+ 78 => 93 ,
11971240 79 => 22 ,
11981241 80 => 21 ,
11991242 81 => 20 ,
@@ -1202,6 +1245,106 @@ fn android_key_code(hid: u16) -> u16 {
12021245 }
12031246}
12041247
1248+ fn hid_text_for_key ( hid : u16 , modifiers : u32 ) -> Option < String > {
1249+ if modifiers & ( MODIFIER_CONTROL | MODIFIER_OPTION | MODIFIER_COMMAND ) != 0 {
1250+ return None ;
1251+ }
1252+
1253+ let shifted = modifiers & MODIFIER_SHIFT != 0 ;
1254+ let caps_locked = modifiers & MODIFIER_CAPS_LOCK != 0 ;
1255+
1256+ if ( 4 ..=29 ) . contains ( & hid) {
1257+ let offset = ( hid - 4 ) as u8 ;
1258+ let base = if shifted ^ caps_locked { b'A' } else { b'a' } ;
1259+ return Some ( char:: from ( base + offset) . to_string ( ) ) ;
1260+ }
1261+
1262+ let text = match ( hid, shifted) {
1263+ ( 30 , false ) => "1" ,
1264+ ( 30 , true ) => "!" ,
1265+ ( 31 , false ) => "2" ,
1266+ ( 31 , true ) => "@" ,
1267+ ( 32 , false ) => "3" ,
1268+ ( 32 , true ) => "#" ,
1269+ ( 33 , false ) => "4" ,
1270+ ( 33 , true ) => "$" ,
1271+ ( 34 , false ) => "5" ,
1272+ ( 34 , true ) => "%" ,
1273+ ( 35 , false ) => "6" ,
1274+ ( 35 , true ) => "^" ,
1275+ ( 36 , false ) => "7" ,
1276+ ( 36 , true ) => "&" ,
1277+ ( 37 , false ) => "8" ,
1278+ ( 37 , true ) => "*" ,
1279+ ( 38 , false ) => "9" ,
1280+ ( 38 , true ) => "(" ,
1281+ ( 39 , false ) => "0" ,
1282+ ( 39 , true ) => ")" ,
1283+ ( 44 , _) => " " ,
1284+ ( 45 , false ) => "-" ,
1285+ ( 45 , true ) => "_" ,
1286+ ( 46 , false ) => "=" ,
1287+ ( 46 , true ) => "+" ,
1288+ ( 47 , false ) => "[" ,
1289+ ( 47 , true ) => "{" ,
1290+ ( 48 , false ) => "]" ,
1291+ ( 48 , true ) => "}" ,
1292+ ( 49 , false ) => "\\ " ,
1293+ ( 49 , true ) => "|" ,
1294+ ( 51 , false ) => ";" ,
1295+ ( 51 , true ) => ":" ,
1296+ ( 52 , false ) => "'" ,
1297+ ( 52 , true ) => "\" " ,
1298+ ( 53 , false ) => "`" ,
1299+ ( 53 , true ) => "~" ,
1300+ ( 54 , false ) => "," ,
1301+ ( 54 , true ) => "<" ,
1302+ ( 55 , false ) => "." ,
1303+ ( 55 , true ) => ">" ,
1304+ ( 56 , false ) => "/" ,
1305+ ( 56 , true ) => "?" ,
1306+ _ => return None ,
1307+ } ;
1308+ Some ( text. to_owned ( ) )
1309+ }
1310+
1311+ fn android_input_text_arg ( text : & str ) -> String {
1312+ let mut escaped = String :: new ( ) ;
1313+ for character in text. chars ( ) {
1314+ match character {
1315+ ' ' => escaped. push_str ( "%s" ) ,
1316+ '%' => escaped. push_str ( "%25" ) ,
1317+ '&' | '(' | ')' | '<' | '>' | ';' | '|' | '*' | '\\' | '"' | '\'' | '`' | '$' => {
1318+ escaped. push ( '\\' ) ;
1319+ escaped. push ( character) ;
1320+ }
1321+ _ => escaped. push ( character) ,
1322+ }
1323+ }
1324+ escaped
1325+ }
1326+
1327+ fn has_android_key_modifiers ( modifiers : u32 ) -> bool {
1328+ modifiers & ( MODIFIER_SHIFT | MODIFIER_CONTROL | MODIFIER_OPTION | MODIFIER_COMMAND ) != 0
1329+ }
1330+
1331+ fn android_modifier_key_codes ( modifiers : u32 ) -> Vec < u16 > {
1332+ let mut keys = Vec :: new ( ) ;
1333+ if modifiers & MODIFIER_CONTROL != 0 {
1334+ keys. push ( 113 ) ;
1335+ }
1336+ if modifiers & MODIFIER_OPTION != 0 {
1337+ keys. push ( 57 ) ;
1338+ }
1339+ if modifiers & MODIFIER_SHIFT != 0 {
1340+ keys. push ( 59 ) ;
1341+ }
1342+ if modifiers & MODIFIER_COMMAND != 0 {
1343+ keys. push ( 117 ) ;
1344+ }
1345+ keys
1346+ }
1347+
12051348fn android_log_level ( line : & str ) -> & ' static str {
12061349 if line. contains ( " E " ) {
12071350 "error"
@@ -1251,6 +1394,58 @@ mod tests {
12511394 assert_eq ! ( value[ "type" ] , "CustomTile" ) ;
12521395 assert_eq ! ( value[ "role" ] , "button" ) ;
12531396 }
1397+
1398+ #[ test]
1399+ fn android_key_code_maps_usb_hid_keyboard_usages ( ) {
1400+ assert_eq ! ( android_key_code( 4 ) , 29 ) ;
1401+ assert_eq ! ( android_key_code( 29 ) , 54 ) ;
1402+ assert_eq ! ( android_key_code( 30 ) , 8 ) ;
1403+ assert_eq ! ( android_key_code( 39 ) , 7 ) ;
1404+ assert_eq ! ( android_key_code( 40 ) , 66 ) ;
1405+ assert_eq ! ( android_key_code( 42 ) , 67 ) ;
1406+ assert_eq ! ( android_key_code( 58 ) , 131 ) ;
1407+ assert_eq ! ( android_key_code( 69 ) , 142 ) ;
1408+ assert_eq ! ( android_key_code( 73 ) , 124 ) ;
1409+ assert_eq ! ( android_key_code( 79 ) , 22 ) ;
1410+ }
1411+
1412+ #[ test]
1413+ fn hid_text_for_key_uses_shift_and_caps_for_printable_input ( ) {
1414+ assert_eq ! ( hid_text_for_key( 4 , 0 ) . as_deref( ) , Some ( "a" ) ) ;
1415+ assert_eq ! ( hid_text_for_key( 4 , MODIFIER_SHIFT ) . as_deref( ) , Some ( "A" ) ) ;
1416+ assert_eq ! (
1417+ hid_text_for_key( 4 , MODIFIER_CAPS_LOCK ) . as_deref( ) ,
1418+ Some ( "A" )
1419+ ) ;
1420+ assert_eq ! (
1421+ hid_text_for_key( 4 , MODIFIER_SHIFT | MODIFIER_CAPS_LOCK ) . as_deref( ) ,
1422+ Some ( "a" )
1423+ ) ;
1424+ assert_eq ! ( hid_text_for_key( 30 , 0 ) . as_deref( ) , Some ( "1" ) ) ;
1425+ assert_eq ! ( hid_text_for_key( 30 , MODIFIER_SHIFT ) . as_deref( ) , Some ( "!" ) ) ;
1426+ assert_eq ! ( hid_text_for_key( 56 , MODIFIER_SHIFT ) . as_deref( ) , Some ( "?" ) ) ;
1427+ assert_eq ! ( hid_text_for_key( 80 , 0 ) , None ) ;
1428+ assert_eq ! ( hid_text_for_key( 4 , MODIFIER_COMMAND ) , None ) ;
1429+ }
1430+
1431+ #[ test]
1432+ fn android_input_text_arg_escapes_adb_shell_text ( ) {
1433+ assert_eq ! ( android_input_text_arg( "hello world" ) , "hello%sworld" ) ;
1434+ assert_eq ! ( android_input_text_arg( "100%" ) , "100%25" ) ;
1435+ assert_eq ! ( android_input_text_arg( "a&b" ) , "a\\ &b" ) ;
1436+ }
1437+
1438+ #[ test]
1439+ fn android_modifier_key_codes_match_android_meta_keys ( ) {
1440+ assert_eq ! ( android_modifier_key_codes( MODIFIER_CONTROL ) , vec![ 113 ] ) ;
1441+ assert_eq ! ( android_modifier_key_codes( MODIFIER_OPTION ) , vec![ 57 ] ) ;
1442+ assert_eq ! ( android_modifier_key_codes( MODIFIER_SHIFT ) , vec![ 59 ] ) ;
1443+ assert_eq ! ( android_modifier_key_codes( MODIFIER_COMMAND ) , vec![ 117 ] ) ;
1444+ assert_eq ! (
1445+ android_modifier_key_codes( MODIFIER_CONTROL | MODIFIER_SHIFT ) ,
1446+ vec![ 113 , 59 ]
1447+ ) ;
1448+ }
12541449}
12551450
12561451#[ allow( dead_code) ]
@@ -1319,28 +1514,6 @@ mod grpc {
13191514 pub text : String ,
13201515 }
13211516
1322- impl KeyboardEvent {
1323- pub fn usb_keypress ( key_code : i32 ) -> Self {
1324- Self {
1325- code_type : keyboard_event:: KeyCodeType :: Usb as i32 ,
1326- event_type : keyboard_event:: KeyEventType :: Keypress as i32 ,
1327- key_code,
1328- key : String :: new ( ) ,
1329- text : String :: new ( ) ,
1330- }
1331- }
1332-
1333- pub fn text ( text : String ) -> Self {
1334- Self {
1335- code_type : keyboard_event:: KeyCodeType :: Usb as i32 ,
1336- event_type : keyboard_event:: KeyEventType :: Keypress as i32 ,
1337- key_code : 0 ,
1338- key : String :: new ( ) ,
1339- text,
1340- }
1341- }
1342- }
1343-
13441517 pub mod keyboard_event {
13451518 #[ derive(
13461519 Clone , Copy , Debug , PartialEq , Eq , Hash , PartialOrd , Ord , :: prost:: Enumeration ,
0 commit comments