From f15bfe3038142ac21f3c89b63e6cfdbba510c500 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 15 Oct 2015 01:50:05 -0700 Subject: [PATCH] Add support for mouse drag using long press --- .../limelight/binding/input/TouchContext.java | 114 ++++++++++++++++-- 1 file changed, 103 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/limelight/binding/input/TouchContext.java b/app/src/main/java/com/limelight/binding/input/TouchContext.java index 129ed5b3..409014bf 100644 --- a/app/src/main/java/com/limelight/binding/input/TouchContext.java +++ b/app/src/main/java/com/limelight/binding/input/TouchContext.java @@ -3,6 +3,9 @@ package com.limelight.binding.input; import com.limelight.nvstream.NvConnection; import com.limelight.nvstream.input.MouseButtonPacket; +import java.util.Timer; +import java.util.TimerTask; + public class TouchContext { private int lastTouchX = 0; private int lastTouchY = 0; @@ -10,14 +13,20 @@ public class TouchContext { private int originalTouchY = 0; private long originalTouchTime = 0; private boolean cancelled; + private boolean confirmedMove; + private boolean confirmedDrag; + private Timer dragTimer; + private double distanceMoved; private final NvConnection conn; private final int actionIndex; private final double xFactor; private final double yFactor; - private static final int TAP_MOVEMENT_THRESHOLD = 10; + private static final int TAP_MOVEMENT_THRESHOLD = 20; + private static final int TAP_DISTANCE_THRESHOLD = 25; private static final int TAP_TIME_THRESHOLD = 250; + private static final int DRAG_TIME_THRESHOLD = 650; public TouchContext(NvConnection conn, int actionIndex, double xFactor, double yFactor) { @@ -32,15 +41,19 @@ public class TouchContext { return actionIndex; } + private boolean isWithinTapBounds(int touchX, int touchY) + { + int xDelta = Math.abs(touchX - originalTouchX); + int yDelta = Math.abs(touchY - originalTouchY); + return xDelta <= TAP_MOVEMENT_THRESHOLD && + yDelta <= TAP_MOVEMENT_THRESHOLD; + } + private boolean isTap() { - int xDelta = Math.abs(lastTouchX - originalTouchX); - int yDelta = Math.abs(lastTouchY - originalTouchY); long timeDelta = System.currentTimeMillis() - originalTouchTime; - return xDelta <= TAP_MOVEMENT_THRESHOLD && - yDelta <= TAP_MOVEMENT_THRESHOLD && - timeDelta <= TAP_TIME_THRESHOLD; + return isWithinTapBounds(lastTouchX, lastTouchY) && timeDelta <= TAP_TIME_THRESHOLD; } private byte getMouseButtonIndex() @@ -58,7 +71,13 @@ public class TouchContext { originalTouchX = lastTouchX = eventX; originalTouchY = lastTouchY = eventY; originalTouchTime = System.currentTimeMillis(); - cancelled = false; + cancelled = confirmedDrag = confirmedMove = false; + distanceMoved = 0; + + if (actionIndex == 0) { + // Start the timer for engaging a drag + startDragTimer(); + } return true; } @@ -69,10 +88,17 @@ public class TouchContext { return; } - if (isTap()) - { - byte buttonIndex = getMouseButtonIndex(); + // Cancel the drag timer + cancelDragTimer(); + byte buttonIndex = getMouseButtonIndex(); + + if (confirmedDrag) { + // Raise the button after a drag + conn.sendMouseButtonUp(buttonIndex); + } + else if (isTap()) + { // Lower the mouse button conn.sendMouseButtonDown(buttonIndex); @@ -87,12 +113,70 @@ public class TouchContext { } } + private synchronized void startDragTimer() { + dragTimer = new Timer(true); + dragTimer.schedule(new TimerTask() { + @Override + public void run() { + synchronized (TouchContext.this) { + // Check if someone already set move + if (confirmedMove) { + return; + } + + // Check if someone cancelled us + if (dragTimer == null) { + return; + } + + // Uncancellable now + dragTimer = null; + + // We haven't been cancelled before the timer expired so begin dragging + confirmedDrag = true; + conn.sendMouseButtonDown(getMouseButtonIndex()); + } + } + }, DRAG_TIME_THRESHOLD); + } + + private synchronized void cancelDragTimer() { + if (dragTimer != null) { + dragTimer.cancel(); + dragTimer = null; + } + } + + private synchronized void checkForConfirmedMove(int eventX, int eventY) { + // If we've already confirmed something, get out now + if (confirmedMove || confirmedDrag) { + return; + } + + // If it leaves the tap bounds before the drag time expires, it's a move. + if (!isWithinTapBounds(eventX, eventY)) { + confirmedMove = true; + cancelDragTimer(); + return; + } + + // Check if we've exceeded the maximum distance moved + distanceMoved += Math.sqrt(Math.pow(eventX - lastTouchX, 2) + Math.pow(eventY - lastTouchY, 2)); + if (distanceMoved >= TAP_DISTANCE_THRESHOLD) { + confirmedMove = true; + cancelDragTimer(); + return; + } + } + public boolean touchMoveEvent(int eventX, int eventY) { if (eventX != lastTouchX || eventY != lastTouchY) { - // We only send moves for the primary touch point + // We only send moves and drags for the primary touch point if (actionIndex == 0) { + checkForConfirmedMove(eventX, eventY); + int deltaX = eventX - lastTouchX; int deltaY = eventY - lastTouchY; @@ -131,6 +215,14 @@ public class TouchContext { public void cancelTouch() { cancelled = true; + + // Cancel the drag timer + cancelDragTimer(); + + // If it was a confirmed drag, we'll need to raise the button now + if (confirmedDrag) { + conn.sendMouseButtonUp(getMouseButtonIndex()); + } } public boolean isCancelled() {