diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt index 7832aa2c72b1..9887dd6d7744 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt @@ -2229,13 +2229,25 @@ class BrowserTabFragment : } is Command.HandleNonHttpAppLink -> { - openExternalDialog( - intent = it.nonHttpAppLink.intent, - fallbackUrl = it.nonHttpAppLink.fallbackUrl, - fallbackIntent = it.nonHttpAppLink.fallbackIntent, - useFirstActivityFound = false, - headers = it.headers, - ) + if (isActiveCustomTab() && isLaunchedFromExternalApp) { + // For external Custom Tabs, non-HTTP(s) schemes are frequently used as auth callbacks + // back into the originating app. Prompting here can break the flow and cause the app + // to restart/loop the login process. + launchNonHttpAppLinkFromExternalCustomTab( + intent = it.nonHttpAppLink.intent, + fallbackUrl = it.nonHttpAppLink.fallbackUrl, + fallbackIntent = it.nonHttpAppLink.fallbackIntent, + headers = it.headers, + ) + } else { + openExternalDialog( + intent = it.nonHttpAppLink.intent, + fallbackUrl = it.nonHttpAppLink.fallbackUrl, + fallbackIntent = it.nonHttpAppLink.fallbackIntent, + useFirstActivityFound = false, + headers = it.headers, + ) + } } is Command.ExtractUrlFromCloakedAmpLink -> { @@ -2751,6 +2763,50 @@ class BrowserTabFragment : } } + private fun launchNonHttpAppLinkFromExternalCustomTab( + intent: Intent, + fallbackUrl: String? = null, + fallbackIntent: Intent? = null, + headers: Map = emptyMap(), + ) { + // Only act if the fragment is still active; avoids launching from background tabs. + if (!isActiveCustomTab()) return + + context?.let { context -> + val pm = context.packageManager + val activities = pm.queryIntentActivities(intent, 0) + + when { + activities.isEmpty() -> { + when { + fallbackIntent != null -> { + runCatching { context.startActivity(fallbackIntent) } + .onFailure { showToast(R.string.unableToOpenLink) } + } + + fallbackUrl != null -> { + webView?.loadUrl(fallbackUrl, headers) + } + + else -> showToast(R.string.unableToOpenLink) + } + } + + activities.size == 1 -> { + runCatching { context.startActivity(intent) } + .onFailure { showToast(R.string.unableToOpenLink) } + } + + else -> { + val title = getString(R.string.openExternalApp) + val chooser = Intent.createChooser(intent, title) + runCatching { context.startActivity(chooser) } + .onFailure { showToast(R.string.unableToOpenLink) } + } + } + } + } + private fun launchDialogForIntent( context: Context, pm: PackageManager, diff --git a/app/src/main/java/com/duckduckgo/app/browser/customtabs/CustomTabActivity.kt b/app/src/main/java/com/duckduckgo/app/browser/customtabs/CustomTabActivity.kt index f3473fd67922..e982211b2b6f 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/customtabs/CustomTabActivity.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/customtabs/CustomTabActivity.kt @@ -108,8 +108,19 @@ class CustomTabActivity : DuckDuckGoActivity() { } companion object { - fun intent(context: Context, flags: Int, text: String?, toolbarColor: Int, isExternal: Boolean): Intent { - return Intent(context, CustomTabActivity::class.java).apply { + fun intent( + context: Context, + originalIntent: Intent?, + flags: Int, + text: String?, + toolbarColor: Int, + isExternal: Boolean, + ): Intent { + // Preserve Custom Tabs extras from the original caller intent (e.g., session binder), + // while still routing to our CustomTabActivity implementation. + val base = originalIntent?.let { Intent(it) } ?: Intent() + return base.apply { + setClass(context, CustomTabActivity::class.java) addFlags(flags) putExtra(CustomTabsIntent.EXTRA_TOOLBAR_COLOR, toolbarColor) putExtra(Intent.EXTRA_TEXT, text) diff --git a/app/src/main/java/com/duckduckgo/app/dispatchers/IntentDispatcherActivity.kt b/app/src/main/java/com/duckduckgo/app/dispatchers/IntentDispatcherActivity.kt index 4d55ce34e76c..7554419b9e6d 100644 --- a/app/src/main/java/com/duckduckgo/app/dispatchers/IntentDispatcherActivity.kt +++ b/app/src/main/java/com/duckduckgo/app/dispatchers/IntentDispatcherActivity.kt @@ -72,7 +72,8 @@ class IntentDispatcherActivity : DuckDuckGoActivity() { startActivity( CustomTabActivity.intent( context = this, - flags = Intent.FLAG_ACTIVITY_NEW_TASK and Intent.FLAG_ACTIVITY_CLEAR_TASK and Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS, + originalIntent = intent, + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS, text = intentText, toolbarColor = toolbarColor, isExternal = isExternal,