Unexpected-Keyboard/srcs/juloo.keyboard2/KeyValue.java
Jules Aguillon 0f62b3044c Option to switch to the previous input method
A new option changes the "change_method" into the new
"change_method_prev". It switch to the previously used input method.

A long press on "change_method_prev" sends "change_method".

A new section is added in the settings and existing options are moved.
2023-02-12 23:20:11 +01:00

456 lines
16 KiB
Java

package juloo.keyboard2;
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,
CHANGE_METHOD_PREV,
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,
DOT_BELOW,
GRAVE,
AIGU,
CIRCONFLEXE,
TILDE,
CEDILLE,
TREMA,
HORN,
HOOK_ABOVE,
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 Placeholder
{
REMOVED,
F11,
F12,
SHINDOT,
SINDOT,
OLE,
METEG
}
public static enum Kind
{
Char, String, Keyevent, Event, Modifier, Editing, Placeholder
}
// 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)];
}
public Placeholder getPlaceholder()
{
return Placeholder.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;
}
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);
}
private static KeyValue charKey(String symbol, char c, int flags)
{
return new KeyValue(symbol, Kind.Char, c, flags);
}
private static KeyValue modifierKey(String symbol, Modifier m, int flags)
{
if (symbol.length() > 1)
flags |= FLAG_SMALLER_FONT;
return new KeyValue(symbol, Kind.Modifier, m.ordinal(),
FLAG_LATCH | FLAG_SPECIAL | FLAG_SECONDARY | flags);
}
private static KeyValue modifierKey(int symbol, Modifier m, int flags)
{
return modifierKey(String.valueOf((char)symbol), m, flags | FLAG_KEY_FONT);
}
private static KeyValue diacritic(int symbol, Modifier m)
{
return new KeyValue(String.valueOf((char)symbol), Kind.Modifier, m.ordinal(),
FLAG_LATCH | FLAG_SPECIAL | FLAG_KEY_FONT);
}
private static KeyValue eventKey(String symbol, Event e, int flags)
{
return new KeyValue(symbol, Kind.Event, e.ordinal(), flags | FLAG_SPECIAL | FLAG_SECONDARY);
}
private static KeyValue eventKey(int symbol, Event e, int flags)
{
return eventKey(String.valueOf((char)symbol), e, flags | FLAG_KEY_FONT);
}
private static KeyValue keyeventKey(String symbol, int code, int flags)
{
return new KeyValue(symbol, Kind.Keyevent, code, flags | FLAG_SECONDARY);
}
private static KeyValue keyeventKey(int symbol, int code, int flags)
{
return keyeventKey(String.valueOf((char)symbol), code, flags | FLAG_KEY_FONT);
}
private static KeyValue editingKey(String symbol, Editing action)
{
return new KeyValue(symbol, Kind.Editing, action.ordinal(),
FLAG_SPECIAL | FLAG_SECONDARY | FLAG_SMALLER_FONT);
}
/** A key that do nothing but has a unique ID. */
private static KeyValue placeholderKey(Placeholder id)
{
return new KeyValue("", Kind.Placeholder, id.ordinal(), 0);
}
private static KeyValue fallbackMakeKey(String name)
{
if (name.length() == 1)
return new KeyValue(name, Kind.Char, name.charAt(0), 0);
else
return new KeyValue(name, Kind.String, 0, 0);
}
public static KeyValue getKeyByName(String name)
{
switch (name)
{
case "shift": return modifierKey(0x0A, Modifier.SHIFT, 0);
case "ctrl": return modifierKey("Ctrl", Modifier.CTRL, 0);
case "alt": return modifierKey("Alt", Modifier.ALT, 0);
case "accent_aigu": return diacritic(0x50, Modifier.AIGU);
case "accent_caron": return diacritic(0x51, Modifier.CARON);
case "accent_cedille": return diacritic(0x52, Modifier.CEDILLE);
case "accent_circonflexe": return diacritic(0x53, Modifier.CIRCONFLEXE);
case "accent_grave": return diacritic(0x54, Modifier.GRAVE);
case "accent_macron": return diacritic(0x55, Modifier.MACRON);
case "accent_ring": return diacritic(0x56, Modifier.RING);
case "accent_tilde": return diacritic(0x57, Modifier.TILDE);
case "accent_trema": return diacritic(0x58, Modifier.TREMA);
case "accent_ogonek": return diacritic(0x59, Modifier.OGONEK);
case "accent_dot_above": return diacritic(0x5A, Modifier.DOT_ABOVE);
case "accent_double_aigu": return diacritic(0x5B, Modifier.DOUBLE_AIGU);
case "accent_slash": return diacritic(0x5C, Modifier.SLASH);
case "accent_arrow_right": return diacritic(0x5D, Modifier.ARROW_RIGHT);
case "accent_breve": return diacritic(0x5E, Modifier.BREVE);
case "accent_bar": return diacritic(0x5F, Modifier.BAR);
case "accent_dot_below": return diacritic(0x60, Modifier.DOT_BELOW);
case "accent_horn": return diacritic(0x61, Modifier.HORN);
case "accent_hook_above": return diacritic(0x62, Modifier.HOOK_ABOVE);
case "superscript": return modifierKey("Sup", Modifier.SUPERSCRIPT, 0);
case "subscript": return modifierKey("Sub", Modifier.SUBSCRIPT, 0);
case "ordinal": return modifierKey("Ord", Modifier.ORDINAL, 0);
case "arrows": return modifierKey("Arr", Modifier.ARROWS, 0);
case "box": return modifierKey("Box", Modifier.BOX, 0);
case "fn": return modifierKey("Fn", Modifier.FN, 0);
case "meta": return modifierKey("Meta", Modifier.META, 0);
case "config": return eventKey(0x04, Event.CONFIG, FLAG_SMALLER_FONT);
case "switch_text": return eventKey("ABC", Event.SWITCH_TEXT, FLAG_SMALLER_FONT);
case "switch_numeric": return eventKey("123+", Event.SWITCH_NUMERIC, FLAG_SMALLER_FONT);
case "switch_emoji": return eventKey(0x01, Event.SWITCH_EMOJI, FLAG_SMALLER_FONT);
case "switch_back_emoji": return eventKey("ABC", Event.SWITCH_BACK_EMOJI, 0);
case "switch_second": return eventKey(0x13, Event.SWITCH_SECOND, FLAG_SMALLER_FONT);
case "switch_second_back": return eventKey(0x14, Event.SWITCH_SECOND_BACK, FLAG_SMALLER_FONT);
case "switch_greekmath": return eventKey("πλ∇¬", Event.SWITCH_GREEKMATH, FLAG_SMALLER_FONT);
case "change_method": return eventKey(0x09, Event.CHANGE_METHOD, FLAG_SMALLER_FONT);
case "change_method_prev": return eventKey(0x09, Event.CHANGE_METHOD_PREV, FLAG_SMALLER_FONT);
case "action": return eventKey("Action", Event.ACTION, FLAG_SMALLER_FONT); // Will always be replaced
case "capslock": return eventKey(0x12, Event.CAPS_LOCK, 0);
case "esc": return keyeventKey("Esc", KeyEvent.KEYCODE_ESCAPE, FLAG_SMALLER_FONT);
case "enter": return keyeventKey(0x0E, KeyEvent.KEYCODE_ENTER, 0);
case "up": return keyeventKey(0x05, KeyEvent.KEYCODE_DPAD_UP, 0);
case "right": return keyeventKey(0x06, KeyEvent.KEYCODE_DPAD_RIGHT, 0);
case "down": return keyeventKey(0x07, KeyEvent.KEYCODE_DPAD_DOWN, 0);
case "left": return keyeventKey(0x08, KeyEvent.KEYCODE_DPAD_LEFT, 0);
case "page_up": return keyeventKey(0x02, KeyEvent.KEYCODE_PAGE_UP, 0);
case "page_down": return keyeventKey(0x03, KeyEvent.KEYCODE_PAGE_DOWN, 0);
case "home": return keyeventKey(0x0B, KeyEvent.KEYCODE_MOVE_HOME, 0);
case "end": return keyeventKey(0x0C, KeyEvent.KEYCODE_MOVE_END, 0);
case "backspace": return keyeventKey(0x11, KeyEvent.KEYCODE_DEL, 0);
case "delete": return keyeventKey(0x10, KeyEvent.KEYCODE_FORWARD_DEL, 0);
case "insert": return keyeventKey("Ins", KeyEvent.KEYCODE_INSERT, FLAG_SMALLER_FONT);
case "f1": return keyeventKey("F1", KeyEvent.KEYCODE_F1, 0);
case "f2": return keyeventKey("F2", KeyEvent.KEYCODE_F2, 0);
case "f3": return keyeventKey("F3", KeyEvent.KEYCODE_F3, 0);
case "f4": return keyeventKey("F4", KeyEvent.KEYCODE_F4, 0);
case "f5": return keyeventKey("F5", KeyEvent.KEYCODE_F5, 0);
case "f6": return keyeventKey("F6", KeyEvent.KEYCODE_F6, 0);
case "f7": return keyeventKey("F7", KeyEvent.KEYCODE_F7, 0);
case "f8": return keyeventKey("F8", KeyEvent.KEYCODE_F8, 0);
case "f9": return keyeventKey("F9", KeyEvent.KEYCODE_F9, 0);
case "f10": return keyeventKey("F10", KeyEvent.KEYCODE_F10, 0);
case "f11": return keyeventKey("F11", KeyEvent.KEYCODE_F11, FLAG_SMALLER_FONT);
case "f12": return keyeventKey("F12", KeyEvent.KEYCODE_F12, FLAG_SMALLER_FONT);
case "tab": return keyeventKey(0x0F, KeyEvent.KEYCODE_TAB, FLAG_SMALLER_FONT);
case "\\t": return charKey("\\t", '\t', 0); // Send the tab character
case "space": return charKey("\r", ' ', FLAG_KEY_FONT | FLAG_SECONDARY);
case "nbsp": return charKey("\u237d", '\u00a0', FLAG_SMALLER_FONT);
/* bidi */
case "lrm": return charKey("", '\u200e', 0); // Send left-to-right mark
case "rlm": return charKey("", '\u200f', 0); // Send right-to-left mark
case "b(": return charKey("(", ')', 0);
case "b)": return charKey(")", '(', 0);
case "b[": return charKey("[", ']', 0);
case "b]": return charKey("]", '[', 0);
case "b{": return charKey("{", '}', 0);
case "b}": return charKey("}", '{', 0);
case "blt": return charKey("<", '>', 0);
case "bgt": return charKey(">", '<', 0);
case "removed": return placeholderKey(Placeholder.REMOVED);
case "f11_placeholder": return placeholderKey(Placeholder.F11);
case "f12_placeholder": return placeholderKey(Placeholder.F12);
/* hebrew niqqud */
case "qamats": return charKey("\u05E7\u05B8", '\u05B8', 0); // kamatz
case "patah": return charKey("\u05E4\u05B7", '\u05B7', 0); // patach
case "sheva": return charKey("\u05E9\u05B0", '\u05B0', 0);
case "dagesh": return charKey("\u05D3\u05BC", '\u05BC', 0); // or mapiq
case "hiriq": return charKey("\u05D7\u05B4", '\u05B4', 0);
case "segol": return charKey("\u05E1\u05B6", '\u05B6', 0);
case "tsere": return charKey("\u05E6\u05B5", '\u05B5', 0);
case "holam": return charKey("\u05D5\u05B9", '\u05B9', 0);
case "qubuts": return charKey("\u05E7\u05BB", '\u05BB', 0); // kubuts
case "hataf_patah": return charKey("\u05D7\u05B2\u05E4\u05B7", '\u05B2', 0); // reduced patach
case "hataf_qamats": return charKey("\u05D7\u05B3\u05E7\u05B8", '\u05B3', 0); // reduced kamatz
case "hataf_segol": return charKey("\u05D7\u05B1\u05E1\u05B6", '\u05B1', 0); // reduced segol
case "shindot": return charKey("\u05E9\u05C1", '\u05C1', 0);
case "shindot_placeholder": return placeholderKey(Placeholder.SHINDOT);
case "sindot": return charKey("\u05E9\u05C2", '\u05C2', 0);
case "sindot_placeholder": return placeholderKey(Placeholder.SINDOT);
/* hebrew punctuation */
case "geresh": return charKey("\u05F3", '\u05F3', 0);
case "gershayim": return charKey("\u05F4", '\u05F4', 0);
case "maqaf": return charKey("\u05BE", '\u05BE', 0);
/* hebrew biblical */
case "rafe": return charKey("\u05E4\u05BF", '\u05BF', 0);
case "ole": return charKey("\u05E2\u05AB", '\u05AB', 0);
case "ole_placeholder": return placeholderKey(Placeholder.OLE);
case "meteg": return charKey("\u05DE\u05BD", '\u05BD', 0); // or siluq or sof-pasuq
case "meteg_placeholder": return placeholderKey(Placeholder.METEG);
case "copy": return editingKey("copy", Editing.COPY);
case "paste": return editingKey("paste", Editing.PASTE);
case "cut": return editingKey("cut", Editing.CUT);
case "selectAll": return editingKey("s. all", Editing.SELECT_ALL);
case "shareText": return editingKey("share", Editing.SHARE);
case "pasteAsPlainText": return editingKey("<paste>", Editing.PASTE_PLAIN);
case "undo": return editingKey("undo", Editing.UNDO);
case "redo": return editingKey("redo", Editing.REDO);
case "replaceText": return editingKey("repl.", Editing.REPLACE);
case "textAssist": return editingKey("assist", Editing.ASSIST);
case "autofill": return editingKey("auto.", Editing.AUTOFILL);
default: return fallbackMakeKey(name);
}
}
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");
}
}