scrcpy-master/server/src/main/java/com/genymobile/scrcpy/KeyComposition.java
package com.genymobile.scrcpy;

import java.util.HashMap;
import java.util.Map;

/**
 * Decompose accented characters.
 * <p>
 * For example, {@link #decompose(char) decompose('é')} returns {@code "\u0301e"}.
 * <p>
 * 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"}).
 * <p>
 * See <a href="https://source.android.com/devices/input/key-character-map-files#behaviors">diacritical dead key characters</a>.
 */
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<Character, String> 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<Character, String> createDecompositionMap() {
        Map<Character, String> 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;
    }
}
