@@ -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