Skip to content

Open the app's own links in a browser on Android - #4

Merged
simonhamp merged 1 commit into
mainfrom
fix/android-own-links-in-browser
Oct 22, 2026
Merged

simonhamp merged 1 commit into
mainfrom
fix/android-own-links-in-browser

Conversation

@simonhamp

Copy link
Copy Markdown
Member

On Android, Browser::open() fires a plain ACTION_VIEW intent. If the URL is one the app holds a verified App Link for (a link on its NATIVEPHP_DEEPLINK_HOST), Android resolves that intent straight back to the app's own MainActivity. The browser never opens and the link comes back in as a deep link. On core 4.6.0 an unrouted deep link is handed to Browser::open() again, so the app loops. On an emulator that was about 1,500 round trips in 6 seconds, and Chrome never started.

Browser::inApp() has the same problem, because a Custom Tabs intent with no package goes through the same resolution. So does the tab Browser::auth() opens.

The fix: before launching, ask the package manager which activities would handle the URL. If the app itself is one of them, set a selector for the same request with a host-less URL (https://). Only browsers match a web URL with no host, so Android picks the default browser and still delivers the real URL. This is the same trick as NativePHP/mobile-air#558. Every other URL is left alone, so YouTube and Maps links still open in those apps. No <queries> entry is needed because an app can always see its own package.

I used queryIntentActivities rather than resolveActivity so the check also covers the chooser. Before Android 12 an unverified link can show the app in the chooser next to the browsers, and resolveActivity would only report the chooser. On Android 12 and later the query already leaves the app out when its link isn't approved, so nothing changes in that case.

Testing

API 35 emulator with Chrome as the default browser and YouTube installed. Starter app on core 4.6.0 with NATIVEPHP_DEEPLINK_HOST=example.com, approved with pm set-app-links-user-selection. Plugin main for "before", this branch for "after". The evidence is the ActivityTaskManager START lines in logcat.

Call Before After
open() own link ~1,500 STARTs back into MainActivity in 6 s, Chrome never started Chrome, sel=https:///
inApp() own link ~1,450 STARTs back into MainActivity in 6 s Chrome, with the Custom Tabs extras
auth() own link Tab intent went to MainActivity, sign-in page never opened Chrome, launched from AuthTabActivity
open() YouTube link YouTube app YouTube app, intent unchanged
open() github.com Chrome Chrome, intent unchanged
open() own link, approval turned off Chrome, intent unchanged

Chrome stopped at its first-run screen because I didn't accept its terms on the emulator, so I didn't see the page itself render.

Not tested: Android 11 and older (the chooser case), and two browsers with no default set.

Custom schemes are untouched. There's no browser to send them to, and open('myapp://...') should keep reaching the app that registered the scheme.

The new test in PluginTest checks the Kotlin source, like the existing AuthTab test. There are no Kotlin unit tests in this repo, which is why I tested on the emulator.

iOS doesn't need this. Opening an app's own universal link with UIApplication.open goes to Safari on the iOS 26.2 and 27.0 simulators (checked for mobile-air#558).

🤖 Generated with Claude Code

Browser::open() fired a plain ACTION_VIEW intent. When the URL is one the app
holds a verified App Link for, Android resolves that intent straight back to
the app's own MainActivity, so the browser never opens and the link comes in as
a deep link instead. On core 4.6 an unrouted deep link is handed to
Browser::open() again, so the app loops. Custom Tabs (inApp, and the auth tab)
go through the same resolution and have the same problem.

Before launching, check whether the app itself is one of the activities that
would handle the URL. Only then set a selector for the same request with a
host-less URL, which only browsers can match, so the default browser gets the
real URL. Every other URL is left alone, so links to YouTube or Maps still open
in those apps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@simonhamp
simonhamp merged commit fdb8926 into main Oct 22, 2026
2 checks passed
@simonhamp
simonhamp deleted the fix/android-own-links-in-browser branch October 22, 2026 19:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant