@@ -18,6 +18,7 @@ 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 = 16 ;
2122 private Bundle arguments ;
2223
2324 @ Override
@@ -91,57 +92,82 @@ private int injectGesture(GestureSpec spec) {
9192 long eventTime = downTime ;
9293 PointerPair start = pointerPairAt (spec , 0 );
9394 PointerPair end = pointerPairAt (spec , 1 );
95+ PointerPair activePointers = start .firstOnly ();
9496 int count = 0 ;
9597
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 ;
98+ try {
99+ inject (
100+ automation ,
101+ motionEvent (downTime , eventTime , MotionEvent .ACTION_DOWN , activePointers ),
102+ true );
103+ count += 1 ;
104+ eventTime += 8 ;
105+ inject (
106+ automation ,
107+ motionEvent (
108+ downTime ,
109+ eventTime ,
110+ MotionEvent .ACTION_POINTER_DOWN | (1 << MotionEvent .ACTION_POINTER_INDEX_SHIFT ),
111+ start ),
112+ true );
113+ count += 1 ;
114+ activePointers = start ;
115+
116+ int frameCount =
117+ Math .max (3 , Math .round (spec .durationMs / (float ) MOVE_FRAME_INTERVAL_MS ));
118+ for (int index = 1 ; index < frameCount ; index += 1 ) {
119+ double t = (double ) index / (double ) frameCount ;
120+ PointerPair frame = pointerPairAt (spec , t );
121+ eventTime = downTime + Math .round (spec .durationMs * t );
122+ inject (automation , motionEvent (downTime , eventTime , MotionEvent .ACTION_MOVE , frame ), false );
123+ count += 1 ;
124+ activePointers = frame ;
125+ }
109126
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 ));
127+ eventTime = downTime + spec .durationMs ;
128+ inject (
129+ automation ,
130+ motionEvent (
131+ downTime ,
132+ eventTime ,
133+ MotionEvent .ACTION_POINTER_UP | (1 << MotionEvent .ACTION_POINTER_INDEX_SHIFT ),
134+ end ),
135+ true );
136+ count += 1 ;
137+ activePointers = end .firstOnly ();
138+ inject (
139+ automation ,
140+ motionEvent (downTime , eventTime + 8 , MotionEvent .ACTION_UP , activePointers ),
141+ true );
116142 count += 1 ;
143+ return count ;
144+ } catch (RuntimeException error ) {
145+ if (count > 0 ) {
146+ injectCancel (automation , downTime , eventTime + 16 , activePointers );
147+ }
148+ throw error ;
117149 }
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 ;
133150 }
134151
135- private static void inject (UiAutomation automation , MotionEvent event ) {
152+ private static void inject (UiAutomation automation , MotionEvent event , boolean waitForDispatch ) {
136153 try {
137- if (!automation .injectInputEvent (event , true )) {
154+ if (!automation .injectInputEvent (event , waitForDispatch )) {
138155 throw new IllegalStateException ("injectInputEvent returned false" );
139156 }
140157 } finally {
141158 event .recycle ();
142159 }
143160 }
144161
162+ private static void injectCancel (
163+ UiAutomation automation , long downTime , long eventTime , PointerPair pair ) {
164+ try {
165+ inject (automation , motionEvent (downTime , eventTime , MotionEvent .ACTION_CANCEL , pair ), true );
166+ } catch (RuntimeException ignored ) {
167+ // Best-effort cleanup; preserve the original injection failure.
168+ }
169+ }
170+
145171 private static MotionEvent motionEvent (long downTime , long eventTime , int action , PointerPair pair ) {
146172 MotionEvent .PointerProperties [] properties =
147173 new MotionEvent .PointerProperties [pair .pointerCount ];
0 commit comments