Compare commits

..

4 Commits

Author SHA1 Message Date
Jules Aguillon
6d3dfd0c5a Add 'playpause' key
Sends the KEYCODE_MEDIA_PLAY_PAUSE keyevent.
2024-09-21 21:53:53 +02:00
Mostafa Khorashadi Zadeh
4d686a8836 Change the place of چ and ژ in Persian Keyboard Layout (#757)
* Change place of "چ" and "ژ"

* rollback unexpected changes
2024-09-21 11:33:59 +02:00
Zitrone
b21bf3fffd Improve bone layout (#748)
* shell.nix: fix gradle error

error was:
> \> Task :compileDebugJavaWithJavac FAILED
> error: Source option 7 is no longer supported. Use 8 or later.
> error: Target option 7 is no longer supported. Use 8 or later.

fixed by overriding the jdk version for gradle to openjdk17

* layouts/latn_bone: fix layout

- fix alignment with tabs mess
- make like actual bone layout, instead of trying to fit it into a 10 key
  wide keyboard
  - fixes missing üäö
  - fix missing $
  - moves q and ß where they belong
  - remove all of the diacritic keys (they can be added through the
    settings as extra keys)
  - kept the number row extra keys integration into top row
  - kept the idea of compressing , and . into the swipe actions of the
    bottom row
  - kept the number keys as key4 in the layer4 positions (instead of
    moving them to a number row, which i also considered)
2024-09-19 23:57:26 +02:00
Spike
7454389f48 doc: Point to code for built-in bottom row and number row for use as starting points (#756) 2024-09-19 23:48:56 +02:00
14 changed files with 103 additions and 431 deletions

View File

@@ -2,10 +2,6 @@ plugins {
id 'com.android.application' version '8.1.1' id 'com.android.application' version '8.1.1'
} }
dependencies {
testImplementation "junit:junit:4.13.2"
}
android { android {
namespace 'juloo.keyboard2' namespace 'juloo.keyboard2'
compileSdk 34 compileSdk 34
@@ -25,10 +21,6 @@ android {
res.srcDirs = ['res', 'build/generated-resources'] res.srcDirs = ['res', 'build/generated-resources']
assets.srcDirs = ['assets'] assets.srcDirs = ['assets']
} }
test {
java.srcDirs = ['test']
}
} }
signingConfigs { signingConfigs {
@@ -92,6 +84,9 @@ android {
} }
} }
dependencies {
}
tasks.register('buildKeyboardFont') { tasks.register('buildKeyboardFont') {
println "\nBuilding assets/special_font.ttf" println "\nBuilding assets/special_font.ttf"
mkdir "$buildDir" mkdir "$buildDir"

View File

@@ -64,7 +64,7 @@ Layout includes some ASCII punctuation but not all, missing: (, ), <, >, [, ], {
# latn_bepo_fr # latn_bepo_fr
0 warnings 0 warnings
# latn_bone # latn_bone
Layout includes some ASCII punctuation but not all, missing: $ Layout doesn't define some important keys, missing: loc esc, loc tab
Layout redefines the bottom row but some important keys are missing, missing: cursor_left, cursor_right, loc compose, loc end, loc home, loc page_down, loc page_up, loc switch_clipboard, loc switch_greekmath, loc voice_typing, switch_backward Layout redefines the bottom row but some important keys are missing, missing: cursor_left, cursor_right, loc compose, loc end, loc home, loc page_down, loc page_up, loc switch_clipboard, loc switch_greekmath, loc voice_typing, switch_backward
2 warnings 2 warnings
# latn_colemak # latn_colemak

View File

@@ -34,11 +34,19 @@ Here is a complete keyboard file with a single row containing an "a" key on the
</keyboard> </keyboard>
## Keyboard metadata ## Keyboard metadata
The `<keyboard>`...`</keyboard>` pair follows the declaration tag and encloses the whole keyboard. The following properties may be used (The first two appear in the example above): The `<keyboard>`...`</keyboard>` pair follows the declaration tag and encloses the whole keyboard. The following properties may be used (The first two appear in the example above):
* `name`: The name of the keyboard. The name you specify will appear in the Settings menu. If not present, the layout will just appear as “Custom layout”. * `name`: The name of the keyboard. The name you specify will appear in the Settings menu. If not present, the layout will just appear as “Custom layout”.
* `script`: The (main) writing system that the keyboard supports. The possible values are `arabic`, `armenian`, `bengali`, `cyrillic`, `devanagari`, `gujarati`, `hangul`, `hebrew`, `latin`, `persian`, `shavian`, and `urdu`. It defaults to `latin`. * `script`: The (main) writing system that the keyboard supports. The possible values are `arabic`, `armenian`, `bengali`, `cyrillic`, `devanagari`, `gujarati`, `hangul`, `hebrew`, `latin`, `persian`, `shavian`, and `urdu`. It defaults to `latin`.
* `numpad_script`: The script to use for the numpad. This is useful for scripts where a different, non-ASCII set of numerals is used, like Devanagari and Arabic. It defaults to the same as `script`. * `numpad_script`: The script to use for the numpad. This is useful for scripts where a different, non-ASCII set of numerals is used, like Devanagari and Arabic. It defaults to the same as `script`.
* `bottom_row`: Whether or not to show the common bottom row. It accepts `true` or `false`, and defaults to `true`. If your custom layout defines the bottom row, then specify `bottom_row="false"` to disable the built-in bottom row.
* `bottom_row`: Whether or not to show the built-in bottom row. It accepts `true` or `false`, and defaults to `true`. If your custom layout defines the bottom row, then specify `bottom_row="false"` to disable the built-in bottom row.
+ We recommend your layout use the built-in bottom row, because it is still evolving and your layout will incorporate innovations in future versions. However, to define your own, the current definition of the bottom row is in [bottom_row.xml](https://github.com/Julow/Unexpected-Keyboard/res/xml/bottom_row.xml). You can copypaste this XML into your custom layout as a starting point.
+ Likewise, the current definition of the top (number) row is in [number_row.xml](https://github.com/Julow/Unexpected-Keyboard/res/xml/number_row.xml).
* `locale_extra_keys`: Whether Unexpected should add language-dependent extra keys from [method.xml](../res/xml/method.xml) to this layout. It accepts `true` or `false`, and defaults to `true`. To disable these automatic additions, specify `locale_extra_keys="false"`. * `locale_extra_keys`: Whether Unexpected should add language-dependent extra keys from [method.xml](../res/xml/method.xml) to this layout. It accepts `true` or `false`, and defaults to `true`. To disable these automatic additions, specify `locale_extra_keys="false"`.
## Row ## Row

View File

@@ -119,7 +119,7 @@ Keys ending in `_placeholder` are normally hidden unless the Fn key is pressed.
`ole`, `ole_placeholder`, `ole`, `ole_placeholder`,
`meteg`, `meteg_placeholder` `meteg`, `meteg_placeholder`
## Keyboard behavior keys ## Unexpected Keyboard specific
Value | Meaning Value | Meaning
:--------------------- | :------ :--------------------- | :------
`config` | Gear icon; opens Unexpected Keyboard settings. `config` | Gear icon; opens Unexpected Keyboard settings.
@@ -148,28 +148,3 @@ These keys are known to do nothing.
These keys are normally hidden unless the Fn modifier is activated. These keys are normally hidden unless the Fn modifier is activated.
`f11_placeholder` | `f12_placeholder` `f11_placeholder` | `f12_placeholder`
## Complex keys
More complex keys are of this form:
```
:<kind> <attributes>:<payload>
```
Where `<kind>` is one of the kinds documented below and `<attributes>` is a
space separated list of attributes. `<payload>` depends on the `<kind>`.
Attributes are:
- `symbol='Sym'` is the symbol to be shown on the keyboard.
- `flags='<flags>'` is a collection of flags that change the behavior of the key.
`<flags>` is a coma separated list of:
+ `dim`: Make the symbol dimmer.
+ `small`: Make the symbol smaller.
### Kind `str`
Defines a key that outputs an arbitrary string. `<payload>` is a string wrapped
in single-quotes (`'`), escaping of other single quotes is allowed with `\'`.
For example: `:str symbol='Sym':'Output string'`

View File

@@ -15,12 +15,13 @@ let
ANDROID_SDK_ROOT = "${android.androidsdk}/libexec/android-sdk"; ANDROID_SDK_ROOT = "${android.androidsdk}/libexec/android-sdk";
gradle = pkgs.gradle.override { java = jdk; };
# Without this option, aapt2 fails to run with a permissions error. # Without this option, aapt2 fails to run with a permissions error.
gradle_wrapped = pkgs.runCommandLocal "gradle-wrapped" { gradle_wrapped = pkgs.runCommandLocal "gradle-wrapped" {
nativeBuildInputs = with pkgs; [ makeBinaryWrapper ]; nativeBuildInputs = with pkgs; [ makeBinaryWrapper ];
} '' } ''
mkdir -p $out/bin mkdir -p $out/bin
ln -s ${pkgs.gradle}/bin/gradle $out/bin/gradle ln -s ${gradle}/bin/gradle $out/bin/gradle
wrapProgram $out/bin/gradle \ wrapProgram $out/bin/gradle \
--add-flags "-Dorg.gradle.project.android.aapt2FromMavenOverride=${ANDROID_SDK_ROOT}/build-tools/${build_tools_version}/aapt2" --add-flags "-Dorg.gradle.project.android.aapt2FromMavenOverride=${ANDROID_SDK_ROOT}/build-tools/${build_tools_version}/aapt2"
''; '';

View File

@@ -210,7 +210,8 @@ public final class Config
KeyValue action_key() KeyValue action_key()
{ {
// Update the name to avoid caching in KeyModifier // Update the name to avoid caching in KeyModifier
return (actionLabel == null) ? null : KeyValue.makeActionKey(actionLabel); return (actionLabel == null) ? null :
KeyValue.getKeyByName("action").withSymbol(actionLabel);
} }
/** Update the layout according to the configuration. /** Update the layout according to the configuration.

View File

@@ -97,7 +97,6 @@ public final class KeyEventHandler
_recv.set_compose_pending(true); _recv.set_compose_pending(true);
break; break;
case Cursor_move: move_cursor(key.getCursorMove()); break; case Cursor_move: move_cursor(key.getCursorMove()); break;
case Complex: send_complex_key(key.getComplexKind(), key.getComplex()); break;
} }
update_meta_state(old_mods); update_meta_state(old_mods);
} }
@@ -216,16 +215,6 @@ public final class KeyEventHandler
conn.performContextMenuAction(id); conn.performContextMenuAction(id);
} }
void send_complex_key(KeyValue.Complex.Kind kind, KeyValue.Complex val)
{
switch (kind)
{
case StringWithSymbol:
send_text(((KeyValue.Complex.StringWithSymbol)val).str);
break;
}
}
@SuppressLint("InlinedApi") @SuppressLint("InlinedApi")
void handle_editing_key(KeyValue.Editing ev) void handle_editing_key(KeyValue.Editing ev)
{ {

View File

@@ -91,8 +91,7 @@ public final class KeyValue implements Comparable<KeyValue>
{ {
Char, String, Keyevent, Event, Compose_pending, Hangul_initial, Char, String, Keyevent, Event, Compose_pending, Hangul_initial,
Hangul_medial, Modifier, Editing, Placeholder, Hangul_medial, Modifier, Editing, Placeholder,
Cursor_move, // Value is encoded as a 16-bit integer. Cursor_move // Value is encoded as a 16-bit integer
Complex, // [_payload] is a [KeyValue.Complex], value is [Complex.Kind].
} }
private static final int FLAGS_OFFSET = 19; private static final int FLAGS_OFFSET = 19;
@@ -130,13 +129,7 @@ public final class KeyValue implements Comparable<KeyValue>
check((((Kind.values().length - 1) << KIND_OFFSET) & ~KIND_BITS) == 0); check((((Kind.values().length - 1) << KIND_OFFSET) & ~KIND_BITS) == 0);
} }
/** private final String _symbol;
* The symbol that is rendered on the keyboard as a [String].
* Except for keys of kind:
* - [String], this is also the string to output.
* - [Complex], this is an instance of [KeyValue.Complex].
*/
private final Object _payload;
/** This field encodes three things: Kind, flags and value. */ /** This field encodes three things: Kind, flags and value. */
private final int _code; private final int _code;
@@ -160,9 +153,7 @@ public final class KeyValue implements Comparable<KeyValue>
When [getKind() == Kind.String], also the string to send. */ When [getKind() == Kind.String], also the string to send. */
public String getString() public String getString()
{ {
if (getKind() == Kind.Complex) return _symbol;
return ((Complex)_payload).getSymbol();
return (String)_payload;
} }
/** Defined only when [getKind() == Kind.Char]. */ /** Defined only when [getKind() == Kind.Char]. */
@@ -220,32 +211,25 @@ public final class KeyValue implements Comparable<KeyValue>
return (short)(_code & VALUE_BITS); return (short)(_code & VALUE_BITS);
} }
/** Defined only when [getKind() == Kind.Complex]. */
public Complex getComplex()
{
return (Complex)_payload;
}
/** Defined only when [getKind() == Kind.Complex]. */
public Complex.Kind getComplexKind()
{
return Complex.Kind.values()[(_code & VALUE_BITS)];
}
/* Update the char and the symbol. */ /* Update the char and the symbol. */
public KeyValue withChar(char c) public KeyValue withChar(char c)
{ {
return new KeyValue(String.valueOf(c), Kind.Char, c, getFlags()); return new KeyValue(String.valueOf(c), Kind.Char, c, getFlags());
} }
public KeyValue withSymbol(String s)
{
return new KeyValue(s, (_code & KIND_BITS), (_code & VALUE_BITS), getFlags());
}
public KeyValue withKeyevent(int code) public KeyValue withKeyevent(int code)
{ {
return new KeyValue(getString(), Kind.Keyevent, code, getFlags()); return new KeyValue(_symbol, Kind.Keyevent, code, getFlags());
} }
public KeyValue withFlags(int f) public KeyValue withFlags(int f)
{ {
return new KeyValue(getString(), (_code & KIND_BITS), (_code & VALUE_BITS), f); return new KeyValue(_symbol, (_code & KIND_BITS), (_code & VALUE_BITS), f);
} }
@Override @Override
@@ -263,9 +247,7 @@ public final class KeyValue implements Comparable<KeyValue>
d = _code - snd._code; d = _code - snd._code;
if (d != 0) if (d != 0)
return d; return d;
if (getKind() == Kind.Complex) return _symbol.compareTo(snd._symbol);
return ((Complex)_payload).compareTo((Complex)snd._payload);
return ((String)_payload).compareTo((String)snd._payload);
} }
/** Type-safe alternative to [equals]. */ /** Type-safe alternative to [equals]. */
@@ -273,36 +255,24 @@ public final class KeyValue implements Comparable<KeyValue>
{ {
if (snd == null) if (snd == null)
return false; return false;
return _code == snd._code && _payload.equals(snd._payload); return _symbol.equals(snd._symbol) && _code == snd._code;
} }
@Override @Override
public int hashCode() public int hashCode()
{ {
return _payload.hashCode() + _code; return _symbol.hashCode() + _code;
} }
public String toString() public KeyValue(String s, int kind, int value, int flags)
{ {
int value = _code & VALUE_BITS; _symbol = s;
return "[KeyValue " + getKind().toString() + "+" + getFlags() + "+" + value + " \"" + getString() + "\"]";
}
private KeyValue(Object p, int kind, int value, int flags)
{
_payload = p;
_code = (kind & KIND_BITS) | (flags & FLAGS_BITS) | (value & VALUE_BITS); _code = (kind & KIND_BITS) | (flags & FLAGS_BITS) | (value & VALUE_BITS);
} }
public KeyValue(Complex p, Complex.Kind value, int flags) public KeyValue(String s, Kind k, int v, int f)
{ {
this((Object)p, (Kind.Complex.ordinal() << KIND_OFFSET), value.ordinal(), this(s, (k.ordinal() << KIND_OFFSET), v, f);
flags);
}
public KeyValue(String p, Kind k, int v, int f)
{
this(p, (k.ordinal() << KIND_OFFSET), v, f);
} }
private static KeyValue charKey(String symbol, char c, int flags) private static KeyValue charKey(String symbol, char c, int flags)
@@ -427,11 +397,6 @@ public final class KeyValue implements Comparable<KeyValue>
return KeyValue.makeCharKey((char)precomposed); return KeyValue.makeCharKey((char)precomposed);
} }
public static KeyValue makeActionKey(String symbol)
{
return eventKey(symbol, Event.ACTION, FLAG_SMALLER_FONT);
}
/** Make a key that types a string. A char key is returned for a string of /** Make a key that types a string. A char key is returned for a string of
length 1. */ length 1. */
public static KeyValue makeStringKey(String str, int flags) public static KeyValue makeStringKey(String str, int flags)
@@ -442,36 +407,12 @@ public final class KeyValue implements Comparable<KeyValue>
return new KeyValue(str, Kind.String, 0, flags | FLAG_SMALLER_FONT); return new KeyValue(str, Kind.String, 0, flags | FLAG_SMALLER_FONT);
} }
public static KeyValue makeStringKeyWithSymbol(String str, String symbol, int flags)
{
return new KeyValue(new Complex.StringWithSymbol(str, symbol),
Complex.Kind.StringWithSymbol, flags);
}
/** Make a modifier key for passing to [KeyModifier]. */ /** Make a modifier key for passing to [KeyModifier]. */
public static KeyValue makeInternalModifier(Modifier mod) public static KeyValue makeInternalModifier(Modifier mod)
{ {
return new KeyValue("", Kind.Modifier, mod.ordinal(), 0); return new KeyValue("", Kind.Modifier, mod.ordinal(), 0);
} }
public static KeyValue parseKeyDefinition(String str)
{
if (str.length() < 2 || str.charAt(0) != ':')
return makeStringKey(str);
try
{
return KeyValueParser.parse(str);
}
catch (KeyValueParser.ParseError _e)
{
return makeStringKey(str);
}
}
/**
* Return a key by its name. If the given name doesn't correspond to a key
* defined in this function, it is passed to [parseStringKey] as a fallback.
*/
public static KeyValue getKeyByName(String name) public static KeyValue getKeyByName(String name)
{ {
switch (name) switch (name)
@@ -561,6 +502,7 @@ public final class KeyValue implements Comparable<KeyValue>
case "f12": return keyeventKey("F12", KeyEvent.KEYCODE_F12, FLAG_SMALLER_FONT); case "f12": return keyeventKey("F12", KeyEvent.KEYCODE_F12, FLAG_SMALLER_FONT);
case "tab": return keyeventKey(0xE00F, KeyEvent.KEYCODE_TAB, FLAG_SMALLER_FONT); case "tab": return keyeventKey(0xE00F, KeyEvent.KEYCODE_TAB, FLAG_SMALLER_FONT);
case "menu": return keyeventKey("Menu", KeyEvent.KEYCODE_MENU, FLAG_SMALLER_FONT); case "menu": return keyeventKey("Menu", KeyEvent.KEYCODE_MENU, FLAG_SMALLER_FONT);
case "playpause": return keyeventKey("Play", KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, FLAG_SMALLER_FONT);
/* Spaces */ /* Spaces */
case "\\t": return charKey("\\t", '\t', 0); // Send the tab character case "\\t": return charKey("\\t", '\t', 0); // Send the tab character
@@ -658,8 +600,8 @@ public final class KeyValue implements Comparable<KeyValue>
case "": return makeHangulInitial("", 17); case "": return makeHangulInitial("", 17);
case "": return makeHangulInitial("", 18); case "": return makeHangulInitial("", 18);
/* The key is not one of the special ones. */ /* Fallback to a string key that types its name */
default: return parseKeyDefinition(name); default: return makeStringKey(name);
} }
} }
@@ -669,49 +611,4 @@ public final class KeyValue implements Comparable<KeyValue>
if (!b) if (!b)
throw new RuntimeException("Assertion failure"); throw new RuntimeException("Assertion failure");
} }
public static abstract class Complex
{
public abstract String getSymbol();
/** [compareTo] can assume that [snd] is an instance of the same class. */
public abstract int compareTo(Complex snd);
public boolean equals(Object snd)
{
if (snd instanceof Complex)
return compareTo((Complex)snd) == 0;
return false;
}
/** [hashCode] will be called on this class. */
/** The kind is stored in the [value] field of the key. */
public static enum Kind
{
StringWithSymbol,
}
public static final class StringWithSymbol extends Complex
{
public final String str;
private final String _symbol;
public StringWithSymbol(String _str, String _sym)
{
str = _str;
_symbol = _sym;
}
public String getSymbol() { return _symbol; }
public int compareTo(Complex _snd)
{
StringWithSymbol snd = (StringWithSymbol)_snd;
int d = str.compareTo(snd.str);
if (d != 0) return d;
return _symbol.compareTo(snd._symbol);
}
}
};
} }

