@@ -18,6 +18,9 @@ public final class MultiTouchInstrumentation extends Instrumentation {
1818 private static final int MAX_RADIUS = 1200 ;
1919 private static final int MIN_DURATION_MS = 16 ;
2020 private static final int MAX_DURATION_MS = 10_000 ;
21+ private static final int MOVE_FRAME_INTERVAL_MS = 32 ;
22+ private static final int MAX_MOVE_FRAMES = 60 ;
23+ private static final boolean WAIT_FOR_INPUT_DISPATCH = false ;
2124 private Bundle arguments ;
2225
2326 @ Override
@@ -91,57 +94,83 @@ private int injectGesture(GestureSpec spec) {
9194 long eventTime = downTime ;
9295 PointerPair start = pointerPairAt (spec , 0 );
9396 PointerPair end = pointerPairAt (spec , 1 );
97+ PointerPair activePointers = start .firstOnly ();
9498 int count = 0 ;
9599
96- inject (
97- automation ,
98- motionEvent (downTime , eventTime , MotionEvent .ACTION_DOWN , start .firstOnly ()));
99- count += 1 ;
100- eventTime += 8 ;
101- inject (
102- automation ,
103- motionEvent (
104- downTime ,
105- eventTime ,
106- MotionEvent .ACTION_POINTER_DOWN | (1 << MotionEvent .ACTION_POINTER_INDEX_SHIFT ),
107- start ));
108- count += 1 ;
100+ try {
101+ inject (
102+ automation ,
103+ motionEvent (downTime , eventTime , MotionEvent .ACTION_DOWN , activePointers ));
104+ count += 1 ;
105+ eventTime += 8 ;
106+ inject (
107+ automation ,
108+ motionEvent (
109+ downTime ,
110+ eventTime ,
111+ MotionEvent .ACTION_POINTER_DOWN | (1 << MotionEvent .ACTION_POINTER_INDEX_SHIFT ),
112+ start ));
113+ count += 1 ;
114+ activePointers = start ;
115+
116+ int frameCount =
117+ Math .max (
118+ 3 ,
119+ Math .min (
120+ MAX_MOVE_FRAMES ,
121+ Math .round (spec .durationMs / (float ) MOVE_FRAME_INTERVAL_MS )));
122+ for (int index = 1 ; index < frameCount ; index += 1 ) {
123+ double t = (double ) index / (double ) frameCount ;
124+ PointerPair frame = pointerPairAt (spec , t );
125+ eventTime = downTime + Math .round (spec .durationMs * t );
126+ inject (automation , motionEvent (downTime , eventTime , MotionEvent .ACTION_MOVE , frame ));
127+ count += 1 ;
128+ activePointers = frame ;
129+ }
109130
110- int frameCount = Math .max (3 , Math .round (spec .durationMs / 16.0f ));
111- for (int index = 1 ; index < frameCount ; index += 1 ) {
112- double t = (double ) index / (double ) frameCount ;
113- PointerPair frame = pointerPairAt (spec , t );
114- eventTime = downTime + Math .round (spec .durationMs * t );
115- inject (automation , motionEvent (downTime , eventTime , MotionEvent .ACTION_MOVE , frame ));
131+ eventTime = downTime + spec .durationMs ;
132+ inject (
133+ automation ,
134+ motionEvent (
135+ downTime ,
136+ eventTime ,
137+ MotionEvent .ACTION_POINTER_UP | (1 << MotionEvent .ACTION_POINTER_INDEX_SHIFT ),
138+ end ));
139+ count += 1 ;
140+ activePointers = end .firstOnly ();
141+ inject (
142+ automation ,
143+ motionEvent (downTime , eventTime + 8 , MotionEvent .ACTION_UP , activePointers ));
116144 count += 1 ;
145+ SystemClock .sleep (32 );
146+ return count ;
147+ } catch (RuntimeException error ) {
148+ if (count > 0 ) {
149+ injectCancel (automation , downTime , eventTime + 16 , activePointers );
150+ }
151+ throw error ;
117152 }
118-
119- eventTime = downTime + spec .durationMs ;
120- inject (
121- automation ,
122- motionEvent (
123- downTime ,
124- eventTime ,
125- MotionEvent .ACTION_POINTER_UP | (1 << MotionEvent .ACTION_POINTER_INDEX_SHIFT ),
126- end ));
127- count += 1 ;
128- inject (
129- automation ,
130- motionEvent (downTime , eventTime + 8 , MotionEvent .ACTION_UP , end .firstOnly ()));
131- count += 1 ;
132- return count ;
133153 }
134154
135155 private static void inject (UiAutomation automation , MotionEvent event ) {
136156 try {
137- if (!automation .injectInputEvent (event , true )) {
157+ if (!automation .injectInputEvent (event , WAIT_FOR_INPUT_DISPATCH )) {
138158 throw new IllegalStateException ("injectInputEvent returned false" );
139159 }
140160 } finally {
141161 event .recycle ();
142162 }
143163 }
144164
165+ private static void injectCancel (
166+ UiAutomation automation , long downTime , long eventTime , PointerPair pair ) {
167+ try {
168+ inject (automation , motionEvent (downTime , eventTime , MotionEvent .ACTION_CANCEL , pair ));
169+ } catch (RuntimeException ignored ) {
170+ // Best-effort cleanup; preserve the original injection failure.
171+ }
172+ }
173+
145174 private static MotionEvent motionEvent (long downTime , long eventTime , int action , PointerPair pair ) {
146175 MotionEvent .PointerProperties [] properties =
147176 new MotionEvent .PointerProperties [pair .pointerCount ];
0 commit comments