2022-02-20 13:09:39 +01:00
|
|
|
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()
|
2022-04-30 23:36:17 +02:00
|
|
|
{
|
|
|
|
return getFlags(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* When [skip_latched] is true, don't take flags of latched keys into account. */
|
|
|
|
private int getFlags(boolean skip_latched)
|
2022-02-20 13:09:39 +01:00
|
|
|
{
|
|
|
|
int flags = 0;
|
|
|
|
for (Pointer p : _ptrs)
|
2022-04-30 23:36:17 +02:00
|
|
|
{
|
|
|
|
if (!(skip_latched && p.pointerId == -1 && (p.flags & KeyValue.FLAG_LOCKED) == 0))
|
|
|
|
flags |= p.flags;
|
|
|
|
}
|
2022-02-20 13:09:39 +01:00
|
|
|
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)
|
|
|
|
{
|
2022-05-01 00:11:52 +02:00
|
|
|
// Comparing names because the keys might have been modified.
|
|
|
|
// Physical equality works because names are never computed or shared.
|
2022-03-19 15:39:20 +01:00
|
|
|
String name = kv.name;
|
2022-02-20 13:09:39 +01:00
|
|
|
for (Pointer p : _ptrs)
|
2022-03-19 15:39:20 +01:00
|
|
|
if (p.value != null && p.value.name == name)
|
2022-02-20 13:09:39 +01:00
|
|
|
return p.flags;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Receiving events
|
|
|
|
|
|
|
|
public void onTouchUp(int pointerId)
|
|
|
|
{
|
|
|
|
Pointer ptr = getPtr(pointerId);
|
|
|
|
if (ptr == null)
|
|
|
|
return;
|
|
|
|
stopKeyRepeat(ptr);
|
2022-03-19 15:39:20 +01:00
|
|
|
Pointer latched = getLatched(ptr);
|
2022-02-20 13:09:39 +01:00
|
|
|
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);
|
2022-04-30 23:17:20 +02:00
|
|
|
_handler.onPointerUp(ptr.value, ptr.modifier_flags);
|
2022-02-20 13:09:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ((ptr.flags & KeyValue.FLAG_LATCH) != 0)
|
|
|
|
{
|
|
|
|
ptr.flags &= ~KeyValue.FLAG_LATCH;
|
|
|
|
ptr.pointerId = -1; // Latch
|
|
|
|
_handler.onPointerFlagsChanged();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
clearLatched();
|
|
|
|
removePtr(ptr);
|
2022-04-30 23:17:20 +02:00
|
|
|
_handler.onPointerUp(ptr.value, ptr.modifier_flags);
|
2022-02-20 13:09:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-15 20:44:02 +01:00
|
|
|
public void onTouchCancel(int pointerId)
|
|
|
|
{
|
|
|
|
Pointer ptr = getPtr(pointerId);
|
|
|
|
if (ptr == null)
|
|
|
|
return;
|
|
|
|
stopKeyRepeat(ptr);
|
|
|
|
removePtr(ptr);
|
|
|
|
_handler.onPointerFlagsChanged();
|
|
|
|
}
|
|
|
|
|
2022-04-30 23:36:17 +02:00
|
|
|
/* Whether an other pointer is down on a non-special key. */
|
|
|
|
private boolean isOtherPointerDown()
|
|
|
|
{
|
|
|
|
for (Pointer p : _ptrs)
|
|
|
|
if (p.pointerId != -1 && (p.flags & KeyValue.FLAG_SPECIAL) == 0)
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-02-20 13:09:39 +01:00
|
|
|
public void onTouchDown(float x, float y, int pointerId, KeyboardData.Key key)
|
|
|
|
{
|
2022-03-17 11:24:33 +01:00
|
|
|
// Ignore new presses while a modulated key is active. On some devices,
|
|
|
|
// ghost touch events can happen while the pointer travels on top of other
|
|
|
|
// keys.
|
|
|
|
if (isModulatedKeyPressed())
|
|
|
|
return;
|
2022-04-30 23:36:17 +02:00
|
|
|
int mflags = getFlags(isOtherPointerDown());
|
2022-04-30 23:17:20 +02:00
|
|
|
KeyValue value = _handler.onPointerDown(key.key0, mflags);
|
2022-05-07 00:08:20 +02:00
|
|
|
Pointer ptr = new Pointer(pointerId, key, key.key0, value, x, y, mflags);
|
2022-02-20 13:09:39 +01:00
|
|
|
_ptrs.add(ptr);
|
2022-04-30 23:36:17 +02:00
|
|
|
if (value != null && (value.flags & KeyValue.FLAG_SPECIAL) == 0)
|
2022-02-20 13:09:39 +01:00
|
|
|
startKeyRepeat(ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void onTouchMove(float x, float y, int pointerId)
|
|
|
|
{
|
|
|
|
Pointer ptr = getPtr(pointerId);
|
|
|
|
if (ptr == null)
|
|
|
|
return;
|
2022-05-06 18:38:43 +02:00
|
|
|
|
|
|
|
// The position in a IME windows is clampled to view.
|
|
|
|
// For a better up swipe behaviour, set the y position to a negative value when clamped.
|
|
|
|
if (y == 0.0) y = -400;
|
|
|
|
|
2022-02-20 13:09:39 +01:00
|
|
|
float dx = x - ptr.downX;
|
|
|
|
float dy = y - ptr.downY;
|
|
|
|
float dist = Math.abs(dx) + Math.abs(dy);
|
|
|
|
ptr.ptrDist = dist;
|
2022-05-07 00:08:20 +02:00
|
|
|
|
|
|
|
int direction;
|
2022-02-20 13:09:39 +01:00
|
|
|
if (dist < _config.swipe_dist_px)
|
|
|
|
{
|
2022-05-07 00:08:20 +02:00
|
|
|
direction = 0;
|
2022-02-20 13:09:39 +01:00
|
|
|
}
|
2022-05-07 00:08:20 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// One of the 8 directions:
|
|
|
|
// |\2|3/|
|
|
|
|
// |1\|/4|
|
|
|
|
// |-----|
|
2022-05-08 01:18:53 +02:00
|
|
|
// |8/|\5|
|
|
|
|
// |/7|6\|
|
2022-05-07 00:08:20 +02:00
|
|
|
direction = 1;
|
|
|
|
if (dx > 0) direction += 2;
|
|
|
|
if (dx > Math.abs(dy) || (dx < 0 && dx > -Math.abs(dy))) direction += 1;
|
2022-05-08 01:18:53 +02:00
|
|
|
if (dy > 0) direction = 9 - direction;
|
2022-02-20 13:09:39 +01:00
|
|
|
}
|
2022-05-07 00:08:20 +02:00
|
|
|
|
|
|
|
KeyValue newSelectedValue = ptr.key.getAtDirection(direction);
|
|
|
|
if (newSelectedValue != ptr.selected_value)
|
2022-02-20 13:09:39 +01:00
|
|
|
{
|
2022-05-07 00:08:20 +02:00
|
|
|
ptr.selected_value = newSelectedValue;
|
|
|
|
// apply modifier flags and trigger vibration.
|
2022-04-30 23:17:20 +02:00
|
|
|
KeyValue newValue =
|
2022-05-07 00:08:20 +02:00
|
|
|
_handler.onPointerSwipe(ptr.selected_value, ptr.modifier_flags);
|
2022-03-19 15:39:20 +01:00
|
|
|
if (newValue != null)
|
2022-02-21 00:24:57 +01:00
|
|
|
{
|
2022-03-19 15:39:20 +01:00
|
|
|
int old_flags = ptr.flags;
|
|
|
|
ptr.value = newValue;
|
|
|
|
ptr.flags = newValue.flags;
|
2022-02-21 00:24:57 +01:00
|
|
|
// Keep the keyrepeat going between modulated keys.
|
2022-03-19 15:39:20 +01:00
|
|
|
if ((old_flags & newValue.flags & KeyValue.FLAG_PRECISE_REPEAT) == 0)
|
|
|
|
{
|
|
|
|
stopKeyRepeat(ptr);
|
2022-04-30 23:36:17 +02:00
|
|
|
if ((newValue.flags & KeyValue.FLAG_SPECIAL) == 0)
|
2022-03-19 15:39:20 +01:00
|
|
|
startKeyRepeat(ptr);
|
|
|
|
}
|
2022-02-21 00:24:57 +01:00
|
|
|
}
|
2022-02-20 13:09:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
2022-03-19 15:39:20 +01:00
|
|
|
private Pointer getLatched(Pointer target)
|
2022-02-20 13:09:39 +01:00
|
|
|
{
|
2022-03-19 15:39:20 +01:00
|
|
|
KeyboardData.Key k = target.key;
|
2022-05-01 00:11:52 +02:00
|
|
|
KeyValue v = target.value;
|
|
|
|
if (v == null)
|
|
|
|
return null;
|
2022-02-20 13:09:39 +01:00
|
|
|
for (Pointer p : _ptrs)
|
2022-05-01 00:11:52 +02:00
|
|
|
if (p.key == k && p.pointerId == -1 && p.value != null && p.value.name == v.name)
|
2022-02-20 13:09:39 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-17 11:24:33 +01:00
|
|
|
private boolean isModulatedKeyPressed()
|
|
|
|
{
|
|
|
|
for (Pointer ptr : _ptrs)
|
|
|
|
{
|
|
|
|
if ((ptr.flags & KeyValue.FLAG_PRECISE_REPEAT) != 0)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-02-20 13:09:39 +01:00
|
|
|
// 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)
|
2022-02-22 19:46:41 +01:00
|
|
|
{
|
|
|
|
// Slower repeat for modulated keys
|
|
|
|
nextInterval *= 2;
|
|
|
|
// Modulate repeat interval depending on the distance of the pointer
|
2022-02-21 00:24:57 +01:00
|
|
|
nextInterval = (long)((float)nextInterval / modulatePreciseRepeat(ptr));
|
2022-02-22 19:46:41 +01:00
|
|
|
}
|
2022-02-20 13:09:39 +01:00
|
|
|
_keyrepeat_handler.sendEmptyMessageDelayed(msg.what, nextInterval);
|
2022-04-30 23:17:20 +02:00
|
|
|
_handler.onPointerHold(ptr.value, ptr.modifier_flags);
|
2022-02-20 13:09:39 +01:00
|
|
|
return (true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (false);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static int uniqueTimeoutWhat = 0;
|
|
|
|
|
|
|
|
private void startKeyRepeat(Pointer ptr)
|
|
|
|
{
|
|
|
|
int what = (uniqueTimeoutWhat++);
|
|
|
|
ptr.timeoutWhat = what;
|
2022-02-22 19:46:41 +01:00
|
|
|
long timeout = _config.longPressTimeout;
|
|
|
|
// Faster repeat timeout for modulated keys
|
|
|
|
if ((ptr.flags & KeyValue.FLAG_PRECISE_REPEAT) != 0)
|
|
|
|
timeout /= 2;
|
|
|
|
_keyrepeat_handler.sendEmptyMessageDelayed(what, timeout);
|
2022-02-20 13:09:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private void stopKeyRepeat(Pointer ptr)
|
|
|
|
{
|
|
|
|
if (ptr.timeoutWhat != -1)
|
|
|
|
{
|
|
|
|
_keyrepeat_handler.removeMessages(ptr.timeoutWhat);
|
|
|
|
ptr.timeoutWhat = -1;
|
2022-02-21 00:24:57 +01:00
|
|
|
ptr.repeatingPtrDist = -1.f;
|
2022-02-20 13:09:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-21 00:24:57 +01:00
|
|
|
private float modulatePreciseRepeat(Pointer ptr)
|
|
|
|
{
|
|
|
|
if (ptr.repeatingPtrDist < 0.f)
|
|
|
|
ptr.repeatingPtrDist = ptr.ptrDist; // First repeat
|
|
|
|
if (ptr.ptrDist > ptr.repeatingPtrDist * 2.f)
|
|
|
|
ptr.repeatingPtrDist = ptr.ptrDist / 2.f; // Large swipe, move the middle point
|
|
|
|
float left = ptr.repeatingPtrDist / 2.f;
|
|
|
|
float accel = (ptr.ptrDist - left) / (ptr.repeatingPtrDist - left);
|
2022-02-22 19:46:41 +01:00
|
|
|
return Math.min(8.f, Math.max(0.1f, accel));
|
2022-02-21 00:24:57 +01:00
|
|
|
}
|
|
|
|
|
2022-02-20 13:09:39 +01:00
|
|
|
private final class Pointer
|
|
|
|
{
|
|
|
|
/** -1 when latched. */
|
|
|
|
public int pointerId;
|
2022-05-07 00:08:20 +02:00
|
|
|
/** The Key pressed by this Pointer */
|
2022-03-19 15:39:20 +01:00
|
|
|
public final KeyboardData.Key key;
|
2022-05-07 00:08:20 +02:00
|
|
|
/** The current seletected KeyValue in key (any one of key0 to key4). */
|
|
|
|
public KeyValue selected_value;
|
|
|
|
/** selected_value with modifier_flags applied. */
|
2022-02-20 13:09:39 +01:00
|
|
|
public KeyValue value;
|
|
|
|
public float downX;
|
|
|
|
public float downY;
|
|
|
|
/** Distance of the pointer to the initial press. */
|
|
|
|
public float ptrDist;
|
2022-04-30 23:17:20 +02:00
|
|
|
/** Modifier flags at the time the key was pressed. */
|
|
|
|
public int modifier_flags;
|
|
|
|
/** Flags of the value. Latch, lock and locked flags are updated. */
|
2022-02-20 13:09:39 +01:00
|
|
|
public int flags;
|
|
|
|
/** Identify timeout messages. */
|
|
|
|
public int timeoutWhat;
|
2022-02-21 00:24:57 +01:00
|
|
|
/** ptrDist at the first repeat, -1 otherwise. */
|
|
|
|
public float repeatingPtrDist;
|
2022-02-20 13:09:39 +01:00
|
|
|
|
2022-05-07 00:08:20 +02:00
|
|
|
public Pointer(int p, KeyboardData.Key k, KeyValue sv, KeyValue v, float x, float y, int mflags)
|
2022-02-20 13:09:39 +01:00
|
|
|
{
|
|
|
|
pointerId = p;
|
|
|
|
key = k;
|
2022-05-07 00:08:20 +02:00
|
|
|
selected_value = sv;
|
2022-02-20 13:09:39 +01:00
|
|
|
value = v;
|
|
|
|
downX = x;
|
|
|
|
downY = y;
|
|
|
|
ptrDist = 0.f;
|
2022-04-30 23:17:20 +02:00
|
|
|
modifier_flags = mflags;
|
2022-02-20 13:09:39 +01:00
|
|
|
flags = (v == null) ? 0 : v.flags;
|
|
|
|
timeoutWhat = -1;
|
2022-02-21 00:24:57 +01:00
|
|
|
repeatingPtrDist = -1.f;
|
2022-02-20 13:09:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public interface IPointerEventHandler
|
|
|
|
{
|
2022-03-19 15:39:20 +01:00
|
|
|
/** A key is pressed. Key can be modified or removed by returning [null].
|
|
|
|
[getFlags()] is not uptodate. */
|
2022-04-30 23:17:20 +02:00
|
|
|
public KeyValue onPointerDown(KeyValue k, int flags);
|
2022-03-19 15:39:20 +01:00
|
|
|
|
|
|
|
/** Pointer swipes into a corner. Key can be modified or removed. */
|
2022-04-30 23:17:20 +02:00
|
|
|
public KeyValue onPointerSwipe(KeyValue k, int flags);
|
2022-03-19 15:39:20 +01:00
|
|
|
|
|
|
|
/** Key is released. [k] is the key that was returned by [onPointerDown] or
|
|
|
|
[onPointerSwipe]. */
|
2022-04-30 23:17:20 +02:00
|
|
|
public void onPointerUp(KeyValue k, int flags);
|
2022-03-19 15:39:20 +01:00
|
|
|
|
|
|
|
/** Flags changed because latched or locked keys or cancelled pointers. */
|
2022-02-20 13:09:39 +01:00
|
|
|
public void onPointerFlagsChanged();
|
2022-03-19 15:39:20 +01:00
|
|
|
|
|
|
|
/** Key is repeating. */
|
2022-04-30 23:17:20 +02:00
|
|
|
public void onPointerHold(KeyValue k, int flags);
|
2022-02-20 13:09:39 +01:00
|
|
|
}
|
|
|
|
}
|