forked from extern/Unexpected-Keyboard
Refactor: Preferred positions for extra keys
The new PreferredPos class represents where an extra key should be placed Currently used to place keys at the same positions they were placed before.
This commit is contained in:
parent
d771e9d2c7
commit
66b1bdc9c9
@ -9,8 +9,10 @@ import android.util.TypedValue;
|
|||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
final class Config
|
final class Config
|
||||||
@ -59,8 +61,8 @@ final class Config
|
|||||||
public int actionId; // Meaningful only when 'actionLabel' isn't 'null'
|
public int actionId; // Meaningful only when 'actionLabel' isn't 'null'
|
||||||
public boolean swapEnterActionKey; // Swap the "enter" and "action" keys
|
public boolean swapEnterActionKey; // Swap the "enter" and "action" keys
|
||||||
public ExtraKeys extra_keys_subtype;
|
public ExtraKeys extra_keys_subtype;
|
||||||
public Set<KeyValue> extra_keys_param;
|
public Map<KeyValue, KeyboardData.PreferredPos> extra_keys_param;
|
||||||
public List<KeyValue> extra_keys_custom;
|
public Map<KeyValue, KeyboardData.PreferredPos> extra_keys_custom;
|
||||||
|
|
||||||
public final IKeyEventHandler handler;
|
public final IKeyEventHandler handler;
|
||||||
public boolean orientation_landscape = false;
|
public boolean orientation_landscape = false;
|
||||||
@ -174,16 +176,16 @@ final class Config
|
|||||||
final KeyValue action_key = action_key();
|
final KeyValue action_key = action_key();
|
||||||
// Extra keys are removed from the set as they are encountered during the
|
// Extra keys are removed from the set as they are encountered during the
|
||||||
// first iteration then automatically added.
|
// first iteration then automatically added.
|
||||||
final Set<KeyValue> extra_keys = new HashSet<KeyValue>();
|
final Map<KeyValue, KeyboardData.PreferredPos> extra_keys = new HashMap<KeyValue, KeyboardData.PreferredPos>();
|
||||||
final Set<KeyValue> remove_keys = new HashSet<KeyValue>();
|
final Set<KeyValue> remove_keys = new HashSet<KeyValue>();
|
||||||
extra_keys.addAll(extra_keys_param);
|
extra_keys.putAll(extra_keys_param);
|
||||||
extra_keys.addAll(extra_keys_custom);
|
extra_keys.putAll(extra_keys_custom);
|
||||||
if (extra_keys_subtype != null)
|
if (extra_keys_subtype != null)
|
||||||
{
|
{
|
||||||
Set<KeyValue> present = new HashSet<KeyValue>();
|
Set<KeyValue> present = new HashSet<KeyValue>();
|
||||||
present.addAll(kw.getKeys().keySet());
|
present.addAll(kw.getKeys().keySet());
|
||||||
present.addAll(extra_keys_param);
|
present.addAll(extra_keys_param.keySet());
|
||||||
present.addAll(extra_keys_custom);
|
present.addAll(extra_keys_custom.keySet());
|
||||||
extra_keys_subtype.compute(extra_keys,
|
extra_keys_subtype.compute(extra_keys,
|
||||||
new ExtraKeys.Query(kw.script, present));
|
new ExtraKeys.Query(kw.script, present));
|
||||||
}
|
}
|
||||||
@ -193,7 +195,7 @@ final class Config
|
|||||||
kw = kw.mapKeys(new KeyboardData.MapKeyValues() {
|
kw = kw.mapKeys(new KeyboardData.MapKeyValues() {
|
||||||
public KeyValue apply(KeyValue key, boolean localized)
|
public KeyValue apply(KeyValue key, boolean localized)
|
||||||
{
|
{
|
||||||
boolean is_extra_key = extra_keys.contains(key);
|
boolean is_extra_key = extra_keys.containsKey(key);
|
||||||
if (is_extra_key)
|
if (is_extra_key)
|
||||||
extra_keys.remove(key);
|
extra_keys.remove(key);
|
||||||
if (localized && !is_extra_key)
|
if (localized && !is_extra_key)
|
||||||
@ -246,7 +248,7 @@ final class Config
|
|||||||
if (number_row)
|
if (number_row)
|
||||||
kw = kw.addNumberRow();
|
kw = kw.addNumberRow();
|
||||||
if (extra_keys.size() > 0)
|
if (extra_keys.size() > 0)
|
||||||
kw = kw.addExtraKeys(extra_keys.iterator());
|
kw = kw.addExtraKeys(extra_keys.entrySet().iterator());
|
||||||
return kw;
|
return kw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,8 +10,9 @@ import android.util.AttributeSet;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import java.util.ArrayList;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
|
|
||||||
@ -30,14 +31,15 @@ public class CustomExtraKeysPreference extends ListGroupPreference<String>
|
|||||||
setKey(KEY);
|
setKey(KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<KeyValue> get(SharedPreferences prefs)
|
public static Map<KeyValue, KeyboardData.PreferredPos> get(SharedPreferences prefs)
|
||||||
{
|
{
|
||||||
List<KeyValue> kvs = new ArrayList<KeyValue>();
|
Map<KeyValue, KeyboardData.PreferredPos> kvs =
|
||||||
|
new HashMap<KeyValue, KeyboardData.PreferredPos>();
|
||||||
List<String> key_names = load_from_preferences(KEY, prefs, null, SERIALIZER);
|
List<String> key_names = load_from_preferences(KEY, prefs, null, SERIALIZER);
|
||||||
if (key_names != null)
|
if (key_names != null)
|
||||||
{
|
{
|
||||||
for (String key_name : key_names)
|
for (String key_name : key_names)
|
||||||
kvs.add(KeyValue.makeStringKey(key_name));
|
kvs.put(KeyValue.makeStringKey(key_name), KeyboardData.PreferredPos.DEFAULT);
|
||||||
}
|
}
|
||||||
return kvs;
|
return kvs;
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ class ExtraKeys
|
|||||||
|
|
||||||
/** Add the keys that should be added to the keyboard into [dst]. Keys
|
/** Add the keys that should be added to the keyboard into [dst]. Keys
|
||||||
already added to [dst] might have an impact, see [ExtraKey.compute]. */
|
already added to [dst] might have an impact, see [ExtraKey.compute]. */
|
||||||
public void compute(Set<KeyValue> dst, Query q)
|
public void compute(Map<KeyValue, KeyboardData.PreferredPos> dst, Query q)
|
||||||
{
|
{
|
||||||
for (ExtraKey k : _ks)
|
for (ExtraKey k : _ks)
|
||||||
k.compute(dst, q);
|
k.compute(dst, q);
|
||||||
@ -72,7 +72,7 @@ class ExtraKeys
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Whether the key should be added to the keyboard. */
|
/** Whether the key should be added to the keyboard. */
|
||||||
public void compute(Set<KeyValue> dst, Query q)
|
public void compute(Map<KeyValue, KeyboardData.PreferredPos> dst, Query q)
|
||||||
{
|
{
|
||||||
// Add the alternative if it's the only one. The list of alternatives is
|
// Add the alternative if it's the only one. The list of alternatives is
|
||||||
// enforced to be complete by the merging step. The same [kv] will not
|
// enforced to be complete by the merging step. The same [kv] will not
|
||||||
@ -80,11 +80,14 @@ class ExtraKeys
|
|||||||
// alternatives.
|
// alternatives.
|
||||||
// Selecting the dead key in the "Add key to the keyboard" option would
|
// Selecting the dead key in the "Add key to the keyboard" option would
|
||||||
// disable this behavior for a key.
|
// disable this behavior for a key.
|
||||||
boolean use_alternative = (alternatives.size() == 1 && !dst.contains(kv));
|
boolean use_alternative = (alternatives.size() == 1 && !dst.containsKey(kv));
|
||||||
if
|
if
|
||||||
((q.script == null || script == null || q.script.equals(script))
|
((q.script == null || script == null || q.script.equals(script))
|
||||||
&& (alternatives.size() == 0 || !q.present.containsAll(alternatives)))
|
&& (alternatives.size() == 0 || !q.present.containsAll(alternatives)))
|
||||||
dst.add(use_alternative ? alternatives.get(0) : kv);
|
{
|
||||||
|
KeyValue kv_ = use_alternative ? alternatives.get(0) : kv;
|
||||||
|
dst.put(kv_, KeyboardData.PreferredPos.DEFAULT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return a new key from two. [kv] are expected to be equal. [script] is
|
/** Return a new key from two. [kv] are expected to be equal. [script] is
|
||||||
|
@ -8,7 +8,8 @@ import android.preference.PreferenceCategory;
|
|||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import java.util.HashSet;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/** This class implements the "extra keys" preference but also defines the
|
/** This class implements the "extra keys" preference but also defines the
|
||||||
@ -100,14 +101,15 @@ public class ExtraKeysPreference extends PreferenceCategory
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Get the set of enabled extra keys. */
|
/** Get the set of enabled extra keys. */
|
||||||
public static Set<KeyValue> get_extra_keys(SharedPreferences prefs)
|
public static Map<KeyValue, KeyboardData.PreferredPos> get_extra_keys(SharedPreferences prefs)
|
||||||
{
|
{
|
||||||
HashSet<KeyValue> ks = new HashSet<KeyValue>();
|
Map<KeyValue, KeyboardData.PreferredPos> ks =
|
||||||
|
new HashMap<KeyValue, KeyboardData.PreferredPos>();
|
||||||
for (String key_name : extra_keys)
|
for (String key_name : extra_keys)
|
||||||
{
|
{
|
||||||
if (prefs.getBoolean(pref_key_of_key_name(key_name),
|
if (prefs.getBoolean(pref_key_of_key_name(key_name),
|
||||||
default_checked(key_name)))
|
default_checked(key_name)))
|
||||||
ks.add(KeyValue.getKeyByName(key_name));
|
ks.put(KeyValue.getKeyByName(key_name), KeyboardData.PreferredPos.DEFAULT);
|
||||||
}
|
}
|
||||||
return ks;
|
return ks;
|
||||||
}
|
}
|
||||||
|
@ -36,26 +36,68 @@ class KeyboardData
|
|||||||
return new KeyboardData(rows_, keysWidth, modmap, script);
|
return new KeyboardData(rows_, keysWidth, modmap, script);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Add keys from the given iterator into the keyboard. Extra keys are added
|
/** Add keys from the given iterator into the keyboard. Preferred position is
|
||||||
* on the empty key4 corner of the second row, from right to left. If there's
|
specified via [PreferredPos]. */
|
||||||
* not enough room, key3 of the second row is tried then key2 and key1 of the
|
public KeyboardData addExtraKeys(Iterator<Map.Entry<KeyValue, PreferredPos>> extra_keys)
|
||||||
* third row. */
|
|
||||||
public KeyboardData addExtraKeys(Iterator<KeyValue> k)
|
|
||||||
{
|
{
|
||||||
|
/* Keys that couldn't be placed at their preferred position. */
|
||||||
|
ArrayList<KeyValue> unplaced_keys = new ArrayList<KeyValue>();
|
||||||
ArrayList<Row> rows = new ArrayList<Row>(this.rows);
|
ArrayList<Row> rows = new ArrayList<Row>(this.rows);
|
||||||
addExtraKeys_to_row(rows, k, 1, 4);
|
while (extra_keys.hasNext())
|
||||||
addExtraKeys_to_row(rows, k, 1, 3);
|
|
||||||
addExtraKeys_to_row(rows, k, 2, 2);
|
|
||||||
addExtraKeys_to_row(rows, k, 2, 1);
|
|
||||||
if (k.hasNext())
|
|
||||||
{
|
{
|
||||||
for (int r = 0; r < rows.size(); r++)
|
Map.Entry<KeyValue, PreferredPos> kp = extra_keys.next();
|
||||||
for (int c = 1; c <= 4; c++)
|
if (!add_key_to_preferred_pos(rows, kp.getKey(), kp.getValue()))
|
||||||
addExtraKeys_to_row(rows, k, r, c);
|
unplaced_keys.add(kp.getKey());
|
||||||
}
|
}
|
||||||
|
for (KeyValue kv : unplaced_keys)
|
||||||
|
add_key_to_preferred_pos(rows, kv, PreferredPos.ANYWHERE);
|
||||||
return new KeyboardData(rows, keysWidth, modmap, script);
|
return new KeyboardData(rows, keysWidth, modmap, script);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Place a key on the keyboard according to its preferred position. Mutates
|
||||||
|
[rows]. Returns [false] if it couldn't be placed. */
|
||||||
|
boolean add_key_to_preferred_pos(List<Row> rows, KeyValue kv, PreferredPos pos)
|
||||||
|
{
|
||||||
|
for (KeyPos p : pos.positions)
|
||||||
|
if (add_key_to_pos(rows, kv, p))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Place a key on the keyboard. A value of [-1] in one of the coordinate
|
||||||
|
means that the key can be placed anywhere in that coordinate, see
|
||||||
|
[PreferredPos]. Mutates [rows]. Returns [false] if it couldn't be placed.
|
||||||
|
*/
|
||||||
|
boolean add_key_to_pos(List<Row> rows, KeyValue kv, KeyPos p)
|
||||||
|
{
|
||||||
|
int i_row = p.row;
|
||||||
|
int i_row_end = p.row;
|
||||||
|
if (p.row == -1) { i_row = 0; i_row_end = rows.size() - 1; }
|
||||||
|
for (; i_row <= i_row_end; i_row++)
|
||||||
|
{
|
||||||
|
Row row = rows.get(i_row);
|
||||||
|
int i_col = p.col;
|
||||||
|
int i_col_end = p.col;
|
||||||
|
if (p.col == -1) { i_col = 0; i_col_end = row.keys.size() - 1; }
|
||||||
|
for (; i_col <= i_col_end; i_col++)
|
||||||
|
{
|
||||||
|
Key col = row.keys.get(i_col);
|
||||||
|
int i_dir = p.dir;
|
||||||
|
int i_dir_end = p.dir;
|
||||||
|
if (p.dir == -1) { i_dir = 1; i_dir_end = 4; }
|
||||||
|
for (; i_dir <= i_dir_end; i_dir++)
|
||||||
|
{
|
||||||
|
if (col.getKeyValue(i_dir) == null)
|
||||||
|
{
|
||||||
|
row.keys.set(i_col, col.withKeyValue(i_dir, kv));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public KeyboardData addNumPad(KeyboardData num_pad)
|
public KeyboardData addNumPad(KeyboardData num_pad)
|
||||||
{
|
{
|
||||||
ArrayList<Row> extendedRows = new ArrayList<Row>();
|
ArrayList<Row> extendedRows = new ArrayList<Row>();
|
||||||
@ -107,20 +149,6 @@ class KeyboardData
|
|||||||
return _key_pos;
|
return _key_pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addExtraKeys_to_row(ArrayList<Row> rows, final Iterator<KeyValue> extra_keys, int row_i, final int d)
|
|
||||||
{
|
|
||||||
if (!extra_keys.hasNext() || row_i >= rows.size())
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Row bottom_row;
|
public static Row bottom_row;
|
||||||
public static Row number_row;
|
public static Row number_row;
|
||||||
public static KeyboardData num_pad;
|
public static KeyboardData num_pad;
|
||||||
@ -263,6 +291,11 @@ class KeyboardData
|
|||||||
return new Row(keys, h, shift);
|
return new Row(keys, h, shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Row copy()
|
||||||
|
{
|
||||||
|
return new Row(new ArrayList<Key>(keys), height, shift);
|
||||||
|
}
|
||||||
|
|
||||||
public void getKeys(Map<KeyValue, KeyPos> dst, int row)
|
public void getKeys(Map<KeyValue, KeyPos> dst, int row)
|
||||||
{
|
{
|
||||||
for (int c = 0; c < keys.size(); c++)
|
for (int c = 0; c < keys.size(); c++)
|
||||||
@ -490,6 +523,36 @@ class KeyboardData
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** See [addExtraKeys()]. */
|
||||||
|
public final static class PreferredPos
|
||||||
|
{
|
||||||
|
public static final PreferredPos DEFAULT;
|
||||||
|
public static final PreferredPos ANYWHERE;
|
||||||
|
|
||||||
|
/** Array of positions to try in order. The special value [-1] as [row],
|
||||||
|
[col] or [dir] means that the field is unspecified. Every possible
|
||||||
|
values are tried for unspecified fields. Unspecified fields are
|
||||||
|
searched in this order: [dir], [col], [row]. */
|
||||||
|
public KeyPos[] positions = ANYWHERE_POSITIONS;
|
||||||
|
|
||||||
|
public PreferredPos() {}
|
||||||
|
|
||||||
|
static final KeyPos[] ANYWHERE_POSITIONS =
|
||||||
|
new KeyPos[]{ new KeyPos(-1, -1, -1) };
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
DEFAULT = new PreferredPos();
|
||||||
|
DEFAULT.positions = new KeyPos[]{
|
||||||
|
new KeyPos(1, -1, 4),
|
||||||
|
new KeyPos(1, -1, 3),
|
||||||
|
new KeyPos(2, -1, 2),
|
||||||
|
new KeyPos(2, -1, 1)
|
||||||
|
};
|
||||||
|
ANYWHERE = new PreferredPos();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Parsing utils */
|
/** Parsing utils */
|
||||||
|
|
||||||
/** Returns [false] on [END_DOCUMENT] or [END_TAG], [true] otherwise. */
|
/** Returns [false] on [END_DOCUMENT] or [END_TAG], [true] otherwise. */
|
||||||
|
Loading…
Reference in New Issue
Block a user