From 000c70a64404807c3ad39fac0b359776571254ff Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 9 Feb 2018 19:15:13 +0100 Subject: [PATCH] Decompose accented characters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Characters like 'é' or 'î' are not resolved by getEvents(). For example, getEvents("é") returns null. However, it is possible to decompose them. For example, getEvents("\u0301e") returns the events generating "é". Thank you Philippe! ;) --- .../genymobile/scrcpy/EventController.java | 16 +- .../com/genymobile/scrcpy/KeyComposition.java | 174 ++++++++++++++++++ 2 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 server/src/main/java/com/genymobile/scrcpy/KeyComposition.java diff --git a/server/src/main/java/com/genymobile/scrcpy/EventController.java b/server/src/main/java/com/genymobile/scrcpy/EventController.java index 7c40cce0..8787a048 100644 --- a/server/src/main/java/com/genymobile/scrcpy/EventController.java +++ b/server/src/main/java/com/genymobile/scrcpy/EventController.java @@ -94,9 +94,13 @@ public class EventController { } private boolean injectText(String text) { + return injectText(text, true); + } + + private boolean injectText(String text, boolean decomposeOnFailure) { KeyEvent[] events = charMap.getEvents(text.toCharArray()); if (events == null) { - return false; + return decomposeOnFailure ? injectDecomposition(text) : false; } for (KeyEvent event : events) { if (!injectEvent(event)) { @@ -106,6 +110,16 @@ public class EventController { return true; } + private boolean injectDecomposition(String text) { + for (char c : text.toCharArray()) { + String composedText = KeyComposition.decompose(c); + if (composedText == null || !injectText(composedText, false)) { + return false; + } + } + return true; + } + private boolean injectMouse(int action, int buttons, Position position) { long now = SystemClock.uptimeMillis(); if (action == MotionEvent.ACTION_DOWN) { diff --git a/server/src/main/java/com/genymobile/scrcpy/KeyComposition.java b/server/src/main/java/com/genymobile/scrcpy/KeyComposition.java new file mode 100644 index 00000000..af22167d --- /dev/null +++ b/server/src/main/java/com/genymobile/scrcpy/KeyComposition.java @@ -0,0 +1,174 @@ +package com.genymobile.scrcpy; + +import java.util.HashMap; +import java.util.Map; + +/** + * Decompose accented characters. + *

+ * For example, {@link #decompose(char) decompose('é')} returns {@code "\u0301e"}. + *

+ * This is useful for injecting key events to generate the expected character ({@link android.view.KeyCharacterMap#getEvents(char[])} + * KeyCharacterMap.getEvents()} returns {@code null} with input {@code "é"} but works with input {@code "\u0301e"}). + *

+ * See diacritical dead key characters. + */ +public final class KeyComposition { + + private static final String KEY_DEAD_GRAVE = "\u0300"; + private static final String KEY_DEAD_ACUTE = "\u0301"; + private static final String KEY_DEAD_CIRCUMFLEX = "\u0302"; + private static final String KEY_DEAD_TILDE = "\u0303"; + private static final String KEY_DEAD_UMLAUT = "\u0308"; + + private static final Map COMPOSITION_MAP = createDecompositionMap(); + + private KeyComposition() { + // not instantiable + } + + public static String decompose(char c) { + return COMPOSITION_MAP.get(c); + } + + private static String grave(char c) { + return KEY_DEAD_GRAVE + c; + } + + private static String acute(char c) { + return KEY_DEAD_ACUTE + c; + } + + private static String circumflex(char c) { + return KEY_DEAD_CIRCUMFLEX + c; + } + + private static String tilde(char c) { + return KEY_DEAD_TILDE + c; + } + + private static String umlaut(char c) { + return KEY_DEAD_UMLAUT + c; + } + + private static Map createDecompositionMap() { + Map map = new HashMap<>(); + map.put('À', grave('A')); + map.put('È', grave('E')); + map.put('Ì', grave('I')); + map.put('Ò', grave('O')); + map.put('Ù', grave('U')); + map.put('à', grave('a')); + map.put('è', grave('e')); + map.put('ì', grave('i')); + map.put('ò', grave('o')); + map.put('ù', grave('u')); + map.put('Ǹ', grave('N')); + map.put('ǹ', grave('n')); + map.put('Ẁ', grave('W')); + map.put('ẁ', grave('w')); + map.put('Ỳ', grave('Y')); + map.put('ỳ', grave('y')); + + map.put('Á', acute('A')); + map.put('É', acute('E')); + map.put('Í', acute('I')); + map.put('Ó', acute('O')); + map.put('Ú', acute('U')); + map.put('Ý', acute('Y')); + map.put('á', acute('a')); + map.put('é', acute('e')); + map.put('í', acute('i')); + map.put('ó', acute('o')); + map.put('ú', acute('u')); + map.put('ý', acute('y')); + map.put('Ć', acute('C')); + map.put('ć', acute('c')); + map.put('Ĺ', acute('L')); + map.put('ĺ', acute('l')); + map.put('Ń', acute('N')); + map.put('ń', acute('n')); + map.put('Ŕ', acute('R')); + map.put('ŕ', acute('r')); + map.put('Ś', acute('S')); + map.put('ś', acute('s')); + map.put('Ź', acute('Z')); + map.put('ź', acute('z')); + map.put('Ǵ', acute('G')); + map.put('ǵ', acute('g')); + map.put('Ḉ', acute('Ç')); + map.put('ḉ', acute('ç')); + map.put('Ḱ', acute('K')); + map.put('ḱ', acute('k')); + map.put('Ḿ', acute('M')); + map.put('ḿ', acute('m')); + map.put('Ṕ', acute('P')); + map.put('ṕ', acute('p')); + map.put('Ẃ', acute('W')); + map.put('ẃ', acute('w')); + + map.put('Â', circumflex('A')); + map.put('Ê', circumflex('E')); + map.put('Î', circumflex('I')); + map.put('Ô', circumflex('O')); + map.put('Û', circumflex('U')); + map.put('â', circumflex('a')); + map.put('ê', circumflex('e')); + map.put('î', circumflex('i')); + map.put('ô', circumflex('o')); + map.put('û', circumflex('u')); + map.put('Ĉ', circumflex('C')); + map.put('ĉ', circumflex('c')); + map.put('Ĝ', circumflex('G')); + map.put('ĝ', circumflex('g')); + map.put('Ĥ', circumflex('H')); + map.put('ĥ', circumflex('h')); + map.put('Ĵ', circumflex('J')); + map.put('ĵ', circumflex('j')); + map.put('Ŝ', circumflex('S')); + map.put('ŝ', circumflex('s')); + map.put('Ŵ', circumflex('W')); + map.put('ŵ', circumflex('w')); + map.put('Ŷ', circumflex('Y')); + map.put('ŷ', circumflex('y')); + map.put('Ẑ', circumflex('Z')); + map.put('ẑ', circumflex('z')); + + map.put('Ã', tilde('A')); + map.put('Ñ', tilde('N')); + map.put('Õ', tilde('O')); + map.put('ã', tilde('a')); + map.put('ñ', tilde('n')); + map.put('õ', tilde('o')); + map.put('Ĩ', tilde('I')); + map.put('ĩ', tilde('i')); + map.put('Ũ', tilde('U')); + map.put('ũ', tilde('u')); + map.put('Ẽ', tilde('E')); + map.put('ẽ', tilde('e')); + map.put('Ỹ', tilde('Y')); + map.put('ỹ', tilde('y')); + + map.put('Ä', umlaut('A')); + map.put('Ë', umlaut('E')); + map.put('Ï', umlaut('I')); + map.put('Ö', umlaut('O')); + map.put('Ü', umlaut('U')); + map.put('ä', umlaut('a')); + map.put('ë', umlaut('e')); + map.put('ï', umlaut('i')); + map.put('ö', umlaut('o')); + map.put('ü', umlaut('u')); + map.put('ÿ', umlaut('y')); + map.put('Ÿ', umlaut('Y')); + map.put('Ḧ', umlaut('H')); + map.put('ḧ', umlaut('h')); + map.put('Ẅ', umlaut('W')); + map.put('ẅ', umlaut('w')); + map.put('Ẍ', umlaut('X')); + map.put('ẍ', umlaut('x')); + map.put('ẗ', umlaut('t')); + + return map; + } +}