diff --git a/.gitmodules b/.gitmodules index 05b912d..78b7d10 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,4 +3,4 @@ url = https://github.com/Julow/cdict [submodule "vendor/libswipetype"] path = vendor/libswipetype - url = https://github.com/libswipetype/libswipetype + url = http://10.0.1.49:3010/harley/libswipetype.git diff --git a/res/raw/en_us_sample.glide b/res/raw/en_us_sample.glide index df60856..c9a0569 100644 Binary files a/res/raw/en_us_sample.glide and b/res/raw/en_us_sample.glide differ diff --git a/srcs/juloo.keyboard2/Config.java b/srcs/juloo.keyboard2/Config.java index 65193a1..7241e20 100644 --- a/srcs/juloo.keyboard2/Config.java +++ b/srcs/juloo.keyboard2/Config.java @@ -151,7 +151,7 @@ public final class Config // The option value uses an unnamed scale where the baseline is around 25. float dpi_ratio = Math.max(dm.xdpi, dm.ydpi) / Math.min(dm.xdpi, dm.ydpi); float swipe_scaling = Math.min(dm.widthPixels, dm.heightPixels) / 10.f * dpi_ratio; - float swipe_dist_value = Float.valueOf(_prefs.getString("swipe_dist", "15")); + float swipe_dist_value = Float.valueOf(_prefs.getString("swipe_dist", "25")); swipe_dist_px = swipe_dist_value / 25.f * swipe_scaling; float slider_sensitivity = Float.valueOf(_prefs.getString("slider_sensitivity", "30")) / 100.f; slide_step_px = slider_sensitivity * swipe_scaling; diff --git a/srcs/juloo.keyboard2/Keyboard2.java b/srcs/juloo.keyboard2/Keyboard2.java index 6107b8a..f043a3b 100644 --- a/srcs/juloo.keyboard2/Keyboard2.java +++ b/srcs/juloo.keyboard2/Keyboard2.java @@ -134,7 +134,7 @@ public class Keyboard2 extends InputMethodService Config.initGlobalConfig(prefs, getResources(), _foldStateTracker.isUnfolded(), _dictionaries); _config = Config.globalConfig(); - _swipeAdapter = new UnexpectedKeyboardSwipeAdapter(this, null, _config); + _swipeAdapter = new UnexpectedKeyboardSwipeAdapter(this); _swipeAdapter.init(new UnexpectedKeyboardSwipeAdapter.CandidatesCallback() { public void onSwipeCandidates(String[] words, float[] scores) { if (words.length > 0) { diff --git a/srcs/juloo.keyboard2/Keyboard2View.java b/srcs/juloo.keyboard2/Keyboard2View.java index 3822fa8..1b09b25 100644 --- a/srcs/juloo.keyboard2/Keyboard2View.java +++ b/srcs/juloo.keyboard2/Keyboard2View.java @@ -17,9 +17,12 @@ import android.view.Window; import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowMetrics; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import dev.dettmer.swipetype.android.KeyboardLayoutDescriptor; + public class Keyboard2View extends View implements View.OnTouchListener, Pointers.IPointerEventHandler { @@ -39,6 +42,9 @@ public class Keyboard2View extends View private int[] _glideKeys = new int[32]; private int _glideKeyCount = 0; private boolean _inGlide = false; + /** Trail points for the swipe gesture (pixels). */ + private List _trailPoints = new ArrayList<>(); + private Paint _trailPaint; private Pointers.Modifiers _mods; @@ -74,6 +80,13 @@ public class Keyboard2View extends View _theme = new Theme(getContext(), attrs); _config = Config.globalConfig(); _pointers = new Pointers(this, _config); + _trailPaint = new Paint(); + _trailPaint.setColor(0x8044AAFF); + _trailPaint.setStyle(Paint.Style.STROKE); + _trailPaint.setStrokeWidth(6f); + _trailPaint.setStrokeCap(Paint.Cap.ROUND); + _trailPaint.setStrokeJoin(Paint.Join.ROUND); + _trailPaint.setAntiAlias(true); refresh_navigation_bar(context); setOnTouchListener(this); int layout_id = (attrs == null) ? 0 : @@ -218,12 +231,17 @@ public class Keyboard2View extends View _pointers.onTouchDown(tx, ty, event.getPointerId(p), key); // Reset glide tracking _glideKeyCount = 0; + _glideKeys[0] = -1; _inGlide = false; _glideLastKey = -1; + _trailPoints.clear(); if (_swipeAdapter != null) _swipeAdapter.cancelGesture(); if (key != null && key.keys[0] != null && key.keys[0].getKind() == KeyValue.Kind.Char) + { _glideLastKey = key.keys[0].getChar(); + _trailPoints.add(new float[]{tx, ty}); + } break; case MotionEvent.ACTION_MOVE: for (p = 0; p < event.getPointerCount(); p++) @@ -231,33 +249,44 @@ public class Keyboard2View extends View float mx = event.getX(p); float my = event.getY(p); _pointers.onTouchMove(mx, my, event.getPointerId(p)); - // Track key under finger for glide detection - if (_swipeAdapter != null && !_inGlide) + // Collect trail + detect glide in one pass + if (_swipeAdapter != null) { KeyboardData.Key k = getKeyAtPosition(mx, my); if (k != null && k.keys[0] != null && k.keys[0].getKind() == KeyValue.Kind.Char) { - int cp = k.keys[0].getChar(); - if (cp != _glideLastKey) + _trailPoints.add(new float[]{mx, my}); + invalidate(); + if (!_inGlide) { - _glideLastKey = cp; - boolean seen = false; - for (int i = 0; i < _glideKeyCount; i++) - if (_glideKeys[i] == cp) { seen = true; break; } - if (!seen && _glideKeyCount < 32) - _glideKeys[_glideKeyCount++] = cp; - if (_glideKeyCount >= 3) + int cp = k.keys[0].getChar(); + if (cp != _glideLastKey) { - _inGlide = true; - _swipeAdapter.startGesture(); + _glideLastKey = cp; + boolean seen = false; + for (int i = 0; i < _glideKeyCount; i++) + if (_glideKeys[i] == cp) { seen = true; break; } + if (!seen && _glideKeyCount < 32) + _glideKeys[_glideKeyCount++] = cp; + if (_glideKeyCount >= 2) + { + _inGlide = true; + _swipeAdapter.startGesture(); + _pointers.cancelPointer(event.getPointerId(p)); + } } } } } - // Feed points during glide + // Feed points during glide (convert to dp for the engine) if (_inGlide && _swipeAdapter != null) - _swipeAdapter.addPoint(mx, my, System.currentTimeMillis()); + { + float density = getResources().getDisplayMetrics().density; + _swipeAdapter.addPoint(mx / density, my / density, System.currentTimeMillis()); + _trailPoints.add(new float[]{mx, my}); + invalidate(); // trigger redraw to show trail + } } break; case MotionEvent.ACTION_UP: @@ -265,10 +294,14 @@ public class Keyboard2View extends View if (_inGlide && _swipeAdapter != null) _swipeAdapter.finishGesture(); _inGlide = false; + _trailPoints.clear(); + invalidate(); _pointers.onTouchUp(event.getPointerId(event.getActionIndex())); break; case MotionEvent.ACTION_CANCEL: _inGlide = false; + _trailPoints.clear(); + invalidate(); _pointers.onTouchCancel(); break; default: @@ -339,6 +372,37 @@ public class Keyboard2View extends View (int)(_tc.row_height * _keyboard.keysHeight + _config.marginTop + _marginBottom); setMeasuredDimension(width, height); + // Pass real key positions to the swipe adapter + if (_swipeAdapter != null && _keyboard != null) + { + float density = getResources().getDisplayMetrics().density; + List keys = new ArrayList<>(); + float yy = _tc.margin_top / density; + for (KeyboardData.Row row : _keyboard.rows) + { + yy += row.shift * _tc.row_height / density; + float xx = _marginLeft / density; + float keyH = (row.height * _tc.row_height - _tc.vertical_margin) / density; + for (KeyboardData.Key k : row.keys) + { + xx += k.shift * _keyWidth / density; + float keyW = (_keyWidth * k.width - _tc.horizontal_margin) / density; + if (k.keys != null && k.keys.length > 0 && k.keys[0] != null + && k.keys[0].getKind() == KeyValue.Kind.Char) + { + char c = k.keys[0].getChar(); + keys.add(new KeyboardLayoutDescriptor.KeyInfo( + String.valueOf(c), (int)c, + xx + keyW/2, yy + keyH/2, keyW, keyH)); + } + xx += (k.width * _keyWidth) / density; + } + yy += row.height * _tc.row_height / density; + } + _swipeAdapter.setKeyLayout(keys, + (_keyboard.keysWidth * _keyWidth + _marginLeft + _tc.margin_left) / density, + yy); + } } Rect _cached_exclusion_rect = new Rect(); @@ -430,6 +494,15 @@ public class Keyboard2View extends View } y += row.height * _tc.row_height; } + // Draw swipe gesture trail + if (_trailPoints != null && _trailPoints.size() >= 2) + { + android.graphics.Path trail = new android.graphics.Path(); + trail.moveTo(_trailPoints.get(0)[0], _trailPoints.get(0)[1]); + for (int i = 1; i < _trailPoints.size(); i++) + trail.lineTo(_trailPoints.get(i)[0], _trailPoints.get(i)[1]); + canvas.drawPath(trail, _trailPaint); + } } @Override diff --git a/srcs/juloo.keyboard2/Pointers.java b/srcs/juloo.keyboard2/Pointers.java index 40f9880..869bf8a 100644 --- a/srcs/juloo.keyboard2/Pointers.java +++ b/srcs/juloo.keyboard2/Pointers.java @@ -195,6 +195,22 @@ public final class Pointers implements Handler.Callback _handler.onPointerFlagsChanged(true); } + /** Cancel a single pointer by its ID — stops long-press and removes it. */ + public void cancelPointer(int pointerId) + { + for (int i = _ptrs.size() - 1; i >= 0; i--) + { + Pointer ptr = _ptrs.get(i); + if (ptr.pointerId == pointerId && !ptr.hasFlagsAny(FLAG_P_LATCHED)) + { + _longpress_handler.removeMessages(ptr.timeoutWhat); + _ptrs.remove(i); + break; + } + } + _handler.onPointerFlagsChanged(false); + } + /* Whether an other pointer is down on a non-special key. */ private boolean isOtherPointerDown() { diff --git a/srcs/juloo.keyboard2/UnexpectedKeyboardSwipeAdapter.java b/srcs/juloo.keyboard2/UnexpectedKeyboardSwipeAdapter.java index cb9913b..8a15a14 100644 --- a/srcs/juloo.keyboard2/UnexpectedKeyboardSwipeAdapter.java +++ b/srcs/juloo.keyboard2/UnexpectedKeyboardSwipeAdapter.java @@ -14,41 +14,42 @@ import dev.dettmer.swipetype.android.SwipeTypeCandidate; import dev.dettmer.swipetype.android.SwipeTypeEngine; import dev.dettmer.swipetype.android.SwipeTypeError; -/** - * Adapter connecting Unexpected Keyboard to the libswipetype gesture engine. - * - * Collects touch points during a glide gesture, sends them to SwipeTypeEngine - * on lift, and delivers candidates back to the keyboard for display. - */ public class UnexpectedKeyboardSwipeAdapter implements SwipeTypeAdapter { private static final String TAG = "SwipeAdapter"; private SwipeTypeEngine _engine; private Context _context; - private KeyboardData _keyboardData; - private Config _config; - /** Collected touch points for the current gesture. */ private List _gesturePoints = new ArrayList<>(); private boolean _gestureActive = false; - - /** Callback to deliver candidates to. */ private CandidatesCallback _candidatesCallback; + /** Key positions (dp) set by the view after layout. */ + private List _cachedKeys = null; + private float _layoutWidthDp = 0; + private float _layoutHeightDp = 0; + public interface CandidatesCallback { void onSwipeCandidates(String[] words, float[] scores); } - public UnexpectedKeyboardSwipeAdapter(Context context, KeyboardData kbd, Config config) + public UnexpectedKeyboardSwipeAdapter(Context context) { _context = context; - _keyboardData = kbd; - _config = config; } - /** Initialize the engine and load the dictionary. Call once on startup. */ + /** Called by the view after layout with all key positions in dp. */ + public void setKeyLayout(List keys, float widthDp, float heightDp) + { + _cachedKeys = keys; + _layoutWidthDp = widthDp; + _layoutHeightDp = heightDp; + if (_engine != null && _engine.isInitialized()) + _engine.notifyLayoutChanged(); + } + public void init(CandidatesCallback callback) { _candidatesCallback = callback; @@ -61,78 +62,58 @@ public class UnexpectedKeyboardSwipeAdapter implements SwipeTypeAdapter { int resId = _context.getResources().getIdentifier( "en_us_sample", "raw", _context.getPackageName()); - if (resId == 0) - { - Log.w(TAG, "Dictionary resource not found"); - return; - } + if (resId == 0) { Log.w(TAG, "Dictionary resource not found"); return; } try (InputStream is = _context.getResources().openRawResource(resId)) { boolean ok = _engine.loadDictionary("en-US", is); Log.i(TAG, "loadDictionary: " + ok); } - catch (Exception e) - { - Log.e(TAG, "Failed to load dictionary: " + e.getMessage()); - } + catch (Exception e) { Log.e(TAG, "Failed: " + e.getMessage()); } } - /** Start a new gesture. Call on initial swipe motion. */ public void startGesture() { _gesturePoints.clear(); _gestureActive = true; } - /** Add a touch point. */ public void addPoint(float x, float y, long timestamp) { - if (_gestureActive) - _gesturePoints.add(new GesturePoint(x, y, timestamp)); + if (_gestureActive) _gesturePoints.add(new GesturePoint(x, y, timestamp)); } - /** Finish the gesture and run recognition. */ public void finishGesture() { _gestureActive = false; - if (_gesturePoints.size() < 3) return; + if (_gesturePoints.size() < 3) { _gesturePoints.clear(); return; } _engine.processGesture(_gesturePoints); _gesturePoints.clear(); } - /** Cancel the current gesture. */ public void cancelGesture() { _gestureActive = false; _gesturePoints.clear(); } - public boolean isGestureActive() { return _gestureActive; } - - // ===== SwipeTypeAdapter implementation ===== + // ===== SwipeTypeAdapter ===== @Override - public void onInit(SwipeTypeEngine engine) - { - Log.i(TAG, "Engine initialized"); - } + public void onInit(SwipeTypeEngine engine) { Log.i(TAG, "Engine initialized"); } @Override public KeyboardLayoutDescriptor getKeyboardLayout() { - // Stub: returns a minimal layout for testing - List keys = new ArrayList<>(); - float density = _context.getResources().getDisplayMetrics().density; - // QWERTY row positions (approximate centers in dp at 24dp key width) + if (_cachedKeys != null && !_cachedKeys.isEmpty()) + return new KeyboardLayoutDescriptor("en-US", _cachedKeys, + _layoutWidthDp, _layoutHeightDp); + // Fallback — should not be reached once the view reports layout + List stub = new ArrayList<>(); + float d = _context.getResources().getDisplayMetrics().density; String[] rows = {"qwertyuiop", "asdfghjkl", "zxcvbnm"}; float y = 0; - for (String row : rows) - { + for (String r : rows) { float x = 0; - for (int i = 0; i < row.length(); i++) - { - char c = row.charAt(i); - keys.add(new KeyInfo(String.valueOf(c), (int)c, - x + 12f / density, y + 12f / density, - 24f / density, 24f / density)); - x += 24f / density; + for (int i = 0; i < r.length(); i++) { + char c = r.charAt(i); + stub.add(new KeyInfo(String.valueOf(c), (int)c, x+12f/d, y+12f/d, 24f/d, 24f/d)); + x += 24f/d; } - y += 24f / density; + y += 24f/d; } - return new KeyboardLayoutDescriptor("en-US", keys, - rows[0].length() * 24f / density, rows.length * 24f / density); + return new KeyboardLayoutDescriptor("en-US", stub, 240f/d, 72f/d); } @Override @@ -150,10 +131,7 @@ public class UnexpectedKeyboardSwipeAdapter implements SwipeTypeAdapter } @Override - public void onError(SwipeTypeError error) - { - Log.e(TAG, "Error: " + error.name()); - } + public void onError(SwipeTypeError error) { Log.e(TAG, "Error: " + error.name()); } public void shutdown() { diff --git a/vendor/libswipetype b/vendor/libswipetype index b4d2f26..e4aef2f 160000 --- a/vendor/libswipetype +++ b/vendor/libswipetype @@ -1 +1 @@ -Subproject commit b4d2f26319b3aba6f184c9a9fae0af8d2dcbeeae +Subproject commit e4aef2faaa661097939b181f9ac734c8acfca779