abouttreesummaryrefslogcommitdiff
diff options
context:
space:
mode:
authorJules Aguillon2022-07-24 20:02:48 +0200
committerJules Aguillon2022-07-24 20:02:48 +0200
commit324756535e139aacfb9d828a5bc9a2a6fef634ea (patch)
tree717553aab3e9bd5a0536aa8b2e2a3a69d5e335ca
parent2d8ed2d85849c95c7d39e0dfe11405bf70eeb395 (diff)
downloadunexpected-keyboard-324756535e139aacfb9d828a5bc9a2a6fef634ea.tar.gz
unexpected-keyboard-324756535e139aacfb9d828a5bc9a2a6fef634ea.zip
Automatic capitalisation at beginning of sentences
Keep track of end-of-sentence characters while typing and automatically enable shift when appropriate. The last few characters just before the cursor need to be queried in some cases: Begin of input, cursor has moved or text is deleted. This might have a performance cost. This normally only enable shift but it also needs to disable shift when the cursor moves.
-rw-r--r--srcs/juloo.keyboard2/Autocapitalisation.java119
-rw-r--r--srcs/juloo.keyboard2/Keyboard2.java27
-rw-r--r--srcs/juloo.keyboard2/Keyboard2View.java21
-rw-r--r--srcs/juloo.keyboard2/KeyboardData.java30
-rw-r--r--srcs/juloo.keyboard2/Pointers.java25
5 files changed, 218 insertions, 4 deletions
diff --git a/srcs/juloo.keyboard2/Autocapitalisation.java b/srcs/juloo.keyboard2/Autocapitalisation.java
new file mode 100644
index 0000000..1affdab
--- /dev/null
+++ b/srcs/juloo.keyboard2/Autocapitalisation.java
@@ -0,0 +1,119 @@
+package juloo.keyboard2;
+
+import android.text.InputType;
+import android.text.TextUtils;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.KeyEvent;
+
+final class Autocapitalisation
+{
+ private boolean _enabled = false;
+ private boolean _beginning_of_sentence = false;
+
+ /** Keep track of the cursor to differentiate 'selection_updated' events
+ corresponding to typing from cursor movement. */
+ private int _cursor = 0;
+
+ public boolean should_enable_shift()
+ {
+ return _enabled && _beginning_of_sentence;
+ }
+
+ /** Returns [true] if shift should be on initially. The input connection
+ isn't stored. */
+ public void started(EditorInfo info, InputConnection ic)
+ {
+ if ((info.inputType & InputType.TYPE_TEXT_FLAG_CAP_SENTENCES) == 0)
+ {
+ _enabled = false;
+ return;
+ }
+ _enabled = true;
+ _beginning_of_sentence = ((info.initialCapsMode & TextUtils.CAP_MODE_SENTENCES) != 0);
+ _cursor = 0; // Just a guess
+ scan_text_before_cursor(10, ic);
+ }
+
+ public void typed(CharSequence c)
+ {
+ for (int i = 0; i < c.length(); i++)
+ typed(c.charAt(i));
+ }
+
+ public void typed(char c)
+ {
+ _cursor++;
+ if (is_beginning_of_sentence(c))
+ _beginning_of_sentence = true;
+ else if (!ignore_at_beginning_of_sentence(c))
+ _beginning_of_sentence = false;
+ }
+
+ public void selection_updated(int old_cursor, int new_cursor, InputConnection ic)
+ {
+ if (new_cursor == _cursor)
+ return;
+ // Text has been inserted
+ if (old_cursor == _cursor && new_cursor > old_cursor)
+ {
+ scan_text_before_cursor(Math.min(new_cursor - old_cursor, 10), ic);
+ }
+ else
+ {
+ // Cursor has moved or [_cursor] wasn't uptodate
+ _beginning_of_sentence = false;
+ scan_text_before_cursor(10, ic);
+ }
+ _cursor = new_cursor;
+ }
+
+ /** Updates [_cursor]. */
+ private void scan_text_before_cursor(int range, InputConnection ic)
+ {
+ if (!_enabled) // Don't query characters if disabled
+ return;
+ CharSequence text_before = ic.getTextBeforeCursor(range, 0);
+ if (text_before == null)
+ {
+ _beginning_of_sentence = false;
+ }
+ else
+ {
+ _beginning_of_sentence = true;
+ typed(text_before);
+ }
+ }
+
+ private boolean ignore_at_beginning_of_sentence(char c)
+ {
+ switch (c)
+ {
+ case ' ':
+ case '"':
+ case '\'':
+ case '(':
+ case '«':
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private boolean is_beginning_of_sentence(char c)
+ {
+ switch (c)
+ {
+ case '.':
+ case ';':
+ case '\n':
+ case '!':
+ case '?':
+ case '¿':
+ case '¡':
+ return true;
+ default:
+ return false;
+ }
+ }
+}
diff --git a/srcs/juloo.keyboard2/Keyboard2.java b/srcs/juloo.keyboard2/Keyboard2.java
index 8483a50..cec2b44 100644
--- a/srcs/juloo.keyboard2/Keyboard2.java
+++ b/srcs/juloo.keyboard2/Keyboard2.java
@@ -35,6 +35,7 @@ public class Keyboard2 extends InputMethodService
private ViewGroup _emojiPane = null;
private Config _config;
+ private Autocapitalisation _autocap = new Autocapitalisation();
private boolean _debug_logs = false;
@@ -57,6 +58,14 @@ public class Keyboard2 extends InputMethodService
_debug_logs = getResources().getBoolean(R.bool.debug_logs);
}
+ private void update_shift_state(boolean might_disable)
+ {
+ if (_autocap.should_enable_shift())
+ _keyboardView.set_shift_state(true);
+ else if (might_disable)
+ _keyboardView.set_shift_state(false);
+ }
+
private List<InputMethodSubtype> getEnabledSubtypes(InputMethodManager imm)
{
String pkg = getPackageName();
@@ -163,7 +172,7 @@ public class Keyboard2 extends InputMethodService
return getResources().getString(res);
}
- private void refreshEditorInfo(EditorInfo info)
+ private void refresh_action_label(EditorInfo info)
{
// First try to look at 'info.actionLabel', if it isn't set, look at
// 'imeOptions'.
@@ -210,11 +219,13 @@ public class Keyboard2 extends InputMethodService
public void onStartInputView(EditorInfo info, boolean restarting)
{
refreshConfig();
- refreshEditorInfo(info);
+ refresh_action_label(info);
if ((info.inputType & InputType.TYPE_CLASS_NUMBER) != 0)
_keyboardView.setKeyboard(getLayout(R.xml.numeric));
else
_keyboardView.setKeyboard(getLayout(_currentTextLayout));
+ _autocap.started(info, getCurrentInputConnection());
+ update_shift_state(false);
setInputView(_keyboardView);
if (_debug_logs)
log_editor_info(info);
@@ -237,6 +248,14 @@ public class Keyboard2 extends InputMethodService
}
@Override
+ public void onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd)
+ {
+ super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart, candidatesEnd);
+ _autocap.selection_updated(oldSelStart, newSelStart, getCurrentInputConnection());
+ update_shift_state(true);
+ }
+
+ @Override
public void onFinishInputView(boolean finishingInput)
{
super.onFinishInputView(finishingInput);
@@ -330,11 +349,15 @@ public class Keyboard2 extends InputMethodService
public void commitText(String text)
{
getCurrentInputConnection().commitText(text, 1);
+ _autocap.typed(text);
+ update_shift_state(false);
}
public void commitChar(char c)
{
sendKeyChar(c);
+ _autocap.typed(c);
+ update_shift_state(false);
}
}
diff --git a/srcs/juloo.keyboard2/Keyboard2View.java b/srcs/juloo.keyboard2/Keyboard2View.java
index fed2be6..d946aa1 100644
--- a/srcs/juloo.keyboard2/Keyboard2View.java
+++ b/srcs/juloo.keyboard2/Keyboard2View.java
@@ -90,6 +90,27 @@ public class Keyboard2View extends View
invalidate();
}
+ /** Called by auto-capitalisation. */
+ public void set_shift_state(boolean state)
+ {
+ KeyValue shift = KeyValue.getKeyByName("shift");
+ KeyboardData.Key key = _keyboard.findKeyWithValue(shift);
+ if (key == null)
+ {
+ // Lookup again for the lockable shift key, which is a different value.
+ shift = shift.withFlags(shift.getFlags() | KeyValue.FLAG_LOCK);
+ key = _keyboard.findKeyWithValue(shift);
+ }
+ if (key != null)
+ {
+ if (state)
+ _pointers.add_fake_pointer(shift, key);
+ else
+ _pointers.remove_fake_pointer(shift, key);
+ invalidate();
+ }
+ }
+
public KeyValue modifyKey(KeyValue k, Pointers.Modifiers mods)
{
return KeyModifier.modify(k, mods);
diff --git a/srcs/juloo.keyboard2/KeyboardData.java b/srcs/juloo.keyboard2/KeyboardData.java
index c95dc51..9b47152 100644
--- a/srcs/juloo.keyboard2/KeyboardData.java
+++ b/srcs/juloo.keyboard2/KeyboardData.java
@@ -43,6 +43,17 @@ class KeyboardData
return new KeyboardData(rows, keysWidth, extra_keys);
}
+ public Key findKeyWithValue(KeyValue kv)
+ {
+ for (Row r : rows)
+ {
+ Key k = r.findKeyWithValue(kv);
+ if (k != null)
+ return k;
+ }
+ return null;
+ }
+
private static void addExtraKeys_to_row(ArrayList<Row> rows, final Iterator<KeyValue> extra_keys, int row_i, final int d)
{
if (!extra_keys.hasNext())
@@ -168,6 +179,14 @@ class KeyboardData
public Key apply(Key k) { return k.scaleWidth(s); }
});
}
+
+ public Key findKeyWithValue(KeyValue kv)
+ {
+ for (Key k : keys)
+ if (k.hasValue(kv))
+ return k;
+ return null;
+ }
}
public static class Key
@@ -291,6 +310,17 @@ class KeyboardData
}
return (c == null) ? null : c.kv;
}
+
+ public boolean hasValue(KeyValue kv)
+ {
+ return (hasValue(key0, kv) || hasValue(key1, kv) || hasValue(key2, kv) ||
+ hasValue(key3, kv) || hasValue(key4, kv));
+ }
+
+ private static boolean hasValue(Corner c, KeyValue kv)
+ {
+ return (c != null && c.kv.equals(kv));
+ }
}
public static final class Corner
diff --git a/srcs/juloo.keyboard2/Pointers.java b/srcs/juloo.keyboard2/Pointers.java
index 9b8ee97..84323ae 100644
--- a/srcs/juloo.keyboard2/Pointers.java
+++ b/srcs/juloo.keyboard2/Pointers.java
@@ -74,6 +74,24 @@ public final class Pointers implements Handler.Callback
return -1;
}
+ /** Fake pointers are latched and not lockable. */
+ public void add_fake_pointer(KeyValue kv, KeyboardData.Key key)
+ {
+ // Avoid adding a fake pointer to a key that is already down.
+ if (isKeyDown(key))
+ return;
+ Pointer ptr = new Pointer(-1, key, kv, 0.f, 0.f, Modifiers.EMPTY);
+ ptr.flags = ptr.flags & ~(KeyValue.FLAG_LATCH | KeyValue.FLAG_LOCK);
+ _ptrs.add(ptr);
+ }
+
+ public void remove_fake_pointer(KeyValue kv, KeyboardData.Key key)
+ {
+ Pointer ptr = getLatched(key, kv);
+ if (ptr != null)
+ removePtr(ptr);
+ }
+
// Receiving events
public void onTouchUp(int pointerId)
@@ -252,8 +270,11 @@ public final class Pointers implements Handler.Callback
private Pointer getLatched(Pointer target)
{
- KeyboardData.Key k = target.key;
- KeyValue v = target.value;
+ return getLatched(target.key, target.value);
+ }
+
+ private Pointer getLatched(KeyboardData.Key k, KeyValue v)
+ {
if (v == null)
return null;
for (Pointer p : _ptrs)