Add the Action key

It is placed on the top-right of the enter key on every layouts.
It sends a special event (performEditorAction) instead of writing a
newline.

The "actionId" is passed through the EditorInfo object in an obfuscated
way so it's not clear whether it's using the right one.
This commit is contained in:
Jules Aguillon 2022-01-09 20:26:06 +01:00
parent 4b43645c4b
commit 53113cadd9
9 changed files with 86 additions and 16 deletions

View File

@ -39,6 +39,6 @@
<key width="1.2" key0="alt" key1="fn" key2="change_method" key3="switch_emoji" key4="config"/>
<key width="4.0" key0="space"/>
<key width="1.2" key1="up" key2="right" key3="left" key4="down"/>
<key width="1.8" key0="enter"/>
<key width="1.8" key0="enter" key2="action"/>
</row>
</keyboard>

View File

@ -30,6 +30,6 @@
<key width="1.5" key0="0"/>
<key width="0.75" key0="." key2=","/>
<key width="0.75" key0="space" key1="&quot;" key2="'" key4="_"/>
<key width="1.5" key0="enter" key2=" key3="="/>
<key width="1.5" key0="enter" key1="±" key2="action" key3="="/>
</row>
</keyboard>

View File

@ -39,6 +39,6 @@
<key width="1.2" key0="alt" key1="fn" key2="change_method" key3="switch_emoji" key4="config"/>
<key width="4.0" key0="space"/>
<key width="1.2" key1="up" key2="right" key3="left" key4="down"/>
<key width="1.8" key0="enter"/>
<key width="1.8" key0="enter" key2="action"/>
</row>
</keyboard>

View File

@ -34,6 +34,8 @@ final class Config
// Dynamically set
public boolean shouldOfferSwitchingToNextInputMethod;
public int key_flags_to_remove;
public String actionLabel; // Might be 'null'
public int actionId; // Meaningful only when 'actionLabel' isn't 'null'
public final IKeyEventHandler handler;
@ -62,6 +64,8 @@ final class Config
// initialized later
shouldOfferSwitchingToNextInputMethod = false;
key_flags_to_remove = 0;
actionLabel = null;
actionId = 0;
handler = h;
}

View File

@ -22,6 +22,7 @@ class KeyEventHandler implements Config.IKeyEventHandler
case KeyValue.EVENT_SWITCH_EMOJI: _recv.setPane_emoji(); return;
case KeyValue.EVENT_SWITCH_BACK_EMOJI: _recv.setPane_normal(); return;
case KeyValue.EVENT_CHANGE_METHOD: _recv.switchToNextInputMethod(); return;
case KeyValue.EVENT_ACTION: _recv.performAction(); return;
default:
if ((flags & (KeyValue.FLAG_CTRL | KeyValue.FLAG_ALT)) != 0)
handleMetaKeyUp(key, flags);
@ -64,6 +65,7 @@ class KeyEventHandler implements Config.IKeyEventHandler
public void setPane_emoji();
public void setPane_normal();
public void showKeyboardConfig();
public void performAction();
/** 'res_id' is '-1' for the currently selected layout. */
public void setLayout(int res_id);

View File

@ -13,6 +13,7 @@ class KeyValue
public static final int EVENT_SWITCH_EMOJI = -5;
public static final int EVENT_SWITCH_BACK_EMOJI = -6;
public static final int EVENT_CHANGE_METHOD = -7;
public static final int EVENT_ACTION = -8;
public static final char CHAR_NONE = '\0';
// Behavior flags
@ -214,6 +215,7 @@ class KeyValue
addSpecialKey("switch_emoji", ":)", EVENT_SWITCH_EMOJI);
addSpecialKey("switch_back_emoji", "ABC", EVENT_SWITCH_BACK_EMOJI);
addSpecialKey("change_method", "", EVENT_CHANGE_METHOD);
addSpecialKey("action", "Action", EVENT_ACTION); // Will always be replaced
addEventKey("esc", "Esc", KeyEvent.KEYCODE_ESCAPE);
addEventKey("enter", "\uE800", KeyEvent.KEYCODE_ENTER, FLAG_KEY_FONT);

View File

@ -138,10 +138,45 @@ public class Keyboard2 extends InputMethodService
}
}
private String actionLabel_of_imeAction(int action)
{
switch (action)
{
case EditorInfo.IME_ACTION_UNSPECIFIED:
case EditorInfo.IME_ACTION_NEXT: return "Next";
case EditorInfo.IME_ACTION_DONE: return "Done";
case EditorInfo.IME_ACTION_GO: return "Go";
case EditorInfo.IME_ACTION_PREVIOUS: return "Prev";
case EditorInfo.IME_ACTION_SEARCH: return "Search";
case EditorInfo.IME_ACTION_SEND: return "Send";
case EditorInfo.IME_ACTION_NONE:
default: return null;
}
}
private void refreshEditorInfo(EditorInfo info)
{
// First try to look at 'info.actionLabel', if it isn't set, look at
// 'imeOptions'.
if (info.actionLabel != null)
{
_config.actionLabel = info.actionLabel.toString();
_config.actionId = info.actionId;
}
else
{
int action = info.imeOptions & EditorInfo.IME_MASK_ACTION;
_config.actionLabel = actionLabel_of_imeAction(action); // Might be null
_config.actionId = action;
}
}
@Override
public void onStartInputView(EditorInfo info, boolean restarting)
{
// Update '_config' before calling 'KeyboardView.setKeyboard'
refreshSubtypeImm();
refreshEditorInfo(info);
if ((info.inputType & InputType.TYPE_CLASS_NUMBER) != 0)
_keyboardView.setKeyboard(getLayout(R.xml.numeric));
else
@ -200,6 +235,14 @@ public class Keyboard2 extends InputMethodService
setInputView(_keyboardView);
}
public void performAction()
{
InputConnection conn = getCurrentInputConnection();
if (conn == null)
return;
conn.performEditorAction(_config.actionId);
}
public void setLayout(int res_id)
{
if (res_id == -1)

View File

@ -60,9 +60,18 @@ public class Keyboard2View extends View
public void setKeyboard(KeyboardData kw)
{
if (!_config.shouldOfferSwitchingToNextInputMethod)
kw = kw.removeKeys(new KeyboardData.RemoveKeysByEvent(KeyValue.EVENT_CHANGE_METHOD));
kw = kw.replaceKeys(
new KeyboardData.ReplaceKeysByEvent(KeyValue.EVENT_CHANGE_METHOD, null));
if (_config.key_flags_to_remove != 0)
kw = kw.removeKeys(new KeyboardData.RemoveKeysByFlags(_config.key_flags_to_remove));
kw = kw.replaceKeys(
new KeyboardData.ReplaceKeysByFlags(_config.key_flags_to_remove, null));
// Replace the action key to show the right label.
KeyValue action_key = null;
if (_config.actionLabel != null)
action_key = new KeyValue(_config.actionLabel, _config.actionLabel,
KeyValue.CHAR_NONE, KeyValue.EVENT_ACTION, KeyValue.FLAG_NOREPEAT);
kw = kw.replaceKeys(
new KeyboardData.ReplaceKeysByEvent(KeyValue.EVENT_ACTION, action_key));
_keyboard = kw;
reset();
}

View File

@ -57,11 +57,11 @@ class KeyboardData
return new KeyboardData(rows);
}
public KeyboardData removeKeys(MapKeys f)
public KeyboardData replaceKeys(MapKeys f)
{
ArrayList<Row> rows_ = new ArrayList<Row>();
for (Row r : rows)
rows_.add(r.removeKeys(f));
rows_.add(r.replaceKeys(f));
return new KeyboardData(rows_);
}
@ -105,11 +105,11 @@ class KeyboardData
return new Row(keys, h, shift);
}
public Row removeKeys(MapKeys f)
public Row replaceKeys(MapKeys f)
{
ArrayList<Key> keys_ = new ArrayList<Key>();
for (Key k : keys)
keys_.add(k.removeKeys(f));
keys_.add(k.replaceKeys(f));
return new Row(keys_, height, shift);
}
}
@ -157,7 +157,7 @@ class KeyboardData
return new Key(k0, k1, k2, k3, k4, width, shift);
}
public Key removeKeys(MapKeys f)
public Key replaceKeys(MapKeys f)
{
return new Key(f.map(key0), f.map(key1), f.map(key2), f.map(key3), f.map(key4), width, shift);
}
@ -168,27 +168,37 @@ class KeyboardData
public abstract KeyValue map(KeyValue k);
}
public static class RemoveKeysByFlags implements MapKeys
public static class ReplaceKeysByFlags implements MapKeys
{
private final int _flags;
private final KeyValue _replacement;
public RemoveKeysByFlags(int flags) { _flags = flags; }
public ReplaceKeysByFlags(int flags, KeyValue r)
{
_flags = flags;
_replacement = r;
}
public KeyValue map(KeyValue k)
{
return (k == null || (k.flags & _flags) != 0) ? null : k;
return (k != null && (k.flags & _flags) != 0) ? _replacement : k;
}
}
public static class RemoveKeysByEvent implements MapKeys
public static class ReplaceKeysByEvent implements MapKeys
{
private final int _eventCode;
private final KeyValue _replacement;
public RemoveKeysByEvent(int ev) { _eventCode = ev; }
public ReplaceKeysByEvent(int ev, KeyValue r)
{
_eventCode = ev;
_replacement = r;
}
public KeyValue map(KeyValue k)
{
return (k == null || k.eventCode == _eventCode) ? null : k;
return (k != null && k.eventCode == _eventCode) ? _replacement : k;
}
}
}