abouttreesummaryrefslogcommitdiff
path: root/srcs/juloo.keyboard2/Pointers.java
diff options
context:
space:
mode:
authorJules Aguillon2022-02-20 13:09:39 +0100
committerJules Aguillon2022-02-20 13:09:39 +0100
commit51ff795be419ff5102b262c90371340f76c74f2c (patch)
tree36bcf5114d8f41a0e05407b8f5aceac493e47c6a /srcs/juloo.keyboard2/Pointers.java
parent632a9ac590b92a52afe57f1f0aaaa919f9dcebdc (diff)
downloadunexpected-keyboard-51ff795be419ff5102b262c90371340f76c74f2c.tar.gz
unexpected-keyboard-51ff795be419ff5102b262c90371340f76c74f2c.zip
Move pointer handling code to its own class
Separate the concerns and have a clearer interface between the two parts of the code.
Diffstat (limited to 'srcs/juloo.keyboard2/Pointers.java')
-rw-r--r--srcs/juloo.keyboard2/Pointers.java262
1 files changed, 262 insertions, 0 deletions
diff --git a/srcs/juloo.keyboard2/Pointers.java b/srcs/juloo.keyboard2/Pointers.java
new file mode 100644
index 0000000..0930c21
--- /dev/null
+++ b/srcs/juloo.keyboard2/Pointers.java
@@ -0,0 +1,262 @@
+package juloo.keyboard2;
+
+import android.os.Handler;
+import android.os.Message;
+import java.util.ArrayList;
+
+/**
+ * Manage pointers (fingers) on the screen and long presses.
+ * Call back to IPointerEventHandler.
+ */
+public final class Pointers implements Handler.Callback
+{
+ private Handler _keyrepeat_handler;
+ private ArrayList<Pointer> _ptrs = new ArrayList<Pointer>();
+ private IPointerEventHandler _handler;
+ private Config _config;
+
+ public Pointers(IPointerEventHandler h, Config c)
+ {
+ _keyrepeat_handler = new Handler(this);
+ _handler = h;
+ _config = c;
+ }
+
+ public int getFlags()
+ {
+ int flags = 0;
+ for (Pointer p : _ptrs)
+ flags |= p.flags;
+ return flags;
+ }
+
+ public void clear()
+ {
+ _ptrs.clear();
+ }
+
+ public boolean isKeyDown(KeyboardData.Key k)
+ {
+ for (Pointer p : _ptrs)
+ if (p.key == k)
+ return true;
+ return false;
+ }
+
+ /**
+ * These flags can be different:
+ * FLAG_LOCK Removed when the key is locked
+ * FLAG_LOCKED Added when the key is locked
+ * FLAG_LATCH Removed when the key is latched (released but not consumed yet)
+ * Returns [-1] if not found.
+ */
+ public int getKeyFlags(KeyValue kv)
+ {
+ for (Pointer p : _ptrs)
+ if (p.value == kv)
+ return p.flags;
+ return -1;
+ }
+
+ // Receiving events
+
+ public void onTouchUp(int pointerId)
+ {
+ Pointer ptr = getPtr(pointerId);
+ if (ptr == null)
+ return;
+ stopKeyRepeat(ptr);
+ Pointer latched = getLatched(ptr.value);
+ if (latched != null) // Already latched
+ {
+ removePtr(ptr); // Remove dupplicate
+ if ((latched.flags & KeyValue.FLAG_LOCK) != 0) // Locking key, toggle lock
+ {
+ latched.flags = (latched.flags & ~KeyValue.FLAG_LOCK) | KeyValue.FLAG_LOCKED;
+ _handler.onPointerFlagsChanged();
+ }
+ else // Otherwise, unlatch
+ {
+ removePtr(latched);
+ _handler.onPointerUp(ptr.value);
+ }
+ }
+ else if ((ptr.flags & KeyValue.FLAG_LATCH) != 0)
+ {
+ ptr.flags &= ~KeyValue.FLAG_LATCH;
+ ptr.pointerId = -1; // Latch
+ _handler.onPointerFlagsChanged();
+ }
+ else
+ {
+ clearLatched();
+ removePtr(ptr);
+ _handler.onPointerUp(ptr.value);
+ }
+ }
+
+ public void onTouchDown(float x, float y, int pointerId, KeyboardData.Key key)
+ {
+ KeyValue value = key.key0;
+ Pointer ptr = new Pointer(pointerId, key, value, x, y);
+ _ptrs.add(ptr);
+ if (value != null && (value.flags & KeyValue.FLAG_NOREPEAT) == 0)
+ startKeyRepeat(ptr);
+ _handler.onPointerDown(value);
+ }
+
+ public void onTouchMove(float x, float y, int pointerId)
+ {
+ Pointer ptr = getPtr(pointerId);
+ if (ptr == null)
+ return;
+ float dx = x - ptr.downX;
+ float dy = y - ptr.downY;
+ float dist = Math.abs(dx) + Math.abs(dy);
+ ptr.ptrDist = dist;
+ KeyValue newValue;
+ if (dist < _config.swipe_dist_px)
+ {
+ newValue = ptr.key.key0;
+ }
+ else if (ptr.key.edgekeys)
+ {
+ if (Math.abs(dy) > Math.abs(dx)) // vertical swipe
+ newValue = (dy < 0) ? ptr.key.key1 : ptr.key.key4;
+ else // horizontal swipe
+ newValue = (dx < 0) ? ptr.key.key3 : ptr.key.key2;
+ }
+ else
+ {
+ if (dx < 0) // left side
+ newValue = (dy < 0) ? ptr.key.key1 : ptr.key.key3;
+ else // right side
+ newValue = (dy < 0) ? ptr.key.key2 : ptr.key.key4;
+ }
+ if (newValue != null && newValue != ptr.value)
+ {
+ stopKeyRepeat(ptr);
+ ptr.value = newValue;
+ ptr.flags = newValue.flags;
+ if ((newValue.flags & KeyValue.FLAG_NOREPEAT) == 0)
+ startKeyRepeat(ptr);
+ _handler.onPointerSwipe(newValue);
+ }
+ }
+
+ // Pointers management
+
+ private Pointer getPtr(int pointerId)
+ {
+ for (Pointer p : _ptrs)
+ if (p.pointerId == pointerId)
+ return p;
+ return null;
+ }
+
+ private void removePtr(Pointer ptr)
+ {
+ _ptrs.remove(ptr);
+ }
+
+ private Pointer getLatched(KeyValue kv)
+ {
+ for (Pointer p : _ptrs)
+ if (p.value == kv && p.pointerId == -1)
+ return p;
+ return null;
+ }
+
+ private void clearLatched()
+ {
+ for (int i = _ptrs.size() - 1; i >= 0; i--)
+ {
+ Pointer ptr = _ptrs.get(i);
+ // Latched and not locked, remove
+ if (ptr.pointerId == -1 && (ptr.flags & KeyValue.FLAG_LOCKED) == 0)
+ _ptrs.remove(i);
+ // Not latched but pressed, don't latch once released
+ else if ((ptr.flags & KeyValue.FLAG_LATCH) != 0)
+ ptr.flags &= ~KeyValue.FLAG_LATCH;
+ }
+ }
+
+ // Key repeat
+
+ /** Message from [_keyrepeat_handler]. */
+ @Override
+ public boolean handleMessage(Message msg)
+ {
+ for (Pointer ptr : _ptrs)
+ {
+ if (ptr.timeoutWhat == msg.what)
+ {
+ long nextInterval = _config.longPressInterval;
+ if (_config.preciseRepeat && (ptr.flags & KeyValue.FLAG_PRECISE_REPEAT) != 0)
+ {
+ // Modulate repeat interval depending on the distance of the pointer
+ float accel = Math.min(4.f, Math.max(0.3f, ptr.ptrDist / (_config.swipe_dist_px * 15.f)));
+ nextInterval = (long)((float)nextInterval / accel);
+ }
+ _keyrepeat_handler.sendEmptyMessageDelayed(msg.what, nextInterval);
+ _handler.onPointerHold(ptr.value);
+ return (true);
+ }
+ }
+ return (false);
+ }
+
+ private static int uniqueTimeoutWhat = 0;
+
+ private void startKeyRepeat(Pointer ptr)
+ {
+ int what = (uniqueTimeoutWhat++);
+ ptr.timeoutWhat = what;
+ _keyrepeat_handler.sendEmptyMessageDelayed(what, _config.longPressTimeout);
+ }
+
+ private void stopKeyRepeat(Pointer ptr)
+ {
+ if (ptr.timeoutWhat != -1)
+ {
+ _keyrepeat_handler.removeMessages(ptr.timeoutWhat);
+ ptr.timeoutWhat = -1;
+ }
+ }
+
+ private final class Pointer
+ {
+ /** -1 when latched. */
+ public int pointerId;
+ public KeyboardData.Key key;
+ public KeyValue value;
+ public float downX;
+ public float downY;
+ /** Distance of the pointer to the initial press. */
+ public float ptrDist;
+ public int flags;
+ /** Identify timeout messages. */
+ public int timeoutWhat;
+
+ public Pointer(int p, KeyboardData.Key k, KeyValue v, float x, float y)
+ {
+ pointerId = p;
+ key = k;
+ value = v;
+ downX = x;
+ downY = y;
+ ptrDist = 0.f;
+ flags = (v == null) ? 0 : v.flags;
+ timeoutWhat = -1;
+ }
+ }
+
+ public interface IPointerEventHandler
+ {
+ public void onPointerDown(KeyValue k);
+ public void onPointerSwipe(KeyValue k);
+ public void onPointerUp(KeyValue k);
+ public void onPointerFlagsChanged();
+ public void onPointerHold(KeyValue k);
+ }
+}