mirror of
https://github.com/Julow/Unexpected-Keyboard.git
synced 2025-08-18 14:20:35 +02:00
Compare commits
9 Commits
delete-wor
...
selection-
Author | SHA1 | Date | |
---|---|---|---|
|
ab23a73357 | ||
|
9cfeb0f0c2 | ||
|
5e77fa84cf | ||
|
06fbc83c9c | ||
|
a123810247 | ||
|
906755b787 | ||
|
80c52460c7 | ||
|
13988ba2fe | ||
|
68c4ba96b7 |
@@ -25,31 +25,30 @@ Key values can be any of the following:
|
|||||||
+ `⏯:keyevent:85` A play/pause key (which has no effect in most apps).
|
+ `⏯:keyevent:85` A play/pause key (which has no effect in most apps).
|
||||||
+ `my@:'my.email@domain.com'` A key that sends an arbitrary string
|
+ `my@:'my.email@domain.com'` A key that sends an arbitrary string
|
||||||
|
|
||||||
- A macro, `symbol:key_def1,key_def2,...`.
|
- A macro, `legend:key_def1,key_def2,...`.
|
||||||
This results in a key that behaves as if the sequence of `key_def` had been pressed in order.
|
This results in a key with legend `legend` that behaves as if the sequence of `key_def` had been pressed in order.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
+ `CA:ctrl,a,ctrl,c` A key with legend CA that sends the sequence `ctrl+a`, `ctrl+c`.
|
+ `CA:ctrl,a,ctrl,c` A key with legend CA that sends the sequence `ctrl+a`, `ctrl+c`.
|
||||||
+ `Cd:ctrl,backspace` A key with legend Cd that sends the shortcut `ctrl+backspace`.
|
+ `Cd:ctrl,backspace` A key with legend Cd that sends the shortcut `ctrl+backspace`.
|
||||||
|
|
||||||
## Escape codes
|
### Escape codes
|
||||||
Value | Escape code for
|
|
||||||
|
When defining a key value, several characters have special effects. If you want a character not to have its usual effect but to be taken literally, you should "escape" it in the usual way for XML:
|
||||||
|
|
||||||
|
To get this character... | ...you can type
|
||||||
:---- | :------
|
:---- | :------
|
||||||
`\?` | `?`
|
A literal newline character, which is different from `enter` and `action` in certain apps. | `\n`
|
||||||
`\#` | `#`
|
A literal tab character, which is different from `tab` in certain apps. | `\t`
|
||||||
`\@` | `@`
|
`\` | `\\`
|
||||||
`\n` | Literal newline character. This is different from `enter` and `action` in certain apps.
|
`&` | `&`
|
||||||
`\t` | Literal tab character. This is different from `tab` in certain apps.
|
`<` | `<`
|
||||||
`\\` | `\`
|
`>` | `>`
|
||||||
|
`"` | `"`
|
||||||
|
|
||||||
XML escape codes also work, including:
|
The characters `?`, `#`, and `@` do not need to be escaped when writing custom layouts. Internally, they can be escaped by prepending backslash (by typing `\?`, `\#`, and `\@`).
|
||||||
|
|
||||||
Value | Escape code for
|
The characters `,` and `:` can be escaped in a key value, using single quotes. For example, this macro defines a key with legend `http` that sends a string containing `:`: `<key c="http:home,'https://'" />` For simplicity, `,` and `:` cannot be escaped in the key legend.
|
||||||
:------- | :------
|
|
||||||
`&` | `&`
|
|
||||||
`<` | `<`
|
|
||||||
`>` | `>`
|
|
||||||
`"` | `"`
|
|
||||||
|
|
||||||
## Modifiers
|
## Modifiers
|
||||||
System modifiers are sent to the app, which can take app-specific action.
|
System modifiers are sent to the app, which can take app-specific action.
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
package juloo.keyboard2;
|
package juloo.keyboard2;
|
||||||
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.inputmethod.EditorInfo;
|
import android.view.inputmethod.EditorInfo;
|
||||||
@@ -27,9 +26,9 @@ public final class Autocapitalisation
|
|||||||
InputType.TYPE_TEXT_FLAG_CAP_SENTENCES |
|
InputType.TYPE_TEXT_FLAG_CAP_SENTENCES |
|
||||||
InputType.TYPE_TEXT_FLAG_CAP_WORDS;
|
InputType.TYPE_TEXT_FLAG_CAP_WORDS;
|
||||||
|
|
||||||
public Autocapitalisation(Looper looper, Callback cb)
|
public Autocapitalisation(Handler h, Callback cb)
|
||||||
{
|
{
|
||||||
_handler = new Handler(looper);
|
_handler = h;
|
||||||
_callback = cb;
|
_callback = cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,6 +2,7 @@ package juloo.keyboard2;
|
|||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
import android.os.Handler;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
import android.view.KeyCharacterMap;
|
import android.view.KeyCharacterMap;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
@@ -28,10 +29,10 @@ public final class KeyEventHandler
|
|||||||
[setSelection] could be used instead. */
|
[setSelection] could be used instead. */
|
||||||
boolean _move_cursor_force_fallback = false;
|
boolean _move_cursor_force_fallback = false;
|
||||||
|
|
||||||
public KeyEventHandler(Looper looper, IReceiver recv)
|
public KeyEventHandler(IReceiver recv)
|
||||||
{
|
{
|
||||||
_recv = recv;
|
_recv = recv;
|
||||||
_autocap = new Autocapitalisation(looper,
|
_autocap = new Autocapitalisation(recv.getHandler(),
|
||||||
this.new Autocapitalisation_callback());
|
this.new Autocapitalisation_callback());
|
||||||
_mods = Pointers.Modifiers.EMPTY;
|
_mods = Pointers.Modifiers.EMPTY;
|
||||||
}
|
}
|
||||||
@@ -241,6 +242,7 @@ public final class KeyEventHandler
|
|||||||
case AUTOFILL: send_context_menu_action(android.R.id.autofill); break;
|
case AUTOFILL: send_context_menu_action(android.R.id.autofill); break;
|
||||||
case DELETE_WORD: send_key_down_up(KeyEvent.KEYCODE_DEL, KeyEvent.META_CTRL_ON | KeyEvent.META_CTRL_LEFT_ON); break;
|
case DELETE_WORD: send_key_down_up(KeyEvent.KEYCODE_DEL, KeyEvent.META_CTRL_ON | KeyEvent.META_CTRL_LEFT_ON); break;
|
||||||
case FORWARD_DELETE_WORD: send_key_down_up(KeyEvent.KEYCODE_FORWARD_DEL, KeyEvent.META_CTRL_ON | KeyEvent.META_CTRL_LEFT_ON); break;
|
case FORWARD_DELETE_WORD: send_key_down_up(KeyEvent.KEYCODE_FORWARD_DEL, KeyEvent.META_CTRL_ON | KeyEvent.META_CTRL_LEFT_ON); break;
|
||||||
|
case SELECTION_CANCEL: cancel_selection(); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,15 +260,17 @@ public final class KeyEventHandler
|
|||||||
return conn.getExtractedText(_move_cursor_req, 0);
|
return conn.getExtractedText(_move_cursor_req, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** [repeatition] might be negative, in which case the direction is reversed. */
|
/** [r] might be negative, in which case the direction is reversed. */
|
||||||
void handle_slider(KeyValue.Slider s, int repeatition)
|
void handle_slider(KeyValue.Slider s, int r)
|
||||||
{
|
{
|
||||||
switch (s)
|
switch (s)
|
||||||
{
|
{
|
||||||
case Cursor_left: move_cursor(-repeatition); break;
|
case Cursor_left: move_cursor(-r); break;
|
||||||
case Cursor_right: move_cursor(repeatition); break;
|
case Cursor_right: move_cursor(r); break;
|
||||||
case Cursor_up: move_cursor_vertical(-repeatition); break;
|
case Cursor_up: move_cursor_vertical(-r); break;
|
||||||
case Cursor_down: move_cursor_vertical(repeatition); break;
|
case Cursor_down: move_cursor_vertical(r); break;
|
||||||
|
case Selection_cursor_left: move_cursor_sel(r, true); break;
|
||||||
|
case Selection_cursor_right: move_cursor_sel(r, false); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,12 +284,7 @@ public final class KeyEventHandler
|
|||||||
if (conn == null)
|
if (conn == null)
|
||||||
return;
|
return;
|
||||||
ExtractedText et = get_cursor_pos(conn);
|
ExtractedText et = get_cursor_pos(conn);
|
||||||
int system_mods =
|
if (et != null && can_set_selection(conn))
|
||||||
KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON | KeyEvent.META_META_ON;
|
|
||||||
// Fallback to sending key events if system modifiers are activated or
|
|
||||||
// ExtractedText is not supported, for example on Termux.
|
|
||||||
if (!_move_cursor_force_fallback && et != null
|
|
||||||
&& (_meta_state & system_mods) == 0)
|
|
||||||
{
|
{
|
||||||
int sel_start = et.selectionStart;
|
int sel_start = et.selectionStart;
|
||||||
int sel_end = et.selectionEnd;
|
int sel_end = et.selectionEnd;
|
||||||
@@ -304,8 +303,45 @@ public final class KeyEventHandler
|
|||||||
sel_start = sel_end;
|
sel_start = sel_end;
|
||||||
}
|
}
|
||||||
if (conn.setSelection(sel_start, sel_end))
|
if (conn.setSelection(sel_start, sel_end))
|
||||||
return; // [setSelection] succeeded, don't fallback to key events
|
return; // Fallback to sending key events if [setSelection] failed
|
||||||
}
|
}
|
||||||
|
move_cursor_fallback(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Move one of the two side of a selection. If [sel_left] is true, the left
|
||||||
|
position is moved, otherwise the right position is moved. */
|
||||||
|
void move_cursor_sel(int d, boolean sel_left)
|
||||||
|
{
|
||||||
|
InputConnection conn = _recv.getCurrentInputConnection();
|
||||||
|
if (conn == null)
|
||||||
|
return;
|
||||||
|
ExtractedText et = get_cursor_pos(conn);
|
||||||
|
if (et != null && can_set_selection(conn))
|
||||||
|
{
|
||||||
|
int sel_start = et.selectionStart;
|
||||||
|
int sel_end = et.selectionEnd;
|
||||||
|
if (sel_left == (sel_start <= sel_end))
|
||||||
|
sel_start += d;
|
||||||
|
else
|
||||||
|
sel_end += d;
|
||||||
|
if (conn.setSelection(sel_start, sel_end))
|
||||||
|
return; // Fallback to sending key events if [setSelection] failed
|
||||||
|
}
|
||||||
|
move_cursor_fallback(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns whether the selection can be set using [conn.setSelection()].
|
||||||
|
This can happen on Termux or when system modifiers are activated for
|
||||||
|
example. */
|
||||||
|
boolean can_set_selection(InputConnection conn)
|
||||||
|
{
|
||||||
|
final int system_mods =
|
||||||
|
KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON | KeyEvent.META_META_ON;
|
||||||
|
return !_move_cursor_force_fallback && (_meta_state & system_mods) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void move_cursor_fallback(int d)
|
||||||
|
{
|
||||||
if (d < 0)
|
if (d < 0)
|
||||||
send_key_down_up_repeat(KeyEvent.KEYCODE_DPAD_LEFT, -d);
|
send_key_down_up_repeat(KeyEvent.KEYCODE_DPAD_LEFT, -d);
|
||||||
else
|
else
|
||||||
@@ -324,32 +360,73 @@ public final class KeyEventHandler
|
|||||||
|
|
||||||
void evaluate_macro(KeyValue[] keys)
|
void evaluate_macro(KeyValue[] keys)
|
||||||
{
|
{
|
||||||
final Pointers.Modifiers empty = Pointers.Modifiers.EMPTY;
|
if (keys.length == 0)
|
||||||
|
return;
|
||||||
// Ignore modifiers that are activated at the time the macro is evaluated
|
// Ignore modifiers that are activated at the time the macro is evaluated
|
||||||
mods_changed(empty);
|
mods_changed(Pointers.Modifiers.EMPTY);
|
||||||
Pointers.Modifiers mods = empty;
|
evaluate_macro_loop(keys, 0, Pointers.Modifiers.EMPTY, _autocap.pause());
|
||||||
final boolean autocap_paused = _autocap.pause();
|
}
|
||||||
for (KeyValue kv : keys)
|
|
||||||
|
/** Evaluate the macro asynchronously to make sure event are processed in the
|
||||||
|
right order. */
|
||||||
|
void evaluate_macro_loop(final KeyValue[] keys, int i, Pointers.Modifiers mods, final boolean autocap_paused)
|
||||||
|
{
|
||||||
|
boolean should_delay = false;
|
||||||
|
KeyValue kv = KeyModifier.modify(keys[i], mods);
|
||||||
|
if (kv != null)
|
||||||
{
|
{
|
||||||
kv = KeyModifier.modify(kv, mods);
|
|
||||||
if (kv == null)
|
|
||||||
continue;
|
|
||||||
if (kv.hasFlagsAny(KeyValue.FLAG_LATCH))
|
if (kv.hasFlagsAny(KeyValue.FLAG_LATCH))
|
||||||
{
|
{
|
||||||
// Non-special latchable keys clear latched modifiers
|
// Non-special latchable keys clear latched modifiers
|
||||||
if (!kv.hasFlagsAny(KeyValue.FLAG_SPECIAL))
|
if (!kv.hasFlagsAny(KeyValue.FLAG_SPECIAL))
|
||||||
mods = empty;
|
mods = Pointers.Modifiers.EMPTY;
|
||||||
mods = mods.with_extra_mod(kv);
|
mods = mods.with_extra_mod(kv);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
key_down(kv, false);
|
key_down(kv, false);
|
||||||
key_up(kv, mods);
|
key_up(kv, mods);
|
||||||
mods = empty;
|
mods = Pointers.Modifiers.EMPTY;
|
||||||
}
|
}
|
||||||
|
should_delay = wait_after_macro_key(kv);
|
||||||
}
|
}
|
||||||
|
i++;
|
||||||
|
if (i >= keys.length) // Stop looping
|
||||||
|
{
|
||||||
_autocap.unpause(autocap_paused);
|
_autocap.unpause(autocap_paused);
|
||||||
}
|
}
|
||||||
|
else if (should_delay)
|
||||||
|
{
|
||||||
|
// Add a delay before sending the next key to avoid race conditions
|
||||||
|
// causing keys to be handled in the wrong order. Notably, KeyEvent keys
|
||||||
|
// handling is scheduled differently than the other edit functions.
|
||||||
|
final int i_ = i;
|
||||||
|
final Pointers.Modifiers mods_ = mods;
|
||||||
|
_recv.getHandler().postDelayed(new Runnable() {
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
evaluate_macro_loop(keys, i_, mods_, autocap_paused);
|
||||||
|
}
|
||||||
|
}, 1000/30);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
evaluate_macro_loop(keys, i, mods, autocap_paused);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean wait_after_macro_key(KeyValue kv)
|
||||||
|
{
|
||||||
|
switch (kv.getKind())
|
||||||
|
{
|
||||||
|
case Keyevent:
|
||||||
|
case Editing:
|
||||||
|
case Event:
|
||||||
|
return true;
|
||||||
|
case Slider:
|
||||||
|
return _move_cursor_force_fallback;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Repeat calls to [send_key_down_up]. */
|
/** Repeat calls to [send_key_down_up]. */
|
||||||
void send_key_down_up_repeat(int event_code, int repeat)
|
void send_key_down_up_repeat(int event_code, int repeat)
|
||||||
@@ -358,12 +435,27 @@ public final class KeyEventHandler
|
|||||||
send_key_down_up(event_code);
|
send_key_down_up(event_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cancel_selection()
|
||||||
|
{
|
||||||
|
InputConnection conn = _recv.getCurrentInputConnection();
|
||||||
|
if (conn == null)
|
||||||
|
return;
|
||||||
|
ExtractedText et = get_cursor_pos(conn);
|
||||||
|
if (et == null) return;
|
||||||
|
final int curs = et.selectionStart;
|
||||||
|
// Notify the receiver as Android's [onUpdateSelection] is not triggered.
|
||||||
|
if (conn.setSelection(curs, curs));
|
||||||
|
_recv.selection_state_changed(false);
|
||||||
|
}
|
||||||
|
|
||||||
public static interface IReceiver
|
public static interface IReceiver
|
||||||
{
|
{
|
||||||
public void handle_event_key(KeyValue.Event ev);
|
public void handle_event_key(KeyValue.Event ev);
|
||||||
public void set_shift_state(boolean state, boolean lock);
|
public void set_shift_state(boolean state, boolean lock);
|
||||||
public void set_compose_pending(boolean pending);
|
public void set_compose_pending(boolean pending);
|
||||||
|
public void selection_state_changed(boolean selection_is_ongoing);
|
||||||
public InputConnection getCurrentInputConnection();
|
public InputConnection getCurrentInputConnection();
|
||||||
|
public Handler getHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
class Autocapitalisation_callback implements Autocapitalisation.Callback
|
class Autocapitalisation_callback implements Autocapitalisation.Callback
|
||||||
|
@@ -82,6 +82,7 @@ public final class KeyModifier
|
|||||||
case HOOK_ABOVE: return apply_compose(k, ComposeKeyData.accent_hook_above);
|
case HOOK_ABOVE: return apply_compose(k, ComposeKeyData.accent_hook_above);
|
||||||
case DOUBLE_GRAVE: return apply_compose(k, ComposeKeyData.accent_double_grave);
|
case DOUBLE_GRAVE: return apply_compose(k, ComposeKeyData.accent_double_grave);
|
||||||
case ARROW_RIGHT: return apply_combining_char(k, "\u20D7");
|
case ARROW_RIGHT: return apply_combining_char(k, "\u20D7");
|
||||||
|
case SELECTION_MODE: return apply_selection_mode(k);
|
||||||
default: return k;
|
default: return k;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -392,6 +393,34 @@ public final class KeyModifier
|
|||||||
return (name == null) ? k : KeyValue.getKeyByName(name);
|
return (name == null) ? k : KeyValue.getKeyByName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static KeyValue apply_selection_mode(KeyValue k)
|
||||||
|
{
|
||||||
|
String name = null;
|
||||||
|
switch (k.getKind())
|
||||||
|
{
|
||||||
|
case Char:
|
||||||
|
switch (k.getChar())
|
||||||
|
{
|
||||||
|
case ' ': name = "selection_cancel"; break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Slider:
|
||||||
|
switch (k.getSlider())
|
||||||
|
{
|
||||||
|
case Cursor_left: name = "selection_cursor_left"; break;
|
||||||
|
case Cursor_right: name = "selection_cursor_right"; break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Keyevent:
|
||||||
|
switch (k.getKeyevent())
|
||||||
|
{
|
||||||
|
case KeyEvent.KEYCODE_ESCAPE: name = "selection_cancel"; break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return (name == null) ? k : KeyValue.getKeyByName(name);
|
||||||
|
}
|
||||||
|
|
||||||
/** Compose the precomposed initial with the medial [kv]. */
|
/** Compose the precomposed initial with the medial [kv]. */
|
||||||
private static KeyValue combine_hangul_initial(KeyValue kv, int precomposed)
|
private static KeyValue combine_hangul_initial(KeyValue kv, int precomposed)
|
||||||
{
|
{
|
||||||
|
@@ -59,6 +59,7 @@ public final class KeyValue implements Comparable<KeyValue>
|
|||||||
BREVE,
|
BREVE,
|
||||||
BAR,
|
BAR,
|
||||||
FN,
|
FN,
|
||||||
|
SELECTION_MODE,
|
||||||
} // Last is be applied first
|
} // Last is be applied first
|
||||||
|
|
||||||
public static enum Editing
|
public static enum Editing
|
||||||
@@ -77,6 +78,7 @@ public final class KeyValue implements Comparable<KeyValue>
|
|||||||
AUTOFILL,
|
AUTOFILL,
|
||||||
DELETE_WORD,
|
DELETE_WORD,
|
||||||
FORWARD_DELETE_WORD,
|
FORWARD_DELETE_WORD,
|
||||||
|
SELECTION_CANCEL,
|
||||||
}
|
}
|
||||||
|
|
||||||
public static enum Placeholder
|
public static enum Placeholder
|
||||||
@@ -715,6 +717,9 @@ public final class KeyValue implements Comparable<KeyValue>
|
|||||||
case "cursor_right": return sliderKey(Slider.Cursor_right, 1);
|
case "cursor_right": return sliderKey(Slider.Cursor_right, 1);
|
||||||
case "cursor_up": return sliderKey(Slider.Cursor_up, 1);
|
case "cursor_up": return sliderKey(Slider.Cursor_up, 1);
|
||||||
case "cursor_down": return sliderKey(Slider.Cursor_down, 1);
|
case "cursor_down": return sliderKey(Slider.Cursor_down, 1);
|
||||||
|
case "selection_cancel": return editingKey("Esc", Editing.SELECTION_CANCEL, FLAG_SMALLER_FONT);
|
||||||
|
case "selection_cursor_left": return sliderKey(Slider.Selection_cursor_left, -1); // Move the left side of the selection
|
||||||
|
case "selection_cursor_right": return sliderKey(Slider.Selection_cursor_right, 1);
|
||||||
// These keys are not used
|
// These keys are not used
|
||||||
case "replaceText": return editingKey("repl", Editing.REPLACE);
|
case "replaceText": return editingKey("repl", Editing.REPLACE);
|
||||||
case "textAssist": return editingKey(0xE038, Editing.ASSIST);
|
case "textAssist": return editingKey(0xE038, Editing.ASSIST);
|
||||||
@@ -764,6 +769,9 @@ public final class KeyValue implements Comparable<KeyValue>
|
|||||||
case "௲": case "௳":
|
case "௲": case "௳":
|
||||||
return makeStringKey(name, FLAG_SMALLER_FONT);
|
return makeStringKey(name, FLAG_SMALLER_FONT);
|
||||||
|
|
||||||
|
/* Internal keys */
|
||||||
|
case "selection_mode": return makeInternalModifier(Modifier.SELECTION_MODE);
|
||||||
|
|
||||||
default: return null;
|
default: return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -780,7 +788,9 @@ public final class KeyValue implements Comparable<KeyValue>
|
|||||||
Cursor_left(0xE008),
|
Cursor_left(0xE008),
|
||||||
Cursor_right(0xE006),
|
Cursor_right(0xE006),
|
||||||
Cursor_up(0xE005),
|
Cursor_up(0xE005),
|
||||||
Cursor_down(0xE007);
|
Cursor_down(0xE007),
|
||||||
|
Selection_cursor_left(0xE008),
|
||||||
|
Selection_cursor_right(0xE006);
|
||||||
|
|
||||||
final String symbol;
|
final String symbol;
|
||||||
|
|
||||||
|
@@ -6,6 +6,7 @@ import android.content.Intent;
|
|||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.inputmethodservice.InputMethodService;
|
import android.inputmethodservice.InputMethodService;
|
||||||
import android.os.Build.VERSION;
|
import android.os.Build.VERSION;
|
||||||
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -38,6 +39,7 @@ public class Keyboard2 extends InputMethodService
|
|||||||
private ViewGroup _emojiPane = null;
|
private ViewGroup _emojiPane = null;
|
||||||
private ViewGroup _clipboard_pane = null;
|
private ViewGroup _clipboard_pane = null;
|
||||||
public int actionId; // Action performed by the Action key.
|
public int actionId; // Action performed by the Action key.
|
||||||
|
private Handler _handler;
|
||||||
|
|
||||||
private Config _config;
|
private Config _config;
|
||||||
|
|
||||||
@@ -107,7 +109,8 @@ public class Keyboard2 extends InputMethodService
|
|||||||
{
|
{
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
SharedPreferences prefs = DirectBootAwarePreferences.get_shared_preferences(this);
|
SharedPreferences prefs = DirectBootAwarePreferences.get_shared_preferences(this);
|
||||||
_keyeventhandler = new KeyEventHandler(getMainLooper(), this.new Receiver());
|
_handler = new Handler(getMainLooper());
|
||||||
|
_keyeventhandler = new KeyEventHandler(this.new Receiver());
|
||||||
Config.initGlobalConfig(prefs, getResources(), _keyeventhandler);
|
Config.initGlobalConfig(prefs, getResources(), _keyeventhandler);
|
||||||
prefs.registerOnSharedPreferenceChangeListener(this);
|
prefs.registerOnSharedPreferenceChangeListener(this);
|
||||||
_config = Config.globalConfig();
|
_config = Config.globalConfig();
|
||||||
@@ -359,6 +362,8 @@ public class Keyboard2 extends InputMethodService
|
|||||||
{
|
{
|
||||||
super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart, candidatesEnd);
|
super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart, candidatesEnd);
|
||||||
_keyeventhandler.selection_updated(oldSelStart, newSelStart);
|
_keyeventhandler.selection_updated(oldSelStart, newSelStart);
|
||||||
|
if ((oldSelStart == oldSelEnd) != (newSelStart == newSelEnd))
|
||||||
|
_keyboardView.set_selection_state(newSelStart != newSelEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -477,10 +482,20 @@ public class Keyboard2 extends InputMethodService
|
|||||||
_keyboardView.set_compose_pending(pending);
|
_keyboardView.set_compose_pending(pending);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void selection_state_changed(boolean selection_is_ongoing)
|
||||||
|
{
|
||||||
|
_keyboardView.set_selection_state(selection_is_ongoing);
|
||||||
|
}
|
||||||
|
|
||||||
public InputConnection getCurrentInputConnection()
|
public InputConnection getCurrentInputConnection()
|
||||||
{
|
{
|
||||||
return Keyboard2.this.getCurrentInputConnection();
|
return Keyboard2.this.getCurrentInputConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Handler getHandler()
|
||||||
|
{
|
||||||
|
return _handler;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private IBinder getConnectionToken()
|
private IBinder getConnectionToken()
|
||||||
|
@@ -139,6 +139,13 @@ public class Keyboard2View extends View
|
|||||||
set_fake_ptr_latched(_compose_key, _compose_kv, pending, false);
|
set_fake_ptr_latched(_compose_key, _compose_kv, pending, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Called from [Keybard2.onUpdateSelection]. */
|
||||||
|
public void set_selection_state(boolean selection_state)
|
||||||
|
{
|
||||||
|
set_fake_ptr_latched(KeyboardData.Key.EMPTY,
|
||||||
|
KeyValue.getKeyByName("selection_mode"), selection_state, true);
|
||||||
|
}
|
||||||
|
|
||||||
public KeyValue modifyKey(KeyValue k, Pointers.Modifiers mods)
|
public KeyValue modifyKey(KeyValue k, Pointers.Modifiers mods)
|
||||||
{
|
{
|
||||||
return KeyModifier.modify(k, mods);
|
return KeyModifier.modify(k, mods);
|
||||||
|
@@ -422,6 +422,8 @@ public final class KeyboardData
|
|||||||
indication = i;
|
indication = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static final Key EMPTY = new Key(new KeyValue[9], null, 0, 1.f, 1.f, null);
|
||||||
|
|
||||||
/** Read a key value attribute that have a synonym. Having both synonyms
|
/** Read a key value attribute that have a synonym. Having both synonyms
|
||||||
present at the same time is an error.
|
present at the same time is an error.
|
||||||
Returns [null] if the attributes are not present. */
|
Returns [null] if the attributes are not present. */
|
||||||
|
@@ -74,7 +74,11 @@ def sync_metadata(value_dir, strings):
|
|||||||
os.makedirs(meta_dir)
|
os.makedirs(meta_dir)
|
||||||
txt_file = os.path.join(meta_dir, fname)
|
txt_file = os.path.join(meta_dir, fname)
|
||||||
with open(txt_file, "w", encoding="utf-8") as out:
|
with open(txt_file, "w", encoding="utf-8") as out:
|
||||||
out.write(string.text.removeprefix('"').removesuffix('"'))
|
out.write(string.text
|
||||||
|
.replace("\\n", "\n")
|
||||||
|
.replace("\\'", "'")
|
||||||
|
.removeprefix('"')
|
||||||
|
.removesuffix('"'))
|
||||||
out.write("\n")
|
out.write("\n")
|
||||||
sync_meta_file("title.txt", ("app_name_release", None))
|
sync_meta_file("title.txt", ("app_name_release", None))
|
||||||
sync_meta_file("short_description.txt", ("short_description", None))
|
sync_meta_file("short_description.txt", ("short_description", None))
|
||||||
|
Reference in New Issue
Block a user