Fix crash when a deep link opens with no browser available#23121
Conversation
A tracking link whose redirect target the app can't handle is passed to an external browser with an implicit ACTION_VIEW intent. On devices with no app able to handle a web link the intent fails to resolve and the deep link receiver dies before it can tell the user anything. Report the missing handler with a toast instead, matching how the rest of the app opens external URLs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Generated by 🚫 Danger |
|
|
|
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## trunk #23121 +/- ##
==========================================
- Coverage 37.69% 37.68% -0.01%
==========================================
Files 2343 2343
Lines 127339 127342 +3
Branches 17658 17658
==========================================
Hits 47995 47995
- Misses 75411 75414 +3
Partials 3933 3933 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
The receiver activity has no UI of its own and nothing on the OpenInBrowser path finishes it, so after the toast the user was left on an empty screen until they pressed back. Verified on device with every web link handler disabled: the toast shows and the launcher comes back. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@adalpari This fix is good but can't we simply reuse |
That was also my thought after Claude made the changes. But when I asked, it turned out to be not exactly what we are looking for:
I'm not sure to understand the purpose after the explanined behaviour in 1, but yes, it's there: |
Oof, that's unfortunate - that existing function should just be a safe way to open a URL, but that deep link disabling corrupts it. At any rate, this PR is good and I'll approve it. |


Description
Fixes an
ActivityNotFoundExceptioncrash inDeepLinkingIntentReceiverActivity:How it happens. A WordPress.com email link (
https://public-api.wordpress.com/mbar/...) is caught by theapp's intent filter.
DeepLinkingIntentReceiverViewModelunwraps itsredirect_toparameter, finds no in-apphandler for the target, and emits
OpenInBrowser.DeepLinkNavigatorthen fired an implicitACTION_VIEWintent with no guard, so on a device where nothing can handle a web link — no browser installed, browser
disabled, or restricted by a work profile — the deep link receiver crashed before it could tell the user
anything.
The fix. The
OpenInBrowserbranch moves into a smallopenInBrowser()helper that catchesActivityNotFoundExceptionand reports it with a toast plus anAppLogentry. This matches how the rest ofthe app already opens external URLs (
ActivityLauncher.openUrlExternal,ReaderActivityLauncher,StatsNavigator,ActivityNavigator) — this call site was the outlier.The helper also calls
finish()on the failure path. The receiver activity has no UI of its own: it uses anopaque theme (
WordPress.NoActionBar) and never callssetContentView, and nothing on theOpenInBrowserpath finishes it — the view model only emits
finishfor links it could not build a navigation action for.Without the
finish()call the user tapping a link from an email would get the toast and then be parked on ablank screen until they pressed back. The toast is unaffected, since it does not depend on the activity
staying alive.
No tests added: there is no existing
DeepLinkNavigatorTest, andhandleNavigationActiontakes a rawAppCompatActivity, so covering this would need a wrapper refactor beyond the scope of a crash fix.Follow-up (not in this PR)
While tracing the crash I found a related gap worth its own issue: the failing URL was on
www.wordpress.com,but every link handler compares the host with exact equality against
HOST_WORDPRESS_COM = "wordpress.com"(
StatsLinkHandler,PagesLinkHandler,ReaderLinkHandler,DeepLinkUriUtils.isWpLoginUrl), and themanifest web-link filters only declare
android:host="wordpress.com". So email links redirecting to thewww.host bounce out to the browser instead of opening in-app, even on devices that have a browser. Fixing that
means normalising the host across the handlers, plus
assetlinks.jsonverification for thewww.hostname ifthe app should intercept those links directly.
Testing instructions
Both checks use the same deep link. Trigger it either via
adb:...or by tapping the same link from an external app (an email or a notes app).
1. Normal hand-off (device with a browser)
2. Crash scenario (device with no browser available)
adb shell pm disable-user --user 0 <package>), then run the queryagain. Repeat until it prints
No activities found— the query surfaces the preferred handler first,so a second browser can stay hidden behind the default one until that one is disabled.
you triggered the link from (the launcher, or the email/notes app).
adb shell pm enable <package>). If you disabled the defaultbrowser, Android reassigns the browser role to another app, so check and restore it:
3. Regression check on a deep link the app handles itself
adb shell am start -a android.intent.action.VIEW -d "wordpress://stats".Why the explicit component (
-n) in the deep link command: on a debug build thepublic-api.wordpress.comhost is not App Links–verified (
adb shell pm get-app-links <applicationId>reports it unverified), soAndroid 12+ routes the implicit intent straight to the default browser and the app is never launched. Naming
the component bypasses that and exercises the real deep link code. Tapping the link from an external app on a
production build works without this, since the host is verified there.