diff --git a/app/src/main/java/org/mozilla/fenix/IntentReceiverActivity.kt b/app/src/main/java/org/mozilla/fenix/IntentReceiverActivity.kt index 6becaa7b6..e9277d8d8 100644 --- a/app/src/main/java/org/mozilla/fenix/IntentReceiverActivity.kt +++ b/app/src/main/java/org/mozilla/fenix/IntentReceiverActivity.kt @@ -10,14 +10,52 @@ import android.os.Bundle import androidx.annotation.VisibleForTesting import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch -import mozilla.components.feature.intent.processing.TabIntentProcessor +import mozilla.components.feature.intent.processing.IntentProcessor import mozilla.components.support.utils.Browsers +import org.mozilla.fenix.components.IntentProcessors import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.customtabs.ExternalAppBrowserActivity import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.settings import org.mozilla.fenix.shortcut.NewTabShortcutIntentProcessor +enum class IntentProcessorType { + EXTERNAL_APP, NEW_TAB, OTHER; + + /** + * The destination activity based on this intent + */ + val activityClassName: String + get() = when (this) { + EXTERNAL_APP -> ExternalAppBrowserActivity::class.java.name + NEW_TAB -> HomeActivity::class.java.name + OTHER -> HomeActivity::class.java.name + } + + /** + * Should this intent automatically navigate to the browser? + */ + fun shouldOpenToBrowser(intent: Intent): Boolean = when (this) { + EXTERNAL_APP -> true + NEW_TAB -> intent.flags and Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY == 0 + OTHER -> false + } +} + +/** + * Classifies the [IntentType] based on the [IntentProcessor] that handled the [Intent] + */ +fun IntentProcessor?.getType(intentProcessors: IntentProcessors): IntentProcessorType { + return when { + intentProcessors.externalAppIntentProcessors.contains(this) || + intentProcessors.customTabIntentProcessor == this || + intentProcessors.privateCustomTabIntentProcessor == this -> IntentProcessorType.EXTERNAL_APP + intentProcessors.intentProcessor == this || + intentProcessors.privateIntentProcessor == this -> IntentProcessorType.NEW_TAB + else -> IntentProcessorType.OTHER + } +} + /** * Processes incoming intents and sends them to the corresponding activity. */ @@ -47,49 +85,35 @@ class IntentReceiverActivity : Activity() { )) } - val tabIntentProcessor = if (settings().openLinksInAPrivateTab) { - components.analytics.metrics.track(Event.OpenedLink(Event.OpenedLink.Mode.PRIVATE)) - components.intentProcessors.privateIntentProcessor - } else { - components.analytics.metrics.track(Event.OpenedLink(Event.OpenedLink.Mode.NORMAL)) - components.intentProcessors.intentProcessor - } + val modeDependentProcessors = if (settings().openLinksInAPrivateTab) { + components.analytics.metrics.track(Event.OpenedLink(Event.OpenedLink.Mode.PRIVATE)) + listOf( + components.intentProcessors.privateCustomTabIntentProcessor, + components.intentProcessors.privateIntentProcessor + ) + } else { + components.analytics.metrics.track(Event.OpenedLink(Event.OpenedLink.Mode.NORMAL)) + listOf( + components.intentProcessors.customTabIntentProcessor, + components.intentProcessors.intentProcessor + ) + } val intentProcessors = components.intentProcessors.externalAppIntentProcessors + - tabIntentProcessor + + modeDependentProcessors + NewTabShortcutIntentProcessor() + // Call process for side effects, short on the first that returns true intentProcessors.any { it.process(intent) } - setIntentActivity(intent, tabIntentProcessor) + val intentProcessorType = intentProcessors + .firstOrNull { it.matches(intent) } + .getType(components.intentProcessors) + + intent.setClassName(applicationContext, intentProcessorType.activityClassName) + intent.putExtra(HomeActivity.OPEN_TO_BROWSER, intentProcessorType.shouldOpenToBrowser(intent)) startActivity(intent) finish() } - - /** - * Sets the activity that this [intent] will launch. - */ - private fun setIntentActivity(intent: Intent, tabIntentProcessor: TabIntentProcessor) { - val openToBrowser = when { - components.intentProcessors.externalAppIntentProcessors.any { it.matches(intent) } -> { - intent.setClassName(applicationContext, ExternalAppBrowserActivity::class.java.name) - true - } - tabIntentProcessor.matches(intent) -> { - intent.setClassName(applicationContext, HomeActivity::class.java.name) - // This Intent was launched from history (recent apps). Android will redeliver the - // original Intent (which might be a VIEW intent). However if there's no active browsing - // session then we do not want to re-process the Intent and potentially re-open a website - // from a session that the user already "erased". - intent.flags and Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY == 0 - } - else -> { - intent.setClassName(applicationContext, HomeActivity::class.java.name) - false - } - } - - intent.putExtra(HomeActivity.OPEN_TO_BROWSER, openToBrowser) - } } diff --git a/app/src/main/java/org/mozilla/fenix/browser/browsingmode/BrowsingModeManager.kt b/app/src/main/java/org/mozilla/fenix/browser/browsingmode/BrowsingModeManager.kt index 9b9dc2a4f..6d7462b08 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/browsingmode/BrowsingModeManager.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/browsingmode/BrowsingModeManager.kt @@ -44,9 +44,3 @@ class DefaultBrowsingModeManager( modeDidChange(value) } } - -class CustomTabBrowsingModeManager : BrowsingModeManager { - override var mode - get() = BrowsingMode.Normal - set(_) { /* no-op */ } -} diff --git a/app/src/main/java/org/mozilla/fenix/components/IntentProcessors.kt b/app/src/main/java/org/mozilla/fenix/components/IntentProcessors.kt index e4cac05ca..c52505658 100644 --- a/app/src/main/java/org/mozilla/fenix/components/IntentProcessors.kt +++ b/app/src/main/java/org/mozilla/fenix/components/IntentProcessors.kt @@ -45,7 +45,11 @@ class IntentProcessors( } val customTabIntentProcessor by lazy { - CustomTabIntentProcessor(sessionManager, sessionUseCases.loadUrl, context.resources) + CustomTabIntentProcessor(sessionManager, sessionUseCases.loadUrl, context.resources, isPrivate = false) + } + + val privateCustomTabIntentProcessor by lazy { + CustomTabIntentProcessor(sessionManager, sessionUseCases.loadUrl, context.resources, isPrivate = true) } val externalAppIntentProcessors by lazy { @@ -58,8 +62,7 @@ class IntentProcessors( apiKey = BuildConfig.DIGITAL_ASSET_LINKS_TOKEN, store = customTabsStore ), - WebAppIntentProcessor(sessionManager, sessionUseCases.loadUrl, ManifestStorage(context)), - customTabIntentProcessor + WebAppIntentProcessor(sessionManager, sessionUseCases.loadUrl, ManifestStorage(context)) ) } } diff --git a/app/src/main/java/org/mozilla/fenix/customtabs/CustomTabsIntegration.kt b/app/src/main/java/org/mozilla/fenix/customtabs/CustomTabsIntegration.kt index 15de11204..6495ffcca 100644 --- a/app/src/main/java/org/mozilla/fenix/customtabs/CustomTabsIntegration.kt +++ b/app/src/main/java/org/mozilla/fenix/customtabs/CustomTabsIntegration.kt @@ -27,7 +27,8 @@ class CustomTabsIntegration( sessionId: String, activity: Activity, engineLayout: View, - onItemTapped: (ToolbarMenu.Item) -> Unit = {} + onItemTapped: (ToolbarMenu.Item) -> Unit = {}, + isPrivate: Boolean ) : LifecycleAwareFeature, UserInteractionHandler { init { @@ -75,6 +76,15 @@ class CustomTabsIntegration( )!! ) } + + // If in private mode, override toolbar background to use private color + // See #5334 + if (isPrivate) { + toolbar.background = AppCompatResources.getDrawable( + activity, + R.drawable.toolbar_background + ) + } } private val customTabToolbarMenu by lazy { diff --git a/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivity.kt b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivity.kt index 1344062a7..f0b09fed7 100644 --- a/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivity.kt +++ b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivity.kt @@ -14,11 +14,8 @@ import mozilla.components.support.utils.SafeIntent import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.NavGraphDirections -import org.mozilla.fenix.browser.browsingmode.BrowsingMode -import org.mozilla.fenix.browser.browsingmode.CustomTabBrowsingModeManager import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.ext.components -import org.mozilla.fenix.theme.CustomTabThemeManager import java.security.InvalidParameterException /** @@ -60,11 +57,6 @@ open class ExternalAppBrowserActivity : HomeActivity() { } } - final override fun createBrowsingModeManager(initialMode: BrowsingMode) = - CustomTabBrowsingModeManager() - - final override fun createThemeManager() = CustomTabThemeManager() - override fun onDestroy() { super.onDestroy() diff --git a/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt index a2bf1ff19..3b23e4299 100644 --- a/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt @@ -28,6 +28,7 @@ import mozilla.components.lib.state.ext.consumeFrom import mozilla.components.support.base.feature.UserInteractionHandler import mozilla.components.support.base.feature.ViewBoundFeatureWrapper import mozilla.components.support.ktx.android.arch.lifecycle.addObservers +import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R import org.mozilla.fenix.browser.BaseBrowserFragment import org.mozilla.fenix.browser.CustomTabContextMenuCandidate @@ -67,7 +68,8 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), UserInteractionHandler sessionId = customTabSessionId, activity = activity, engineLayout = view.swipeRefresh, - onItemTapped = { browserInteractor.onBrowserToolbarMenuItemTapped(it) } + onItemTapped = { browserInteractor.onBrowserToolbarMenuItemTapped(it) }, + isPrivate = (activity as HomeActivity).browsingModeManager.mode.isPrivate ), owner = this, view = view) diff --git a/app/src/main/java/org/mozilla/fenix/theme/ThemeManager.kt b/app/src/main/java/org/mozilla/fenix/theme/ThemeManager.kt index a955a9b67..1d8977ff2 100644 --- a/app/src/main/java/org/mozilla/fenix/theme/ThemeManager.kt +++ b/app/src/main/java/org/mozilla/fenix/theme/ThemeManager.kt @@ -121,9 +121,3 @@ class DefaultThemeManager( } } } - -class CustomTabThemeManager : ThemeManager() { - override var currentTheme - get() = BrowsingMode.Normal - set(_) { /* noop */ } -} diff --git a/app/src/test/java/org/mozilla/fenix/IntentReceiverActivityTest.kt b/app/src/test/java/org/mozilla/fenix/IntentReceiverActivityTest.kt index 281612095..e81b7144d 100644 --- a/app/src/test/java/org/mozilla/fenix/IntentReceiverActivityTest.kt +++ b/app/src/test/java/org/mozilla/fenix/IntentReceiverActivityTest.kt @@ -10,11 +10,13 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runBlockingTest import mozilla.components.support.test.robolectric.testContext import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.`when` import org.mockito.Mockito.never import org.mockito.Mockito.verify +import org.mozilla.fenix.customtabs.ExternalAppBrowserActivity import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.settings import org.mozilla.fenix.shortcut.NewTabShortcutIntentProcessor @@ -37,6 +39,8 @@ class IntentReceiverActivityTest { `when`(testContext.components.intentProcessors.intentProcessor.process(intent)).thenReturn(true) `when`(testContext.components.intentProcessors.intentProcessor.matches(intent)).thenReturn(true) + `when`(testContext.components.intentProcessors.customTabIntentProcessor.process(intent)).thenReturn(false) + `when`(testContext.components.intentProcessors.customTabIntentProcessor.matches(intent)).thenReturn(false) val activity = Robolectric.buildActivity(IntentReceiverActivity::class.java, intent).get() activity.processIntent(intent) @@ -55,6 +59,7 @@ class IntentReceiverActivityTest { intent.action = NewTabShortcutIntentProcessor.ACTION_OPEN_PRIVATE_TAB `when`(testContext.components.intentProcessors.intentProcessor.process(intent)).thenReturn(false) + `when`(testContext.components.intentProcessors.customTabIntentProcessor.process(intent)).thenReturn(false) val activity = Robolectric.buildActivity(IntentReceiverActivity::class.java, intent).get() activity.processIntent(intent) @@ -74,6 +79,7 @@ class IntentReceiverActivityTest { intent.action = NewTabShortcutIntentProcessor.ACTION_OPEN_TAB `when`(testContext.components.intentProcessors.intentProcessor.process(intent)).thenReturn(true) + `when`(testContext.components.intentProcessors.customTabIntentProcessor.process(intent)).thenReturn(false) val activity = Robolectric.buildActivity(IntentReceiverActivity::class.java, intent).get() activity.processIntent(intent) @@ -91,6 +97,7 @@ class IntentReceiverActivityTest { val intent = Intent() `when`(testContext.components.intentProcessors.intentProcessor.process(intent)).thenReturn(true) + `when`(testContext.components.intentProcessors.customTabIntentProcessor.process(intent)).thenReturn(false) val activity = Robolectric.buildActivity(IntentReceiverActivity::class.java, intent).get() activity.processIntent(intent) @@ -107,6 +114,7 @@ class IntentReceiverActivityTest { val intent = Intent() `when`(testContext.components.intentProcessors.privateIntentProcessor.process(intent)).thenReturn(true) + `when`(testContext.components.intentProcessors.privateCustomTabIntentProcessor.process(intent)).thenReturn(false) val activity = Robolectric.buildActivity(IntentReceiverActivity::class.java, intent).get() activity.processIntent(intent) @@ -122,6 +130,7 @@ class IntentReceiverActivityTest { val intent = Intent() `when`(testContext.components.intentProcessors.intentProcessor.process(intent)).thenReturn(true) + `when`(testContext.components.intentProcessors.customTabIntentProcessor.process(intent)).thenReturn(false) val activity = Robolectric.buildActivity(IntentReceiverActivity::class.java, intent).get() activity.processIntent(intent) @@ -131,4 +140,44 @@ class IntentReceiverActivityTest { verify(testContext.components.intentProcessors.privateIntentProcessor, never()).process(intent) verify(testContext.components.intentProcessors.intentProcessor).process(intent) } + + @Test + fun `process custom tab intent`() = runBlockingTest { + testContext.settings().openLinksInAPrivateTab = false + + val intent = Intent() + `when`(testContext.components.intentProcessors.customTabIntentProcessor.process(intent)).thenReturn(true) + `when`(testContext.components.intentProcessors.customTabIntentProcessor.matches(intent)).thenReturn(true) + + val activity = Robolectric.buildActivity(IntentReceiverActivity::class.java, intent).get() + activity.processIntent(intent) + + // Not using mockk here because process is a suspend function + // and mockito makes this easier to read. + verify(testContext.components.intentProcessors.privateIntentProcessor, never()).process(intent) + verify(testContext.components.intentProcessors.customTabIntentProcessor).process(intent) + + assertEquals(ExternalAppBrowserActivity::class.java.name, intent.component!!.className) + assertTrue(intent.getBooleanExtra(HomeActivity.OPEN_TO_BROWSER, false)) + } + + @Test + fun `process private custom tab intent`() = runBlockingTest { + testContext.settings().openLinksInAPrivateTab = true + + val intent = Intent() + `when`(testContext.components.intentProcessors.privateCustomTabIntentProcessor.process(intent)).thenReturn(true) + `when`(testContext.components.intentProcessors.privateCustomTabIntentProcessor.matches(intent)).thenReturn(true) + + val activity = Robolectric.buildActivity(IntentReceiverActivity::class.java, intent).get() + activity.processIntent(intent) + + // Not using mockk here because process is a suspend function + // and mockito makes this easier to read. + verify(testContext.components.intentProcessors.intentProcessor, never()).process(intent) + verify(testContext.components.intentProcessors.privateCustomTabIntentProcessor).process(intent) + + assertEquals(ExternalAppBrowserActivity::class.java.name, intent.component!!.className) + assertTrue(intent.getBooleanExtra(HomeActivity.OPEN_TO_BROWSER, false)) + } } diff --git a/app/src/test/java/org/mozilla/fenix/components/TestComponents.kt b/app/src/test/java/org/mozilla/fenix/components/TestComponents.kt index b7e3c08ed..0ce3505a9 100644 --- a/app/src/test/java/org/mozilla/fenix/components/TestComponents.kt +++ b/app/src/test/java/org/mozilla/fenix/components/TestComponents.kt @@ -32,6 +32,8 @@ class TestComponents(private val context: Context) : Components(context) { `when`(processors.externalAppIntentProcessors).thenReturn(emptyList()) `when`(processors.privateIntentProcessor).thenReturn(mock()) `when`(processors.intentProcessor).thenReturn(mock()) + `when`(processors.customTabIntentProcessor).thenReturn(mock()) + `when`(processors.privateCustomTabIntentProcessor).thenReturn(mock()) processors } override val analytics by lazy { Analytics(context) }