mirror of
https://github.com/Julow/Unexpected-Keyboard.git
synced 2025-08-18 09:29:56 +02:00
Compare commits
2 Commits
macro-sync
...
delete-wor
Author | SHA1 | Date | |
---|---|---|---|
|
e60304b30d | ||
|
a6f9c72eb3 |
@@ -25,30 +25,31 @@ 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, `legend:key_def1,key_def2,...`.
|
- A macro, `symbol:key_def1,key_def2,...`.
|
||||||
This results in a key with legend `legend` that behaves as if the sequence of `key_def` had been pressed in order.
|
This results in a key 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.
|
||||||
`>` | `>`
|
`\\` | `\`
|
||||||
`"` | `"`
|
|
||||||
|
|
||||||
The characters `?`, `#`, and `@` do not need to be escaped when writing custom layouts. Internally, they can be escaped by prepending backslash (by typing `\?`, `\#`, and `\@`).
|
XML escape codes also work, including:
|
||||||
|
|
||||||
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.
|
Value | Escape code for
|
||||||
|
:------- | :------
|
||||||
|
`&` | `&`
|
||||||
|
`<` | `<`
|
||||||
|
`>` | `>`
|
||||||
|
`"` | `"`
|
||||||
|
|
||||||
## 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,6 +1,7 @@
|
|||||||
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;
|
||||||
@@ -26,9 +27,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(Handler h, Callback cb)
|
public Autocapitalisation(Looper looper, Callback cb)
|
||||||
{
|
{
|
||||||
_handler = h;
|
_handler = new Handler(looper);
|
||||||
_callback = cb;
|
_callback = cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,7 +2,6 @@ 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;
|
||||||
@@ -29,10 +28,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(IReceiver recv)
|
public KeyEventHandler(Looper looper, IReceiver recv)
|
||||||
{
|
{
|
||||||
_recv = recv;
|
_recv = recv;
|
||||||
_autocap = new Autocapitalisation(recv.getHandler(),
|
_autocap = new Autocapitalisation(looper,
|
||||||
this.new Autocapitalisation_callback());
|
this.new Autocapitalisation_callback());
|
||||||
_mods = Pointers.Modifiers.EMPTY;
|
_mods = Pointers.Modifiers.EMPTY;
|
||||||
}
|
}
|
||||||
@@ -325,73 +324,32 @@ public final class KeyEventHandler
|
|||||||
|
|
||||||
void evaluate_macro(KeyValue[] keys)
|
void evaluate_macro(KeyValue[] keys)
|
||||||
{
|
{
|
||||||
if (keys.length == 0)
|
final Pointers.Modifiers empty = Pointers.Modifiers.EMPTY;
|
||||||
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(Pointers.Modifiers.EMPTY);
|
mods_changed(empty);
|
||||||
evaluate_macro_loop(keys, 0, Pointers.Modifiers.EMPTY, _autocap.pause());
|
Pointers.Modifiers mods = empty;
|
||||||
}
|
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 = Pointers.Modifiers.EMPTY;
|
mods = 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 = Pointers.Modifiers.EMPTY;
|
mods = 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)
|
||||||
@@ -406,7 +364,6 @@ public final class KeyEventHandler
|
|||||||
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 InputConnection getCurrentInputConnection();
|
public InputConnection getCurrentInputConnection();
|
||||||
public Handler getHandler();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Autocapitalisation_callback implements Autocapitalisation.Callback
|
class Autocapitalisation_callback implements Autocapitalisation.Callback
|
||||||
|
@@ -6,7 +6,6 @@ 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;
|
||||||
@@ -39,7 +38,6 @@ 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;
|
||||||
|
|
||||||
@@ -109,8 +107,7 @@ public class Keyboard2 extends InputMethodService
|
|||||||
{
|
{
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
SharedPreferences prefs = DirectBootAwarePreferences.get_shared_preferences(this);
|
SharedPreferences prefs = DirectBootAwarePreferences.get_shared_preferences(this);
|
||||||
_handler = new Handler(getMainLooper());
|
_keyeventhandler = new KeyEventHandler(getMainLooper(), this.new Receiver());
|
||||||
_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();
|
||||||
@@ -484,11 +481,6 @@ public class Keyboard2 extends InputMethodService
|
|||||||
{
|
{
|
||||||
return Keyboard2.this.getCurrentInputConnection();
|
return Keyboard2.this.getCurrentInputConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Handler getHandler()
|
|
||||||
{
|
|
||||||
return _handler;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IBinder getConnectionToken()
|
private IBinder getConnectionToken()
|
||||||
|
@@ -74,11 +74,7 @@ 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
|
out.write(string.text.removeprefix('"').removesuffix('"'))
|
||||||
.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