Skip to content

Commit c3eb286

Browse files
committed
refactor: consolidate android gesture backend selection
1 parent 7ab9998 commit c3eb286

3 files changed

Lines changed: 68 additions & 81 deletions

File tree

src/platforms/android/__tests__/multitouch-helper.test.ts

Lines changed: 32 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,10 @@ import { ANDROID_EMULATOR } from '../../../__tests__/test-utils/index.ts';
88
import {
99
ensureAndroidMultiTouchHelper,
1010
parseAndroidMultiTouchHelperOutput,
11-
pinchAndroid,
1211
resetAndroidMultiTouchHelperInstallCache,
1312
rotateGestureAndroid,
1413
runAndroidMultiTouchHelperGesture,
1514
swipeGestureAndroid,
16-
transformGestureAndroid,
1715
} from '../multitouch-helper.ts';
1816
import {
1917
withAndroidAdbProvider,
@@ -109,9 +107,7 @@ test('runAndroidMultiTouchHelperGesture encodes one-finger swipe payloads', asyn
109107
capturedPayload = JSON.parse(Buffer.from(args[6]!, 'base64').toString('utf8'));
110108
return {
111109
exitCode: 0,
112-
stdout: [resultRecord({ ok: 'true', kind: 'swipe' }), 'INSTRUMENTATION_CODE: 0'].join(
113-
'\n',
114-
),
110+
stdout: [resultRecord({ ok: 'true', kind: 'swipe' }), 'INSTRUMENTATION_CODE: 0'].join('\n'),
115111
stderr: '',
116112
};
117113
},
@@ -166,66 +162,6 @@ test('runAndroidMultiTouchHelperGesture preserves helper failure messages', asyn
166162
);
167163
});
168164

