forked from extern/Unexpected-Keyboard
f4032e3be9
It allowed to modulate the repeat speed of some keys (arrow, backspace, delete) by move the finger farther or closer to the key. In practice, this wasn't pratical and doesn't seem popular. It is removed in favor of a better mechanism for moving the cursor.
403 lines
12 KiB
Java
403 lines
12 KiB
Java
package juloo.keyboard2;
|
|
|
|
import android.view.KeyCharacterMap;
|
|
import android.view.KeyEvent;
|
|
import java.util.HashMap;
|
|
|
|
final class KeyValue
|
|
{
|
|
public static enum Event
|
|
{
|
|
CONFIG,
|
|
SWITCH_TEXT,
|
|
SWITCH_NUMERIC,
|
|
SWITCH_EMOJI,
|
|
SWITCH_BACK_EMOJI,
|
|
CHANGE_METHOD,
|
|
ACTION,
|
|
SWITCH_SECOND,
|
|
SWITCH_SECOND_BACK,
|
|
SWITCH_GREEKMATH,
|
|
CAPS_LOCK,
|
|
}
|
|
|
|
// Must be evaluated in the reverse order of their values.
|
|
public static enum Modifier
|
|
{
|
|
SHIFT,
|
|
CTRL,
|
|
ALT,
|
|
META,
|
|
DOUBLE_AIGU,
|
|
DOT_ABOVE,
|
|
GRAVE,
|
|
AIGU,
|
|
CIRCONFLEXE,
|
|
TILDE,
|
|
CEDILLE,
|
|
TREMA,
|
|
SUPERSCRIPT,
|
|
SUBSCRIPT,
|
|
RING,
|
|
CARON,
|
|
MACRON,
|
|
ORDINAL,
|
|
ARROWS,
|
|
BOX,
|
|
OGONEK,
|
|
SLASH,
|
|
ARROW_RIGHT,
|
|
BREVE,
|
|
BAR,
|
|
FN, // Must be placed last to be applied first
|
|
}
|
|
|
|
public static enum Editing
|
|
{
|
|
COPY,
|
|
PASTE,
|
|
CUT,
|
|
SELECT_ALL,
|
|
PASTE_PLAIN,
|
|
UNDO,
|
|
REDO,
|
|
// Android context menu actions
|
|
REPLACE,
|
|
SHARE,
|
|
ASSIST,
|
|
AUTOFILL,
|
|
}
|
|
|
|
public static enum Kind
|
|
{
|
|
Char, String, Keyevent, Event, Modifier, Editing
|
|
}
|
|
|
|
// Behavior flags.
|
|
public static final int FLAG_LATCH = (1 << 20);
|
|
public static final int FLAG_LOCK = (1 << 21);
|
|
// Special keys are not repeated and don't clear latched modifiers.
|
|
public static final int FLAG_SPECIAL = (1 << 22);
|
|
// Free flag: (1 << 23);
|
|
// Rendering flags.
|
|
public static final int FLAG_KEY_FONT = (1 << 24); // special font file
|
|
public static final int FLAG_SMALLER_FONT = (1 << 25); // 25% smaller symbols
|
|
public static final int FLAG_SECONDARY = (1 << 26); // dimmer
|
|
// Used by [Pointers].
|
|
public static final int FLAG_LOCKED = (1 << 28);
|
|
public static final int FLAG_FAKE_PTR = (1 << 29);
|
|
|
|
// Ranges for the different components
|
|
private static final int FLAGS_BITS = (0b111111111 << 20); // 9 bits wide
|
|
private static final int KIND_BITS = (0b111 << 29); // 3 bits wide
|
|
private static final int VALUE_BITS = ~(FLAGS_BITS | KIND_BITS); // 20 bits wide
|
|
static
|
|
{
|
|
check((FLAGS_BITS & KIND_BITS) == 0); // No overlap
|
|
check((FLAGS_BITS | KIND_BITS | VALUE_BITS) == ~0); // No holes
|
|
}
|
|
|
|
private final String _symbol;
|
|
|
|
/** This field encodes three things: Kind, flags and value. */
|
|
private final int _code;
|
|
|
|
public Kind getKind()
|
|
{
|
|
return Kind.values()[(_code & KIND_BITS) >>> 29];
|
|
}
|
|
|
|
public int getFlags()
|
|
{
|
|
return (_code & FLAGS_BITS);
|
|
}
|
|
|
|
public boolean hasFlags(int has)
|
|
{
|
|
return ((_code & has) == has);
|
|
}
|
|
|
|
/** The string to render on the keyboard.
|
|
When [getKind() == Kind.String], also the string to send. */
|
|
public String getString()
|
|
{
|
|
return _symbol;
|
|
}
|
|
|
|
/** Defined only when [getKind() == Kind.Char]. */
|
|
public char getChar()
|
|
{
|
|
return (char)(_code & VALUE_BITS);
|
|
}
|
|
|
|
/** Defined only when [getKind() == Kind.Keyevent]. */
|
|
public int getKeyevent()
|
|
{
|
|
return (_code & VALUE_BITS);
|
|
}
|
|
|
|
/** Defined only when [getKind() == Kind.Event]. */
|
|
public Event getEvent()
|
|
{
|
|
return Event.values()[(_code & VALUE_BITS)];
|
|
}
|
|
|
|
/** Defined only when [getKind() == Kind.Modifier]. */
|
|
public Modifier getModifier()
|
|
{
|
|
return Modifier.values()[(_code & VALUE_BITS)];
|
|
}
|
|
|
|
/** Defined only when [getKind() == Kind.Editing]. */
|
|
public Editing getEditing()
|
|
{
|
|
return Editing.values()[(_code & VALUE_BITS)];
|
|
}
|
|
|
|
/* Update the char and the symbol. */
|
|
public KeyValue withChar(char c)
|
|
{
|
|
return new KeyValue(String.valueOf(c), Kind.Char, c, getFlags());
|
|
}
|
|
|
|
public KeyValue withString(String s)
|
|
{
|
|
return new KeyValue(s, Kind.String, 0, getFlags());
|
|
}
|
|
|
|
public KeyValue withSymbol(String s)
|
|
{
|
|
return new KeyValue(s, (_code & KIND_BITS), (_code & VALUE_BITS), getFlags());
|
|
}
|
|
|
|
public KeyValue withKeyevent(int code)
|
|
{
|
|
return new KeyValue(_symbol, Kind.Keyevent, code, getFlags());
|
|
}
|
|
|
|
public KeyValue withFlags(int f)
|
|
{
|
|
return new KeyValue(_symbol, (_code & KIND_BITS), (_code & VALUE_BITS), f);
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object obj)
|
|
{
|
|
KeyValue snd = (KeyValue)obj;
|
|
return _symbol.equals(snd._symbol) && _code == snd._code;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode()
|
|
{
|
|
return _symbol.hashCode() + _code;
|
|
}
|
|
|
|
private static HashMap<String, KeyValue> keys = new HashMap<String, KeyValue>();
|
|
|
|
public KeyValue(String s, int kind, int value, int flags)
|
|
{
|
|
check((kind & ~KIND_BITS) == 0);
|
|
check((flags & ~FLAGS_BITS) == 0);
|
|
check((value & ~VALUE_BITS) == 0);
|
|
_symbol = s;
|
|
_code = kind | flags | value;
|
|
}
|
|
|
|
public KeyValue(String s, Kind k, int v, int f)
|
|
{
|
|
this(s, (k.ordinal() << 29), v, f);
|
|
}
|
|
|
|
public static KeyValue getKeyByName(String name)
|
|
{
|
|
KeyValue kv = keys.get(name);
|
|
if (kv != null)
|
|
return kv;
|
|
if (name.length() == 1)
|
|
return new KeyValue(name, Kind.Char, name.charAt(0), 0);
|
|
else
|
|
return new KeyValue(name, Kind.String, 0, 0);
|
|
}
|
|
|
|
private static void addKey(String name, String symbol, Kind kind, int code, int flags)
|
|
{
|
|
keys.put(name, new KeyValue(symbol, kind, code, flags));
|
|
}
|
|
|
|
private static void addCharKey(String name, String symbol, char c, int flags)
|
|
{
|
|
addKey(name, symbol, Kind.Char, c, flags);
|
|
}
|
|
|
|
private static void addModifierKey(String name, String symbol, Modifier m, int flags)
|
|
{
|
|
if (symbol.length() > 1)
|
|
flags |= FLAG_SMALLER_FONT;
|
|
addKey(name, symbol, Kind.Modifier, m.ordinal(),
|
|
FLAG_LATCH | FLAG_SPECIAL | FLAG_SECONDARY | flags);
|
|
}
|
|
|
|
private static void addModifierKey(String name, int symbol, Modifier m, int flags)
|
|
{
|
|
addModifierKey(name, String.valueOf((char)symbol), m, flags | FLAG_KEY_FONT);
|
|
}
|
|
|
|
private static void addDiacritic(String name, int symbol, Modifier m)
|
|
{
|
|
addKey(name, String.valueOf((char)symbol), Kind.Modifier, m.ordinal(),
|
|
FLAG_LATCH | FLAG_SPECIAL | FLAG_KEY_FONT);
|
|
}
|
|
|
|
private static void addEventKey(String name, String symbol, Event e, int flags)
|
|
{
|
|
addKey(name, symbol, Kind.Event, e.ordinal(), flags | FLAG_SPECIAL | FLAG_SECONDARY);
|
|
}
|
|
|
|
private static void addEventKey(String name, int symbol, Event e, int flags)
|
|
{
|
|
addEventKey(name, String.valueOf((char)symbol), e, flags | FLAG_KEY_FONT);
|
|
}
|
|
|
|
private static void addKeyeventKey(String name, String symbol, int code, int flags)
|
|
{
|
|
addKey(name, symbol, Kind.Keyevent, code, flags | FLAG_SECONDARY);
|
|
}
|
|
|
|
private static void addKeyeventKey(String name, int symbol, int code, int flags)
|
|
{
|
|
addKeyeventKey(name, String.valueOf((char)symbol), code, flags | FLAG_KEY_FONT);
|
|
}
|
|
|
|
private static void addEditingKey(String name, String symbol, Editing action)
|
|
{
|
|
addKey(name, symbol, Kind.Editing, action.ordinal(),
|
|
FLAG_SPECIAL | FLAG_SECONDARY | FLAG_SMALLER_FONT);
|
|
}
|
|
|
|
// Within VALUE_BITS
|
|
private static int placeholder_unique_id = 0;
|
|
|
|
/** Use a unique id as the value because the symbol is shared between every
|
|
placeholders (it is the empty string). */
|
|
private static void addPlaceholderKey(String name)
|
|
{
|
|
addKey(name, "", Kind.String, placeholder_unique_id++, 0);
|
|
}
|
|
|
|
static
|
|
{
|
|
addModifierKey("shift", 0x0A, Modifier.SHIFT, 0);
|
|
addModifierKey("ctrl", "Ctrl", Modifier.CTRL, 0);
|
|
addModifierKey("alt", "Alt", Modifier.ALT, 0);
|
|
addDiacritic("accent_aigu", 0x50, Modifier.AIGU);
|
|
addDiacritic("accent_caron", 0x51, Modifier.CARON);
|
|
addDiacritic("accent_cedille", 0x52, Modifier.CEDILLE);
|
|
addDiacritic("accent_circonflexe", 0x53, Modifier.CIRCONFLEXE);
|
|
addDiacritic("accent_grave", 0x54, Modifier.GRAVE);
|
|
addDiacritic("accent_macron", 0x55, Modifier.MACRON);
|
|
addDiacritic("accent_ring", 0x56, Modifier.RING);
|
|
addDiacritic("accent_tilde", 0x57, Modifier.TILDE);
|
|
addDiacritic("accent_trema", 0x58, Modifier.TREMA);
|
|
addDiacritic("accent_ogonek", 0x59, Modifier.OGONEK);
|
|
addDiacritic("accent_dot_above", 0x5A, Modifier.DOT_ABOVE);
|
|
addDiacritic("accent_double_aigu", 0x5B, Modifier.DOUBLE_AIGU);
|
|
addDiacritic("accent_slash", 0x5C, Modifier.SLASH);
|
|
addDiacritic("accent_arrow_right", 0x5D, Modifier.ARROW_RIGHT);
|
|
addDiacritic("accent_breve", 0x5E, Modifier.BREVE);
|
|
addDiacritic("accent_bar", 0x5F, Modifier.BAR);
|
|
addModifierKey("superscript", "Sup", Modifier.SUPERSCRIPT, 0);
|
|
addModifierKey("subscript", "Sub", Modifier.SUBSCRIPT, 0);
|
|
addModifierKey("ordinal", "Ord", Modifier.ORDINAL, 0);
|
|
addModifierKey("arrows", "Arr", Modifier.ARROWS, 0);
|
|
addModifierKey("box", "Box", Modifier.BOX, 0);
|
|
addModifierKey("fn", "Fn", Modifier.FN, 0);
|
|
addModifierKey("meta", "Meta", Modifier.META, 0);
|
|
|
|
addEventKey("config", 0x04, Event.CONFIG, FLAG_SMALLER_FONT);
|
|
addEventKey("switch_text", "ABC", Event.SWITCH_TEXT, FLAG_SMALLER_FONT);
|
|
addEventKey("switch_numeric", "123+", Event.SWITCH_NUMERIC, FLAG_SMALLER_FONT);
|
|
addEventKey("switch_emoji", 0x01, Event.SWITCH_EMOJI, FLAG_SMALLER_FONT);
|
|
addEventKey("switch_back_emoji", "ABC", Event.SWITCH_BACK_EMOJI, 0);
|
|
addEventKey("switch_second", 0x13, Event.SWITCH_SECOND, FLAG_SMALLER_FONT);
|
|
addEventKey("switch_second_back", 0x14, Event.SWITCH_SECOND_BACK, FLAG_SMALLER_FONT);
|
|
addEventKey("switch_greekmath", "πλ∇¬", Event.SWITCH_GREEKMATH, FLAG_SMALLER_FONT);
|
|
addEventKey("change_method", 0x09, Event.CHANGE_METHOD, FLAG_SMALLER_FONT);
|
|
addEventKey("action", "Action", Event.ACTION, FLAG_SMALLER_FONT); // Will always be replaced
|
|
addEventKey("capslock", 0x12, Event.CAPS_LOCK, 0);
|
|
|
|
addKeyeventKey("esc", "Esc", KeyEvent.KEYCODE_ESCAPE, FLAG_SMALLER_FONT);
|
|
addKeyeventKey("enter", 0x0E, KeyEvent.KEYCODE_ENTER, 0);
|
|
addKeyeventKey("up", 0x05, KeyEvent.KEYCODE_DPAD_UP, 0);
|
|
addKeyeventKey("right", 0x06, KeyEvent.KEYCODE_DPAD_RIGHT, 0);
|
|
addKeyeventKey("down", 0x07, KeyEvent.KEYCODE_DPAD_DOWN, 0);
|
|
addKeyeventKey("left", 0x08, KeyEvent.KEYCODE_DPAD_LEFT, 0);
|
|
addKeyeventKey("page_up", 0x02, KeyEvent.KEYCODE_PAGE_UP, 0);
|
|
addKeyeventKey("page_down", 0x03, KeyEvent.KEYCODE_PAGE_DOWN, 0);
|
|
addKeyeventKey("home", 0x0B, KeyEvent.KEYCODE_MOVE_HOME, 0);
|
|
addKeyeventKey("end", 0x0C, KeyEvent.KEYCODE_MOVE_END, 0);
|
|
addKeyeventKey("backspace", 0x11, KeyEvent.KEYCODE_DEL, 0);
|
|
addKeyeventKey("delete", 0x10, KeyEvent.KEYCODE_FORWARD_DEL, 0);
|
|
addKeyeventKey("insert", "Ins", KeyEvent.KEYCODE_INSERT, FLAG_SMALLER_FONT);
|
|
addKeyeventKey("f1", "F1", KeyEvent.KEYCODE_F1, 0);
|
|
addKeyeventKey("f2", "F2", KeyEvent.KEYCODE_F2, 0);
|
|
addKeyeventKey("f3", "F3", KeyEvent.KEYCODE_F3, 0);
|
|
addKeyeventKey("f4", "F4", KeyEvent.KEYCODE_F4, 0);
|
|
addKeyeventKey("f5", "F5", KeyEvent.KEYCODE_F5, 0);
|
|
addKeyeventKey("f6", "F6", KeyEvent.KEYCODE_F6, 0);
|
|
addKeyeventKey("f7", "F7", KeyEvent.KEYCODE_F7, 0);
|
|
addKeyeventKey("f8", "F8", KeyEvent.KEYCODE_F8, 0);
|
|
addKeyeventKey("f9", "F9", KeyEvent.KEYCODE_F9, 0);
|
|
addKeyeventKey("f10", "F10", KeyEvent.KEYCODE_F10, 0);
|
|
addKeyeventKey("f11", "F11", KeyEvent.KEYCODE_F11, FLAG_SMALLER_FONT);
|
|
addKeyeventKey("f12", "F12", KeyEvent.KEYCODE_F12, FLAG_SMALLER_FONT);
|
|
addKeyeventKey("tab", 0x0F, KeyEvent.KEYCODE_TAB, FLAG_SMALLER_FONT);
|
|
|
|
addCharKey("\\t", "\\t", '\t', 0); // Send the tab character
|
|
addCharKey("space", "\r", ' ', FLAG_KEY_FONT | FLAG_SECONDARY);
|
|
addCharKey("nbsp", "\u237d", '\u00a0', FLAG_SMALLER_FONT);
|
|
|
|
addPlaceholderKey("removed");
|
|
addPlaceholderKey("f11_placeholder");
|
|
addPlaceholderKey("f12_placeholder");
|
|
|
|
addEditingKey("copy", "copy", Editing.COPY);
|
|
addEditingKey("paste", "paste", Editing.PASTE);
|
|
addEditingKey("cut", "cut", Editing.CUT);
|
|
addEditingKey("selectAll", "s. all", Editing.SELECT_ALL);
|
|
addEditingKey("shareText", "share", Editing.SHARE);
|
|
addEditingKey("pasteAsPlainText", "<paste>", Editing.PASTE_PLAIN);
|
|
addEditingKey("undo", "undo", Editing.UNDO);
|
|
addEditingKey("redo", "redo", Editing.REDO);
|
|
addEditingKey("replaceText", "repl.", Editing.REPLACE);
|
|
addEditingKey("textAssist", "assist", Editing.ASSIST);
|
|
addEditingKey("autofill", "auto.", Editing.AUTOFILL);
|
|
}
|
|
|
|
static final HashMap<String, String> keys_descr = new HashMap<String, String>();
|
|
|
|
/* Some keys have a description attached. Return [null] if otherwise. */
|
|
public static String getKeyDescription(String name)
|
|
{
|
|
return keys_descr.get(name);
|
|
}
|
|
|
|
static void addKeyDescr(String name, String descr)
|
|
{
|
|
keys_descr.put(name, descr);
|
|
}
|
|
|
|
static {
|
|
/* Keys description is shown in the settings. */
|
|
addKeyDescr("capslock", "Caps lock");
|
|
addKeyDescr("switch_greekmath", "Greek & math symbols");
|
|
}
|
|
|
|
// Substitute for [assert], which has no effect on Android.
|
|
private static void check(boolean b)
|
|
{
|
|
if (!b)
|
|
throw new RuntimeException("Assertion failure");
|
|
}
|
|
}
|