View File

@@ -1,150 +0,0 @@
package juloo.keyboard2;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
Parse a key definition. The syntax for a key definition is:
- [:(kind) (attributes):(payload)].
- If [str] doesn't start with a [:] character, it is interpreted as an
arbitrary string key.
[(kind)] specifies the kind of the key, it can be:
- [str]: An arbitrary string key. The payload is the string to output when
typed and is quoted by single quotes ([']). The payload can contain single
quotes if they are escaped with a backslash ([\']).
The [(attributes)] part is a space-separated list of attributes, all optional,
of the form: [attrname='attrvalue'].
Attributes can be:
- [flags]: Add flags that change the behavior of the key.
Value is a coma separated list of:
- [dim]: Make the symbol dimmer on the keyboard.
- [small]: Make the symbol smaller on the keyboard.
- [symbol]: Specify the symbol that is rendered on the keyboard.
It can contain single quotes if they are escaped: ([\']).
Examples:
- [:str flags=dim,small symbol='MyKey':'My arbitrary string'].
- [:str:'My arbitrary string'].
*/
public final class KeyValueParser
{
static Pattern START_PAT;
static Pattern ATTR_PAT;
static Pattern QUOTED_PAT;
static Pattern PAYLOAD_START_PAT;
static public KeyValue parse(String str) throws ParseError
{
String symbol = null;
int flags = 0;
init();
// Kind
Matcher m = START_PAT.matcher(str);
if (!m.lookingAt())
parseError("Expected kind, for example \":str ...\".", m);
String kind = m.group(1);
// Attributes
while (true)
{
if (!match(m, ATTR_PAT))
break;
String attr_name = m.group(1);
String attr_value = parseSingleQuotedString(m);
switch (attr_name)
{
case "flags":
flags = parseFlags(attr_value, m);
break;
case "symbol":
symbol = attr_value;
break;
default:
parseError("Unknown attribute "+attr_name, m);
}
}
// Payload
if (!match(m, PAYLOAD_START_PAT))
parseError("Unexpected character", m);
switch (kind)
{
case "str":
String payload = parseSingleQuotedString(m);
if (symbol == null)
return KeyValue.makeStringKey(payload, flags);
return KeyValue.makeStringKeyWithSymbol(payload, symbol, flags);
default: break;
}
parseError("Unknown kind '"+kind+"'", m, 1);
return null; // Unreachable
}
static String parseSingleQuotedString(Matcher m) throws ParseError
{
if (!match(m, QUOTED_PAT))
parseError("Expected quoted string", m);
return m.group(1).replace("\\'", "'");
}
static int parseFlags(String s, Matcher m) throws ParseError
{
int flags = 0;
for (String f : s.split(","))
{
switch (f)
{
case "dim": flags |= KeyValue.FLAG_SECONDARY; break;
case "small": flags |= KeyValue.FLAG_SMALLER_FONT; break;
default: parseError("Unknown flag "+f, m);
}
}
return flags;
}
static boolean match(Matcher m, Pattern pat)
{
try { m.region(m.end(), m.regionEnd()); } catch (Exception _e) {}
m.usePattern(pat);
return m.lookingAt();
}
static void init()
{
if (START_PAT != null)
return;
START_PAT = Pattern.compile(":(\\w+)");
ATTR_PAT = Pattern.compile("\\s*(\\w+)\\s*=");
QUOTED_PAT = Pattern.compile("'(([^'\\\\]+|\\\\')*)'");
PAYLOAD_START_PAT = Pattern.compile("\\s*:");
}
static void parseError(String msg, Matcher m) throws ParseError
{
parseError(msg, m, m.regionStart());
}
static void parseError(String msg, Matcher m, int i) throws ParseError
{
StringBuilder msg_ = new StringBuilder("Syntax error");
try
{
char c = m.group(0).charAt(0);
msg_.append(" at character '").append(c).append("'");
} catch (IllegalStateException _e) {}
msg_.append(" at position ");
msg_.append(i);
msg_.append(": ");
msg_.append(msg);
throw new ParseError(msg_.toString());
}
public static class ParseError extends Exception
{
public ParseError(String msg) { super(msg); }
};
}

View File

@@ -40,7 +40,7 @@ public class CustomExtraKeysPreference extends ListGroupPreference<String>
if (key_names != null) if (key_names != null)
{ {
for (String key_name : key_names) for (String key_name : key_names)
kvs.put(KeyValue.parseKeyDefinition(key_name), KeyboardData.PreferredPos.DEFAULT); kvs.put(KeyValue.makeStringKey(key_name), KeyboardData.PreferredPos.DEFAULT);
} }
return kvs; return kvs;
} }

View File

@@ -11,7 +11,7 @@
<key key0="ه" key2="۸" key3="*"/> <key key0="ه" key2="۸" key3="*"/>
<key key0="خ" key2="۹" key3="(" key4=")"/> <key key0="خ" key2="۹" key3="(" key4=")"/>
<key key0="ح" key2="۰"/> <key key0="ح" key2="۰"/>
<key key0="ج" key2="چ"/> <key key0="ج"/>
</row> </row>
<row> <row>
<key key0="ش" key2="loc tab"/> <key key0="ش" key2="loc tab"/>
@@ -29,13 +29,13 @@
<row> <row>
<key shift="0.5" key0="ظ"/> <key shift="0.5" key0="ظ"/>
<key key0="ط"/> <key key0="ط"/>
<key key0="ز" key2="«"/> <key key0="ز" key1="«" key2="»"/>
<key key0="ر" key2="»"/> <key key0="ر" key1="ژ" key2="."/>
<key key0="ژ" key2="."/>
<key key0="ذ" key2=":"/> <key key0="ذ" key2=":"/>
<key key0="د" key2="؛"/> <key key0="د" key2="؛"/>
<key key0="پ" key2="&#1567;"/> <key key0="پ" key2="&#1567;"/>
<key key0="و"/> <key key0="و"/>
<key key0="چ"/>
<key width="1.5" key0="backspace" key2="delete"/> <key width="1.5" key0="backspace" key2="delete"/>
</row> </row>
</keyboard> </keyboard>

View File

@@ -1,46 +1,72 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- https://neo-layout.org/Layouts/bone/ --> <!-- https://neo-layout.org/Layouts/bone/ -->
<keyboard name="Bone" bottom_row="false" script="latin"> <keyboard name="Bone" bottom_row="false" script="latin">
<!-- first row + characters from number row:
jduaxphlmwß
…_[]^!<>=&ſ
°§ℓ»«$€„“”—
›‹¢¥‚‘’
-->
<row> <row>
<key key0="j" key2="loc esc" key4="…"/> <!--left side-->
<key key0="d" key2="°" key4="_"/> <key key0="j" key2="°" key4=""/>
<key key0="u" key2="§" key4="["/> <key key0="d" key2="§" key4="_"/>
<key key0="a" key4="]"/> <key key0="u" key2="" key4="["/>
<key key0="x" key4="^"/> <key key0="a" key2="»" key4="]" key1=""/>
<key key0="p" key3="!" key4="7"/> <key key0="x" key2="«" key4="^" key1=""/>
<key key0="h" key3="&lt;" key4="8"/> <!--middle-->
<key key0="l" key1="" key3="&gt;" key4="9"/> <key key0="p" key7="¢" key8="!"/>
<key key0="m" key1="≠" key3="="/> <!--right side-->
<key key0="w" key3="&amp;"/> <key key0="h" key1="€" key3="&lt;" key4="7" key2="/>
<key key0="l" key1="„" key3="&gt;" key4="8" key2=""/>
<key key0="m" key1="“" key3="=" key4="9" key2=""/>
<key key0="w" key1="”" key3="&amp;" key2=""/>
<key key0="ß" key1="—" key3="ſ"/>
</row> </row>
<!--second row:
ctieobnrsgq
\/{}*?()-:@
-->
<row> <row>
<key key0="c" key2="loc tab" key4="\\"/> <!--left side-->
<key key0="t" key1="accent_circonflexe" key2="accent_caron" key4="/"/> <key key0="c" key4="\\"/>
<key key0="i" key1="accent_aigu" key2="accent_grave" key4="{"/> <key key0="t" key4="/"/>
<key key0="e" key1="accent_cedille" key2="accent_ogonek" key4="}"/> <key key0="i" key4="{"/>
<key key0="o" key1="accent_ring" key2="accent_dot_above" key4="*"/> <key key0="e" key4="}"/>
<key key0="b" key2="accent_macron" key3="\?" key4="4"/> <key key0="o" key4="*"/>
<key key0="n" key2="accent_tilde" key3="(" key4="5"/> <!--middle-->
<key key0="r" key2="accent_trema" key3=")" key4="6"/> <key key0="b" key8="\?"/>
<key key0="s" key2="accent_slash" key3="-"/> <!--right side-->
<key key0="g" key1="\@" key3=":"/> <key key0="n" key3="(" key4="4"/>
<key key0="r" key3=")" key4="5"/>
<key key0="s" key3="-" key4="6"/>
<key key0="g" key3=":"/>
<key key0="q" key3="@"/>
</row> </row>
<!--third row -> compressed to also fit shift and backspace:
fvüäöyz,.k
#$|~`+%"';
-->
<row> <row>
<key width="1.5" key0="shift" key4="\#" key2="loc capslock"/> <!--left side-->
<key key0="f" key4="|"/> <key width="1.5" key0="shift" key4="\#"/>
<key key0="v" key4="~"/> <key key0="f" key4="$"/>
<key key0="ß" key4="`"/> <key key0="v" key4="|"/>
<key key0="y" key3="%" key4="1"/> <key key0="ü" key4="~"/>
<key key0="z" key1="&quot;" key3="+" key4="2"/> <key key0="ä" key4="`"/>
<key key0="q" key1="&apos;" key3="," key4="3"/> <!--right side-->
<key key0="k" key3="."/> <key key0="ö" key3="+"/>
<key width="1.5" key0="backspace" key1="delete" key3=";"/> <key key0="y" key3="%" key4="1"/>
<key key0="z" key3="," key1="&quot;" key4="2"/>
<key key0="k" key3="." key1="&apos;" key4="3"/>
<key width="1.5" key0="backspace" key3=";" key1="delete"/>
</row> </row>
<!--bottom row-->
<row height="0.95"> <row height="0.95">
<key width="1.8" key0="ctrl" key2="loc meta" key4="switch_numeric"/> <key width="1.8" key0="ctrl" key2="loc meta" key4="switch_numeric"/>
<key width="1.2" key0="fn" key1="loc alt" key2="loc change_method" key3="switch_emoji" key4="config"/> <key width="1.2" key0="fn" key1="loc alt" key2="loc change_method" key3="switch_emoji" key4="config"/>
<key width="4.0" key0="space" key7="switch_forward" key8="0"/> <key width="5.0" key0="space" key7="switch_forward" key8="0"/>
<key width="1.2" key7="up" key6="right" key5="left" key8="down"/> <key width="1.2" key5="left" key6="right" key7="up" key8="down"/>
<key width="1.8" key0="enter" key3="action"/> <key width="1.8" key0="enter" key3="action"/>
</row> </row>
</keyboard> </keyboard>

View File

@@ -1,54 +0,0 @@
package juloo.keyboard2;
import juloo.keyboard2.KeyValue;
import juloo.keyboard2.KeyValueParser;
import org.junit.Test;
import static org.junit.Assert.*;
public class KeyValueParserTest
{
public KeyValueParserTest() {}
@Test
public void parse() throws Exception
{
Utils.parse(":str:'Foo'", KeyValue.makeStringKey("Foo"));
Utils.parse(":str flags='dim':'Foo'", KeyValue.makeStringKey("Foo", KeyValue.FLAG_SECONDARY));
Utils.parse(":str symbol='Symbol':'Foo'", KeyValue.makeStringKeyWithSymbol("Foo", "Symbol", 0));
Utils.parse(":str symbol='Symbol' flags='dim':'Foo'", KeyValue.makeStringKeyWithSymbol("Foo", "Symbol", KeyValue.FLAG_SECONDARY));
Utils.parse(":str flags='dim,small':'Foo'", KeyValue.makeStringKey("Foo", KeyValue.FLAG_SECONDARY | KeyValue.FLAG_SMALLER_FONT));
Utils.parse(":str flags=',,':'Foo'", KeyValue.makeStringKey("Foo")); // Unintentional
Utils.expect_error(":unknown:Foo"); // Unknown kind
Utils.expect_error(":str:Foo"); // Unquoted string
Utils.expect_error(":str flags:'Foo'"); // Malformed flags
Utils.expect_error(":str flags=dim:'Foo'"); // Unquoted flags
Utils.expect_error(":str unknown='foo':'Foo'"); // Unknown flags
// Unterminated
Utils.expect_error(":str");
Utils.expect_error(":str ");
Utils.expect_error(":str flags");
Utils.expect_error(":str flags=");
Utils.expect_error(":str flags='");
Utils.expect_error(":str flags='' ");
Utils.expect_error(":str flags='':");
Utils.expect_error(":str flags='':'");
}
/** JUnit removes these functions from stacktraces. */
static class Utils
{
static void parse(String key_descr, KeyValue ref) throws Exception
{
assertEquals(ref, KeyValueParser.parse(key_descr));
}
static void expect_error(String key_descr)
{
try
{
fail("Expected failure but got " + KeyValueParser.parse(key_descr));
}
catch (KeyValueParser.ParseError e) {}
}
}
}

View File

@@ -1,16 +0,0 @@
package juloo.keyboard2;
import juloo.keyboard2.KeyValue;
import org.junit.Test;
import static org.junit.Assert.*;
public class KeyValueTest
{
public KeyValueTest() {}
@Test
public void equals()
{
assertEquals(KeyValue.makeStringKeyWithSymbol("Foo", "Symbol", 0), KeyValue.makeStringKeyWithSymbol("Foo", "Symbol", 0));
}
}