Skip to content

Commit 6dde810

Browse files
committed
InputService: add live dispatching of pointer strokes on API >= 26
This makes use of GestureDescription.StrokeDescription.continueStroke() to be able to dispatch parts of a logical stroke to the OS while the stroke is continuing. Credit is due to https://github.com/gm-vm who came up with a first working implementation I used as a basis for this one. Co-authored-by: Gabriele Mazzotta <gabriele.mzt@gmail.com> Closes #69 Closes #285
1 parent be3565d commit 6dde810

File tree

1 file changed

+61
-9
lines changed

1 file changed

+61
-9
lines changed

app/src/main/java/net/christianbeier/droidvnc_ng/InputService.java

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ private static class InputContext {
7070
// pointer-related
7171
boolean isButtonOneDown;
7272
Path path = new Path();
73+
GestureDescription.StrokeDescription stroke;
7374
long lastGestureStartTime;
7475
GestureCallback gestureCallback = new GestureCallback();
7576
InputPointerView pointerView;
@@ -300,18 +301,18 @@ public static void onPointerEvent(int buttonMask, int x, int y, long client) {
300301
// down, was up
301302
if ((buttonMask & (1 << 0)) != 0 && !inputContext.isButtonOneDown) {
302303
inputContext.isButtonOneDown = true;
303-
instance.startGesture(inputContext, x, y);
304+
instance.startStroke(inputContext, x, y);
304305
}
305306

306307
// down, was down
307308
if ((buttonMask & (1 << 0)) != 0 && inputContext.isButtonOneDown) {
308-
instance.continueGesture(inputContext, x, y);
309+
instance.continueStroke(inputContext, x, y);
309310
}
310311

311312
// up, was down
312313
if ((buttonMask & (1 << 0)) == 0 && inputContext.isButtonOneDown) {
313314
inputContext.isButtonOneDown = false;
314-
instance.endGesture(inputContext, x, y);
315+
instance.endStroke(inputContext, x, y);
315316
}
316317

317318

@@ -833,25 +834,76 @@ public static boolean isTakingScreenShots() {
833834
}
834835
}
835836

836-
private void startGesture(InputContext inputContext, int x, int y) {
837+
private void startStroke(InputContext inputContext, int x, int y) {
837838
inputContext.path.reset();
838839
inputContext.path.moveTo( x, y );
839840
inputContext.lastGestureStartTime = SystemClock.elapsedRealtime();
841+
// On API level 26 and newer, we can submit the stroke via multiple gestures, one per
842+
// continued stroke. Reset the stroke here to mark the start of a stroke which will be
843+
// continued in continueStroke() or ended in endStroke()
844+
// On older API levels, the stroke is constructed and submitted at the very end from the whole path.
845+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
846+
inputContext.stroke = null;
847+
}
840848
}
841849

842-
private void continueGesture(InputContext inputContext, int x, int y) {
843-
inputContext.path.lineTo( x, y );
850+
private void continueStroke(InputContext inputContext, int x, int y) {
851+
inputContext.path.lineTo(x, y);
852+
// On API level 26 and newer, we can dispatch the stroke via multiple gestures.
853+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
854+
long currentTime = SystemClock.elapsedRealtime();
855+
long duration = currentTime - inputContext.lastGestureStartTime;
856+
// if passing 0, getting "IllegalArgumentException: Duration must be positive"
857+
if (duration == 0) duration = 1;
858+
859+
// Either create a new stroke if this is the first continueStroke() after startStroke()
860+
// or create a continued stroke.
861+
if (inputContext.stroke == null) {
862+
inputContext.stroke = new GestureDescription.StrokeDescription(inputContext.path, 0, duration, true);
863+
} else {
864+
inputContext.stroke = inputContext.stroke.continueStroke(inputContext.path, 0, duration, true);
865+
}
866+
867+
// and dispatch it to OS
868+
dispatchStrokeAsGesture(inputContext.stroke, inputContext.getDisplayId());
869+
870+
// start a new gesture
871+
inputContext.lastGestureStartTime = currentTime;
872+
873+
// start a new path
874+
inputContext.path.reset();
875+
inputContext.path.moveTo(x, y);
876+
}
844877
}
845878

846-
private void endGesture(InputContext inputContext, int x, int y) {
879+
private void endStroke(InputContext inputContext, int x, int y) {
847880
inputContext.path.lineTo( x, y );
848881
long duration = SystemClock.elapsedRealtime() - inputContext.lastGestureStartTime;
849882
// gesture ended very very shortly after start (< 1ms). make it 1ms to get dispatched to the system
850883
if (duration == 0) duration = 1;
851-
GestureDescription.StrokeDescription stroke = new GestureDescription.StrokeDescription( inputContext.path, 0, duration);
884+
885+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
886+
// On API level 26 and newer, we can submit the stroke via multiple gestures, so end it here.
887+
// Either create a new stroke if this is endStroke comes right after startStroke()
888+
// or create a continued stroke.
889+
if (inputContext.stroke == null) {
890+
inputContext.stroke = new GestureDescription.StrokeDescription(inputContext.path, 0, duration, false);
891+
} else {
892+
inputContext.stroke = inputContext.stroke.continueStroke(inputContext.path, 0, duration, false);
893+
}
894+
} else {
895+
// On older API levels, the stroke is constructed and submitted at the very end from the whole path
896+
inputContext.stroke = new GestureDescription.StrokeDescription(inputContext.path, 0, duration);
897+
}
898+
899+
dispatchStrokeAsGesture(inputContext.stroke, inputContext.getDisplayId());
900+
}
901+
902+
/// Dispatch the given stroke to the OS, display-specific starting at API level 30
903+
private void dispatchStrokeAsGesture(GestureDescription.StrokeDescription stroke, int displayId) {
852904
GestureDescription.Builder builder = new GestureDescription.Builder();
853905
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
854-
builder.setDisplayId(inputContext.getDisplayId());
906+
builder.setDisplayId(displayId);
855907
}
856908
builder.addStroke(stroke);
857909
// Docs says: Any gestures currently in progress, whether from the user, this service, or another service, will be cancelled.

0 commit comments

Comments
 (0)