Skip to content

Commit 142e109

Browse files
EarthXPclaude
andcommitted
fix: add MAIN/LAUNCHER flags to Android launcher activity resolver
The `resolve-activity` fallback command was missing `-a android.intent.action.MAIN` and `-c android.intent.category.LAUNCHER`, causing it to resolve the wrong activity on multi-entry apps like Microsoft Outlook. This adds the correct intent flags so the resolver consistently returns the launcher activity. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 67b6698 commit 142e109

2 files changed

Lines changed: 101 additions & 1 deletion

File tree

src/platforms/android/__tests__/index.test.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,95 @@ test('swipeAndroid invokes adb input swipe with duration', async () => {
317317
}
318318
});
319319

320+
test('openAndroidApp default launch uses -p package flag', async () => {
321+
await withMockedAdb(
322+
'agent-device-android-open-default-',
323+
[
324+
'#!/bin/sh',
325+
'printf "__CMD__\\n" >> "$AGENT_DEVICE_TEST_ARGS_FILE"',
326+
'printf "%s\\n" "$@" >> "$AGENT_DEVICE_TEST_ARGS_FILE"',
327+
'if [ "$1" = "-s" ]; then',
328+
' shift',
329+
' shift',
330+
'fi',
331+
'if [ "$1" = "shell" ] && [ "$2" = "pm" ] && [ "$3" = "list" ]; then',
332+
' echo "package:com.example.app"',
333+
' exit 0',
334+
'fi',
335+
'if [ "$1" = "shell" ] && [ "$2" = "am" ] && [ "$3" = "start" ]; then',
336+
' echo "Status: ok"',
337+
' exit 0',
338+
'fi',
339+
'exit 0',
340+
'',
341+
].join('\n'),
342+
async ({ argsLogPath, device }) => {
343+
await openAndroidApp(device, 'com.example.app');
344+
const logged = await fs.readFile(argsLogPath, 'utf8');
345+
assert.match(logged, /shell\nam\nstart\n-W\n-a\nandroid\.intent\.action\.MAIN/);
346+
assert.match(logged, /-p\ncom\.example\.app/);
347+
},
348+
);
349+
});
350+
351+
test('openAndroidApp fallback resolve-activity includes MAIN/LAUNCHER flags', async () => {
352+
await withMockedAdb(
353+
'agent-device-android-open-fallback-',
354+
[
355+
'#!/bin/sh',
356+
'printf "__CMD__\\n" >> "$AGENT_DEVICE_TEST_ARGS_FILE"',
357+
'printf "%s\\n" "$@" >> "$AGENT_DEVICE_TEST_ARGS_FILE"',
358+
'if [ "$1" = "-s" ]; then',
359+
' shift',
360+
' shift',
361+
'fi',
362+
'if [ "$1" = "shell" ] && [ "$2" = "pm" ] && [ "$3" = "list" ]; then',
363+
' echo "package:com.microsoft.office.outlook"',
364+
' exit 0',
365+
'fi',
366+
'# First am start (with -p) fails to simulate multi-entry app issue',
367+
'if [ "$1" = "shell" ] && [ "$2" = "am" ] && [ "$3" = "start" ]; then',
368+
' for arg in "$@"; do',
369+
' if [ "$arg" = "-p" ]; then',
370+
' echo "Error: Activity not started" >&2',
371+
' exit 1',
372+
' fi',
373+
' done',
374+
' echo "Status: ok"',
375+
' exit 0',
376+
'fi',
377+
'# resolve-activity returns correct launcher component',
378+
'if [ "$1" = "shell" ] && [ "$2" = "cmd" ] && [ "$3" = "package" ] && [ "$4" = "resolve-activity" ]; then',
379+
' echo "priority=0 preferredOrder=0 match=0x108000 specificIndex=-1 isDefault=true"',
380+
' echo "com.microsoft.office.outlook/com.microsoft.office.outlook.ui.miit.MiitLauncherActivity"',
381+
' exit 0',
382+
'fi',
383+
'exit 0',
384+
'',
385+
].join('\n'),
386+
async ({ argsLogPath, device }) => {
387+
await openAndroidApp(device, 'com.microsoft.office.outlook');
388+
const logged = await fs.readFile(argsLogPath, 'utf8');
389+
// Verify resolve-activity was called with MAIN/LAUNCHER flags
390+
assert.match(logged, /resolve-activity\n--brief\n-a\nandroid\.intent\.action\.MAIN\n-c\nandroid\.intent\.category\.LAUNCHER\ncom\.microsoft\.office\.outlook/);
391+
// Verify fallback launch used the resolved component
392+
assert.match(logged, /-n\ncom\.microsoft\.office\.outlook\/com\.microsoft\.office\.outlook\.ui\.miit\.MiitLauncherActivity/);
393+
},
394+
);
395+
});
396+
397+
test('parseAndroidLaunchComponent handles multi-entry resolve output', () => {
398+
// Some devices return extra metadata lines before the component
399+
const stdout = [
400+
'priority=0 preferredOrder=0 match=0x108000 specificIndex=-1 isDefault=true',
401+
'com.microsoft.office.outlook/com.microsoft.office.outlook.ui.miit.MiitLauncherActivity',
402+
].join('\n');
403+
assert.equal(
404+
parseAndroidLaunchComponent(stdout),
405+
'com.microsoft.office.outlook/com.microsoft.office.outlook.ui.miit.MiitLauncherActivity',
406+
);
407+
});
408+
320409
test('setAndroidSetting permission grant camera uses pm grant', async () => {
321410
await withMockedAdb(
322411
'agent-device-android-permission-camera-',

src/platforms/android/index.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,18 @@ async function resolveAndroidLaunchComponent(
286286
): Promise<string | null> {
287287
const result = await runCmd(
288288
'adb',
289-
adbArgs(device, ['shell', 'cmd', 'package', 'resolve-activity', '--brief', packageName]),
289+
adbArgs(device, [
290+
'shell',
291+
'cmd',
292+
'package',
293+
'resolve-activity',
294+
'--brief',
295+
'-a',
296+
'android.intent.action.MAIN',
297+
'-c',
298+
'android.intent.category.LAUNCHER',
299+
packageName,
300+
]),
290301
{ allowFailure: true },
291302
);
292303
if (result.exitCode !== 0) return null;

0 commit comments

Comments
 (0)