2023-06-24 23:29:24 +02:00
|
|
|
package juloo.keyboard2;
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
2023-08-06 01:30:02 +02:00
|
|
|
import java.util.Arrays;
|
2023-08-06 16:49:29 +02:00
|
|
|
import java.util.Collection;
|
|
|
|
import java.util.Collections;
|
2023-06-24 23:29:24 +02:00
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.Set;
|
|
|
|
|
2024-01-13 20:59:05 +01:00
|
|
|
public final class ExtraKeys
|
2023-06-24 23:29:24 +02:00
|
|
|
{
|
2023-08-06 16:49:29 +02:00
|
|
|
public static final ExtraKeys EMPTY = new ExtraKeys(Collections.EMPTY_LIST);
|
2023-06-24 23:29:24 +02:00
|
|
|
|
2023-08-06 16:49:29 +02:00
|
|
|
Collection<ExtraKey> _ks;
|
2023-06-24 23:29:24 +02:00
|
|
|
|
2023-08-06 16:49:29 +02:00
|
|
|
public ExtraKeys(Collection<ExtraKey> ks)
|
2023-06-24 23:29:24 +02:00
|
|
|
{
|
2023-08-06 16:49:29 +02:00
|
|
|
_ks = ks;
|
2023-06-24 23:29:24 +02:00
|
|
|
}
|
|
|
|
|
2023-08-06 17:11:29 +02:00
|
|
|
/** Add the keys that should be added to the keyboard into [dst]. Keys
|
|
|
|
already added to [dst] might have an impact, see [ExtraKey.compute]. */
|
2023-09-15 18:00:27 +02:00
|
|
|
public void compute(Map<KeyValue, KeyboardData.PreferredPos> dst, Query q)
|
2023-06-24 23:29:24 +02:00
|
|
|
{
|
2023-08-06 01:30:02 +02:00
|
|
|
for (ExtraKey k : _ks)
|
2023-08-06 16:49:29 +02:00
|
|
|
k.compute(dst, q);
|
2023-06-24 23:29:24 +02:00
|
|
|
}
|
|
|
|
|
2023-08-06 16:49:29 +02:00
|
|
|
public static ExtraKeys parse(String script, String str)
|
2023-06-24 23:29:24 +02:00
|
|
|
{
|
2023-08-06 16:49:29 +02:00
|
|
|
Collection<ExtraKey> dst = new ArrayList<ExtraKey>();
|
2023-06-24 23:29:24 +02:00
|
|
|
String[] ks = str.split("\\|");
|
|
|
|
for (int i = 0; i < ks.length; i++)
|
2023-08-06 01:30:02 +02:00
|
|
|
dst.add(ExtraKey.parse(ks[i], script));
|
2023-08-06 16:49:29 +02:00
|
|
|
return new ExtraKeys(dst);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Merge identical keys. This is required to decide whether to add
|
|
|
|
alternatives. Script is generalized (set to null) on any conflict. */
|
|
|
|
public static ExtraKeys merge(List<ExtraKeys> kss)
|
|
|
|
{
|
|
|
|
Map<KeyValue, ExtraKey> merged_keys = new HashMap<KeyValue, ExtraKey>();
|
|
|
|
for (ExtraKeys ks : kss)
|
|
|
|
for (ExtraKey k : ks._ks)
|
|
|
|
{
|
|
|
|
ExtraKey k2 = merged_keys.get(k.kv);
|
|
|
|
if (k2 != null)
|
|
|
|
k = k.merge_with(k2);
|
|
|
|
merged_keys.put(k.kv, k);
|
|
|
|
}
|
|
|
|
return new ExtraKeys(merged_keys.values());
|
2023-06-24 23:29:24 +02:00
|
|
|
}
|
2023-08-06 01:30:02 +02:00
|
|
|
|
|
|
|
final static class ExtraKey
|
|
|
|
{
|
|
|
|
/** The key to add. */
|
|
|
|
final KeyValue kv;
|
|
|
|
/** The key will be added to layouts of the same script. If null, might be
|
|
|
|
added to layouts of any script. */
|
|
|
|
final String script;
|
|
|
|
/** The key will not be added to layout that already contain all the
|
|
|
|
alternatives. */
|
|
|
|
final List<KeyValue> alternatives;
|
2023-09-24 16:35:24 +02:00
|
|
|
/** The key next to which to add. Might be [null]. */
|
|
|
|
final KeyValue next_to;
|
2023-08-06 01:30:02 +02:00
|
|
|
|
2023-09-24 16:35:24 +02:00
|
|
|
ExtraKey(KeyValue kv_, String script_, List<KeyValue> alts_, KeyValue next_to_)
|
2023-08-06 01:30:02 +02:00
|
|
|
{
|
|
|
|
kv = kv_;
|
|
|
|
script = script_;
|
|
|
|
alternatives = alts_;
|
2023-09-24 16:35:24 +02:00
|
|
|
next_to = next_to_;
|
2023-08-06 01:30:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Whether the key should be added to the keyboard. */
|
2023-09-15 18:00:27 +02:00
|
|
|
public void compute(Map<KeyValue, KeyboardData.PreferredPos> dst, Query q)
|
2023-08-06 16:49:29 +02:00
|
|
|
{
|
|
|
|
// 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
|
|
|
|
// appear again in the list of extra keys with a different list of
|
|
|
|
// alternatives.
|
2023-08-06 17:11:29 +02:00
|
|
|
// Selecting the dead key in the "Add key to the keyboard" option would
|
|
|
|
// disable this behavior for a key.
|
2023-09-15 18:00:27 +02:00
|
|
|
boolean use_alternative = (alternatives.size() == 1 && !dst.containsKey(kv));
|
2023-08-06 16:49:29 +02:00
|
|
|
if
|
|
|
|
((q.script == null || script == null || q.script.equals(script))
|
|
|
|
&& (alternatives.size() == 0 || !q.present.containsAll(alternatives)))
|
2023-09-15 18:00:27 +02:00
|
|
|
{
|
|
|
|
KeyValue kv_ = use_alternative ? alternatives.get(0) : kv;
|
2023-09-24 16:35:24 +02:00
|
|
|
KeyboardData.PreferredPos pos = KeyboardData.PreferredPos.DEFAULT;
|
|
|
|
if (next_to != null)
|
|
|
|
{
|
|
|
|
pos = new KeyboardData.PreferredPos(pos);
|
|
|
|
pos.next_to = next_to;
|
|
|
|
}
|
|
|
|
dst.put(kv_, pos);
|
2023-09-15 18:00:27 +02:00
|
|
|
}
|
2023-08-06 16:49:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Return a new key from two. [kv] are expected to be equal. [script] is
|
|
|
|
generalized to [null] on any conflict. [alternatives] are concatenated.
|
|
|
|
*/
|
|
|
|
public ExtraKey merge_with(ExtraKey k2)
|
2023-08-06 01:30:02 +02:00
|
|
|
{
|
2023-09-24 16:35:24 +02:00
|
|
|
String script_ = one_or_none(script, k2.script);
|
2023-08-06 16:49:29 +02:00
|
|
|
List<KeyValue> alts = new ArrayList<KeyValue>(alternatives);
|
2023-09-24 16:35:24 +02:00
|
|
|
KeyValue next_to_ = one_or_none(next_to, k2.next_to);
|
2023-08-06 16:49:29 +02:00
|
|
|
alts.addAll(k2.alternatives);
|
2023-09-24 16:35:24 +02:00
|
|
|
return new ExtraKey(kv, script_, alts, next_to_);
|
2023-08-06 01:30:02 +02:00
|
|
|
}
|
|
|
|
|
2023-09-24 16:35:24 +02:00
|
|
|
/** If one of [a] or [b] is null, return the other. If [a] and [b] are
|
|
|
|
equal, return [a]. Otherwise, return null. */
|
|
|
|
<E> E one_or_none(E a, E b)
|
|
|
|
{
|
|
|
|
return (a == null) ? b : (b == null || a.equals(b)) ? a : null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Extra keys are of the form "key name" or "key name:alt1:alt2@next_to". */
|
2023-08-06 01:30:02 +02:00
|
|
|
public static ExtraKey parse(String str, String script)
|
|
|
|
{
|
2023-09-24 16:35:24 +02:00
|
|
|
String[] split_on_at = str.split("@", 2);
|
|
|
|
String[] key_names = split_on_at[0].split(":");
|
|
|
|
KeyValue kv = KeyValue.getKeyByName(key_names[0]);
|
|
|
|
KeyValue[] alts = new KeyValue[key_names.length-1];
|
|
|
|
for (int i = 1; i < key_names.length; i++)
|
|
|
|
alts[i-1] = KeyValue.getKeyByName(key_names[i]);
|
|
|
|
KeyValue next_to = null;
|
|
|
|
if (split_on_at.length > 1)
|
|
|
|
next_to = KeyValue.getKeyByName(split_on_at[1]);
|
|
|
|
return new ExtraKey(kv, script, Arrays.asList(alts), next_to);
|
2023-08-06 01:30:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public final static class Query
|
|
|
|
{
|
|
|
|
/** Script of the current layout. Might be null. */
|
|
|
|
final String script;
|
|
|
|
/** Keys present on the layout. */
|
|
|
|
final Set<KeyValue> present;
|
|
|
|
|
|
|
|
public Query(String script_, Set<KeyValue> present_)
|
|
|
|
{
|
|
|
|
script = script_;
|
|
|
|
present = present_;
|
|
|
|
}
|
|
|
|
}
|
2023-06-24 23:29:24 +02:00
|
|
|
}
|