forked from extern/Unexpected-Keyboard
Automatically place localized keys on the layouts
Layouts no longer need to mention every localized keys and dead keys. They are now placed automatically starting from the second row on the bottom-right corner. The "loc " prefix is not removed to still be able to define a more optimal and consistent placement for some extra keys (eg. 'ß' near 's'). Programming layouts no longer need to place every dead keys.
This commit is contained in:
parent
89dfc782a7
commit
2e81cb5cf7
@ -83,17 +83,16 @@ make installd
|
||||
A programming layout must contains every ASCII characters.
|
||||
The current programming layouts are: QWERTY, Dvorak.
|
||||
|
||||
Ideally, programming layouts should allow to type every Latin-script languages
|
||||
by containing every dead-keys. See for example: 0bf7ff5 (Latvian), 573c13f (Swedish).
|
||||
It is also possible to add some characters that are hidden in other languages,
|
||||
for example 93e84ba (ß), though the space is limited.
|
||||
|
||||
Layouts are defined in XML, see `res/xml/qwerty.xml`. An entry must be added to
|
||||
the layout option in `res/values/arrays.xml`, to both `pref_layout_values`
|
||||
(correspond to the file name) and `pref_layout_entries` (display name).
|
||||
The layout must also be referenced in `srcs/juloo.keyboard2/Config.java` in
|
||||
`layoutId_of_string`.
|
||||
|
||||
Keys with a name starting in `loc ` are hidden unless they are configured for
|
||||
the user's installed languages in `res/xml/method.xml`. These keys are optional
|
||||
and will be added automatically when necessary.
|
||||
|
||||
Some users cannot easily type the characters close the the edges of the screen
|
||||
due to a bulky phone case. It is best to avoid placing important characters
|
||||
there (such as the digits or punctuation).
|
||||
|
@ -9,6 +9,7 @@ import android.preference.PreferenceManager;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.TypedValue;
|
||||
import android.view.KeyEvent;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
|
||||
@ -142,20 +143,26 @@ final class Config
|
||||
|
||||
/** Update the layout according to the configuration.
|
||||
* - Remove the switching key if it isn't needed
|
||||
* - Remove keys from other locales (not in 'extra_keys')
|
||||
* - Remove "localized" keys from other locales (not in 'extra_keys')
|
||||
* - Replace the action key to show the right label
|
||||
* - Swap the enter and action keys
|
||||
*/
|
||||
public KeyboardData modify_layout(KeyboardData kw)
|
||||
public KeyboardData modify_layout(KeyboardData original_kw)
|
||||
{
|
||||
// Update the name to avoid caching in KeyModifier
|
||||
final KeyValue action_key = (actionLabel == null) ? null :
|
||||
KeyValue.getKeyByName("action").withNameAndSymbol(actionLabel, actionLabel);
|
||||
return kw.replaceKeys(new KeyboardData.MapKeys() {
|
||||
// Extra keys are removed from the set as they are encountered during the
|
||||
// first iteration then automatically added.
|
||||
final Set<String> extra_keys = new HashSet<String>(this.extra_keys);
|
||||
KeyboardData kw = original_kw.mapKeys(new KeyboardData.MapKeyValues() {
|
||||
public KeyValue apply(KeyValue key)
|
||||
{
|
||||
if (key == null)
|
||||
return null;
|
||||
boolean is_extra_key = extra_keys.contains(key.name);
|
||||
if (is_extra_key)
|
||||
extra_keys.remove(key.name);
|
||||
switch (key.eventCode)
|
||||
{
|
||||
case KeyValue.EVENT_CHANGE_METHOD:
|
||||
@ -170,9 +177,7 @@ final class Config
|
||||
default:
|
||||
if (key.flags != 0)
|
||||
{
|
||||
if ((key.flags & KeyValue.FLAG_LOCALIZED) != 0 &&
|
||||
extra_keys != null &&
|
||||
!extra_keys.contains(key.name))
|
||||
if ((key.flags & KeyValue.FLAG_LOCALIZED) != 0 && !is_extra_key)
|
||||
return null;
|
||||
if ((key.flags & lockable_modifiers) != 0)
|
||||
return key.withFlags(key.flags | KeyValue.FLAG_LOCK);
|
||||
@ -181,6 +186,17 @@ final class Config
|
||||
}
|
||||
}
|
||||
});
|
||||
if (extra_keys.size() > 0)
|
||||
{
|
||||
final Iterator<String> extra_keys_it = extra_keys.iterator();
|
||||
kw = kw.addExtraKeys(
|
||||
new Iterator<KeyValue>()
|
||||
{
|
||||
public boolean hasNext() { return extra_keys_it.hasNext(); }
|
||||
public KeyValue next() { return KeyValue.getKeyByName(extra_keys_it.next()); }
|
||||
});
|
||||
}
|
||||
return kw;
|
||||
}
|
||||
|
||||
private float getDipPref(DisplayMetrics dm, SharedPreferences prefs, String pref_name, float def)
|
||||
|
@ -295,7 +295,7 @@ public class Keyboard2 extends InputMethodService
|
||||
if (_config.programming_layout == -1)
|
||||
return;
|
||||
KeyboardData layout =
|
||||
getLayout(_config.programming_layout).replaceKeys(new KeyboardData.MapKeys() {
|
||||
getLayout(_config.programming_layout).mapKeys(new KeyboardData.MapKeyValues() {
|
||||
public KeyValue apply(KeyValue key)
|
||||
{
|
||||
if (key != null && key.eventCode == KeyValue.EVENT_SWITCH_PROGRAMMING)
|
||||
|
@ -3,10 +3,11 @@ package juloo.keyboard2;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.Function;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
class KeyboardData
|
||||
{
|
||||
@ -16,14 +17,42 @@ class KeyboardData
|
||||
/* Total height of the keyboard. Unit is abstract. */
|
||||
public final float keysHeight;
|
||||
|
||||
public KeyboardData replaceKeys(MapKeys f)
|
||||
public KeyboardData mapKeys(MapKey f)
|
||||
{
|
||||
ArrayList<Row> rows_ = new ArrayList<Row>();
|
||||
for (Row r : rows)
|
||||
rows_.add(r.replaceKeys(f));
|
||||
rows_.add(r.mapKeys(f));
|
||||
return new KeyboardData(rows_, keysWidth);
|
||||
}
|
||||
|
||||
/** Add keys from the given iterator into the keyboard. Extra keys are added
|
||||
* on the empty key4 corner of the second row, from right to left. If there's
|
||||
* not enough room, key3 of the second row is tried then key2 and key1 of the
|
||||
* third row. */
|
||||
public KeyboardData addExtraKeys(Iterator<KeyValue> k)
|
||||
{
|
||||
ArrayList<Row> rows = new ArrayList<Row>(this.rows);
|
||||
addExtraKeys_to_row(rows, k, 1, 4);
|
||||
addExtraKeys_to_row(rows, k, 1, 3);
|
||||
addExtraKeys_to_row(rows, k, 2, 2);
|
||||
addExtraKeys_to_row(rows, k, 2, 1);
|
||||
return new KeyboardData(rows, keysWidth);
|
||||
}
|
||||
|
||||
private static void addExtraKeys_to_row(ArrayList<Row> rows, final Iterator<KeyValue> extra_keys, int row_i, final int d)
|
||||
{
|
||||
if (!extra_keys.hasNext())
|
||||
return;
|
||||
rows.set(row_i, rows.get(row_i).mapKeys(new MapKey(){
|
||||
public Key apply(Key k) {
|
||||
if (k.getKeyValue(d) == null && extra_keys.hasNext())
|
||||
return k.withKeyValue(d, extra_keys.next());
|
||||
else
|
||||
return k;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private static Row _bottomRow = null;
|
||||
private static Map<Integer, KeyboardData> _layoutCache = new HashMap<Integer, KeyboardData>();
|
||||
|
||||
@ -117,22 +146,21 @@ class KeyboardData
|
||||
return new Row(keys, h, shift);
|
||||
}
|
||||
|
||||
public Row replaceKeys(MapKeys f)
|
||||
public Row mapKeys(MapKey f)
|
||||
{
|
||||
ArrayList<Key> keys_ = new ArrayList<Key>();
|
||||
for (Key k : keys)
|
||||
keys_.add(k.replaceKeys(f));
|
||||
keys_.add(f.apply(k));
|
||||
return new Row(keys_, height, shift);
|
||||
}
|
||||
|
||||
/** Change the width of every keys so that the row is 's' units wide. */
|
||||
public Row updateWidth(float newWidth)
|
||||
{
|
||||
float s = newWidth / keysWidth;
|
||||
ArrayList<Key> keys_ = new ArrayList<Key>();
|
||||
for (Key k : keys)
|
||||
keys_.add(k.scaleWidth(s));
|
||||
return new Row(keys_, height, shift);
|
||||
final float s = newWidth / keysWidth;
|
||||
return mapKeys(new MapKey(){
|
||||
public Key apply(Key k) { return k.scaleWidth(s); }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,18 +211,39 @@ class KeyboardData
|
||||
return new Key(k0, k1, k2, k3, k4, width, shift, edgekeys);
|
||||
}
|
||||
|
||||
public Key replaceKeys(MapKeys f)
|
||||
{
|
||||
return new Key(f.apply(key0), f.apply(key1), f.apply(key2),
|
||||
f.apply(key3), f.apply(key4), width, shift, edgekeys);
|
||||
}
|
||||
|
||||
/** New key with the width multiplied by 's'. */
|
||||
public Key scaleWidth(float s)
|
||||
{
|
||||
return new Key(key0, key1, key2, key3, key4, width * s, shift, edgekeys);
|
||||
}
|
||||
|
||||
public KeyValue getKeyValue(int i)
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
case 0: return key0;
|
||||
case 1: return key1;
|
||||
case 2: return key2;
|
||||
case 3: return key3;
|
||||
case 4: return key4;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Key withKeyValue(int i, KeyValue kv)
|
||||
{
|
||||
KeyValue k0 = key0, k1 = key1, k2 = key2, k3 = key3, k4 = key4;
|
||||
switch (i)
|
||||
{
|
||||
case 0: k0 = kv; break;
|
||||
case 1: k1 = kv; break;
|
||||
case 2: k2 = kv; break;
|
||||
case 3: k3 = kv; break;
|
||||
case 4: k4 = kv; break;
|
||||
}
|
||||
return new Key(k0, k1, k2, k3, k4, width, shift, edgekeys);
|
||||
}
|
||||
|
||||
/*
|
||||
* See Pointers.onTouchMove() for the represented direction.
|
||||
*/
|
||||
@ -235,8 +284,18 @@ class KeyboardData
|
||||
}
|
||||
|
||||
// Not using Function<KeyValue, KeyValue> to keep compatibility with Android 6.
|
||||
public static abstract interface MapKeys {
|
||||
public KeyValue apply(KeyValue kv);
|
||||
public static abstract interface MapKey {
|
||||
public Key apply(Key k);
|
||||
}
|
||||
|
||||
public static abstract class MapKeyValues implements MapKey {
|
||||
abstract public KeyValue apply(KeyValue kv);
|
||||
|
||||
public Key apply(Key k)
|
||||
{
|
||||
return new Key(apply(k.key0), apply(k.key1), apply(k.key2),
|
||||
apply(k.key3), apply(k.key4), k.width, k.shift, k.edgekeys);
|
||||
}
|
||||
}
|
||||
|
||||
/** Parsing utils */
|
||||
|
Loading…
Reference in New Issue
Block a user