169-
test('swipeGestureAndroid and multi-touch gestures prefer provider-native touch injection', async () => {
170-
const calls: unknown[] = [];
171-
await withAndroidAdbProvider(
172-
{
173-
exec: async () => {
174-
throw new Error('adb should not run when native touch is available');
175-
},
176-
touch: async (request) => {
177-
calls.push(request);
178-
return { backendDetail: 'native' };
179-
},
180-
},
181-
{ serial: ANDROID_EMULATOR.id },
182-
async () => {
183-
const swipe = await swipeGestureAndroid(ANDROID_EMULATOR, {
184-
x1: 340,
185-
y1: 400,
186-
x2: 60,
187-
y2: 400,
188-
durationMs: 300,
189-
});
190-
const pinch = await pinchAndroid(ANDROID_EMULATOR, { scale: 2, x: 100, y: 200 });
191-
const rotate = await rotateGestureAndroid(ANDROID_EMULATOR, {
192-
degrees: -215,
193-
x: 100,
194-
y: 200,
195-
});
196-
const transform = await transformGestureAndroid(ANDROID_EMULATOR, {
197-
x: 100,
198-
y: 200,
199-
dx: 30,
200-
dy: -20,
201-
scale: 1.5,
202-
degrees: 35,
203-
});
204-
205-
assert.equal(swipe?.backend, 'provider-native-touch');
206-
assert.equal(pinch.backend, 'provider-native-touch');
207-
assert.equal(rotate.backend, 'provider-native-touch');
208-
assert.equal(transform.backend, 'provider-native-touch');
209-
},
210-
);
211-
212-
assert.deepEqual(calls, [
213-
{ kind: 'swipe', x1: 340, y1: 400, x2: 60, y2: 400, durationMs: 300 },
214-
{ kind: 'pinch', x: 100, y: 200, scale: 2, durationMs: undefined },
215-
{ kind: 'rotate', x: 100, y: 200, degrees: -215, durationMs: undefined },
216-
{
217-
kind: 'transform',
218-
x: 100,
219-
y: 200,
220-
dx: 30,
221-
dy: -20,
222-
scale: 1.5,
223-
degrees: 35,
224-
durationMs: undefined,
225-
},
226-
]);
227-
});
228-
229165
test('swipeGestureAndroid falls back to adb input swipe when helper path is unavailable', async () => {
230166
const adbCalls: string[][] = [];
231167
const result = await withAndroidAdbProvider(
@@ -274,6 +210,37 @@ test('swipeGestureAndroid falls back to adb input swipe when helper path is unav
274210
assert.ok(adbCalls.some((args) => args.join(' ') === 'shell input swipe 340 400 60 400 300'));
275211
});
276212

213+
test('swipeGestureAndroid propagates provider-native failures without adb fallback', async () => {
214+
const adbCalls: string[][] = [];
215+
await withAndroidAdbProvider(
216+
{
217+
exec: async (args) => {
218+
adbCalls.push(args);
219+
return { exitCode: 0, stdout: '', stderr: '' };
220+
},
221+
touch: async () => {
222+
throw new Error('native touch failed');
223+
},
224+
},
225+
{ serial: ANDROID_EMULATOR.id },
226+
async () => {
227+
await assert.rejects(
228+
() =>
229+
swipeGestureAndroid(ANDROID_EMULATOR, {
230+
x1: 340,
231+
y1: 400,
232+
x2: 60,
233+
y2: 400,
234+
durationMs: 300,
235+
}),
236+
/native touch failed/,
237+
);
238+
},
239+
);
240+
241+
assert.deepEqual(adbCalls, []);
242+
});
243+
277244
test('rotateGestureAndroid rejects zero velocity before provider dispatch', async () => {
278245
await withAndroidAdbProvider(
279246
{

src/platforms/android/multitouch-helper.ts

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -120,16 +120,14 @@ export async function swipeGestureAndroid(
120120
device: DeviceInfo,
121121
options: AndroidSwipeGestureOptions,
122122
): Promise<Record<string, unknown> | void> {
123-
const providerTouch = resolveAndroidTouchInjector(device);
124-
if (providerTouch) {
125-
return {
126-
backend: 'provider-native-touch',
127-
...((await providerTouch({ kind: 'swipe', ...options })) ?? {}),
128-
};
129-
}
123+
const providerResult = await runAndroidTouchProviderGesture(device, {
124+
kind: 'swipe',
125+
...options,
126+
});
127+
if (providerResult) return providerResult;
130128

131129
try {
132-
return await runAndroidMultiTouchGesture(device, { kind: 'swipe', ...options });
130+
return await runAndroidMultiTouchHelperGestureForDevice(device, { kind: 'swipe', ...options });
133131
} catch (error) {
134132
emitDiagnostic({
135133
level: 'warn',
@@ -151,7 +149,7 @@ export async function pinchAndroid(
151149
throw new AppError('INVALID_ARGS', 'gesture pinch requires scale > 0');
152150
}
153151
const center = await resolveGestureCenter(device, options.x, options.y);
154-
return await runAndroidMultiTouchGesture(device, {
152+
return await performAndroidTouchGesture(device, {
155153
kind: 'pinch',
156154
x: center.x,
157155
y: center.y,
@@ -175,7 +173,7 @@ export async function rotateGestureAndroid(
175173
}
176174
const center = await resolveGestureCenter(device, options.x, options.y);
177175
const degrees = options.degrees;
178-
return await runAndroidMultiTouchGesture(device, {
176+
return await performAndroidTouchGesture(device, {
179177
kind: 'rotate',
180178
x: center.x,
181179
y: center.y,
@@ -197,7 +195,7 @@ export async function transformGestureAndroid(
197195
if (![options.x, options.y, options.dx, options.dy].every(Number.isFinite)) {
198196
throw new AppError('INVALID_ARGS', 'gesture transform requires finite x y dx dy');
199197
}
200-
return await runAndroidMultiTouchGesture(device, {
198+
return await performAndroidTouchGesture(device, {
201199
kind: 'transform',
202200
x: options.x,
203201
y: options.y,
@@ -219,16 +217,30 @@ async function resolveGestureCenter(
219217
return { x: Math.round(size.width / 2), y: Math.round(size.height / 2) };
220218
}
221219

222-
async function runAndroidMultiTouchGesture(
220+
async function performAndroidTouchGesture(
223221
device: DeviceInfo,
224222
request: AndroidTouchGestureRequest,
225223
): Promise<Record<string, unknown>> {
224+
const providerResult = await runAndroidTouchProviderGesture(device, request);
225+
if (providerResult) return providerResult;
226+
227+
return await runAndroidMultiTouchHelperGestureForDevice(device, request);
228+
}
229+
230+
async function runAndroidTouchProviderGesture(
231+
device: DeviceInfo,
232+
request: AndroidTouchGestureRequest,
233+
): Promise<Record<string, unknown> | undefined> {
226234
const providerTouch = resolveAndroidTouchInjector(device);
227-
if (providerTouch) {
228-
const result = (await providerTouch(request)) ?? {};
229-
return { backend: 'provider-native-touch', ...result };
230-
}
235+
if (!providerTouch) return undefined;
236+
const result = (await providerTouch(request)) ?? {};
237+
return { backend: 'provider-native-touch', ...result };
238+
}
231239

240+
async function runAndroidMultiTouchHelperGestureForDevice(
241+
device: DeviceInfo,
242+
request: AndroidTouchGestureRequest,
243+
): Promise<Record<string, unknown>> {
232244
const adb = resolveAndroidAdbExecutor(device);
233245
const artifact = await resolveAndroidMultiTouchHelperArtifact();
234246
const adbProvider = resolveAndroidAdbProvider(device);

test/integration/provider-scenarios/android-lifecycle.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@ test('Provider-backed integration Android touch provider handles multi-touch ges
8585
const client = world.daemon.client();
8686
await client.apps.open({ app: 'settings', ...world.selection });
8787

88+
await client.interactions.swipe({
89+
from: { x: 340, y: 400 },
90+
to: { x: 60, y: 400 },
91+
durationMs: 300,
92+
...world.selection,
93+
});
94+
8895
const pinch = await client.interactions.pinch({
8996
scale: 2,
9097
x: 195,
@@ -118,6 +125,7 @@ test('Provider-backed integration Android touch provider handles multi-touch ges
118125
assert.equal(transform.backend, 'provider-native-touch');
119126

120127
assert.deepEqual(world.touchInjectionCalls, [
128+
{ kind: 'swipe', x1: 340, y1: 400, x2: 60, y2: 400, durationMs: 300 },
121129
{ kind: 'pinch', x: 195, y: 320, scale: 2, durationMs: undefined },
122130
{ kind: 'rotate', x: 195, y: 320, degrees: 145, durationMs: undefined },
123131
{

0 commit comments

Comments
 (0)