Skip to content

Commit 8abbdf1

Browse files
adalpariclaude
andauthored
Fix crash when a deep link opens with no browser available (#23121)
* Guard the deep link browser hand-off against a missing browser 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> * Finish the deep link receiver when the browser hand-off fails 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> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 0ff05d5 commit 8abbdf1

2 files changed

Lines changed: 27 additions & 5 deletions

File tree

RELEASE-NOTES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* [*] You can now browse Google Photos (albums, collections, and search) when adding photos or videos from your device.
66
* [**] Fixed media uploads and the Media Library failing on Jetpack-connected self-hosted sites that have Application Passwords disabled.
77
* [*] Stats now refresh when you return to the screen, so your latest data appears without a manual pull-to-refresh. [https://github.com/wordpress-mobile/WordPress-Android/pull/23112]
8+
* [*] Links from emails that open outside the app now show a clear message when no browser is available, instead of closing the app.
89

910
26.9
1011
-----

WordPress/src/main/java/org/wordpress/android/ui/deeplinks/DeepLinkNavigator.kt

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package org.wordpress.android.ui.deeplinks
22

3+
import android.content.ActivityNotFoundException
34
import android.content.Intent
45
import androidx.appcompat.app.AppCompatActivity
6+
import org.wordpress.android.R
57
import org.wordpress.android.fluxc.model.SiteModel
68
import org.wordpress.android.ui.ActivityLauncher
79
import org.wordpress.android.ui.ActivityNavigator
@@ -34,6 +36,9 @@ import org.wordpress.android.ui.sitecreation.misc.SiteCreationSource.DEEP_LINK
3436
import org.wordpress.android.ui.sitemonitor.SiteMonitorType
3537
import org.wordpress.android.ui.stats.StatsTimeframe
3638
import org.wordpress.android.ui.stats.refresh.utils.StatsLaunchedFrom
39+
import org.wordpress.android.util.AppLog
40+
import org.wordpress.android.util.AppLog.T.UTILS
41+
import org.wordpress.android.util.ToastUtils
3742
import org.wordpress.android.util.UriWrapper
3843
import javax.inject.Inject
3944

@@ -52,11 +57,7 @@ class DeepLinkNavigator
5257
activity,
5358
navigateAction.site
5459
)
55-
is OpenInBrowser -> {
56-
@SuppressWarnings("UnsafeImplicitIntentLaunch")
57-
val browserIntent = Intent(Intent.ACTION_VIEW, navigateAction.uri.uri)
58-
activity.startActivity(browserIntent)
59-
}
60+
is OpenInBrowser -> openInBrowser(activity, navigateAction.uri)
6061
is OpenEditorForPost -> ActivityLauncher.openEditorForPostInNewStack(
6162
activity,
6263
navigateAction.site,
@@ -125,6 +126,26 @@ class DeepLinkNavigator
125126
}
126127
}
127128

129+
/**
130+
* Opens the uri with an external browser. The device may have no app able to handle a web link (no browser
131+
* installed, browser disabled or restricted by a work profile), so the missing handler is reported to the user
132+
* instead of crashing the deep link entry point.
133+
*
134+
* The receiver activity has no UI of its own, so it is finished when the hand-off fails. Otherwise the user is
135+
* left on an empty screen after the toast. The toast still shows, since it does not depend on the activity.
136+
*/
137+
private fun openInBrowser(activity: AppCompatActivity, uri: UriWrapper) {
138+
@SuppressWarnings("UnsafeImplicitIntentLaunch")
139+
val browserIntent = Intent(Intent.ACTION_VIEW, uri.uri)
140+
try {
141+
activity.startActivity(browserIntent)
142+
} catch (e: ActivityNotFoundException) {
143+
ToastUtils.showToast(activity, R.string.cant_open_url, ToastUtils.Duration.LONG)
144+
AppLog.e(UTILS, "No app available on the device to open the deep link: $uri", e)
145+
activity.finish()
146+
}
147+
}
148+
128149
sealed class NavigateAction {
129150
object LoginForResult : NavigateAction()
130151
data class OpenInBrowser(val uri: UriWrapper) : NavigateAction()

0 commit comments

Comments
 (0)