* For #5334: added private custom tab processor * For #5334 - Fixes up IntentReceiverActivity for handling intents * For 5334: update styling for private custom tabbs * For 5334: update tests to account for new processors Note that two are still failing. These appear to be true failures, and will be corrected in a later commit. * For 5334: fixes bug introduced by changes to IntentReceiverActivity RCA: intent className and extra were previously set based on which processors matched, not which successfully processed. This patch reintroduces that behavior. * For 5334: add tests for custom tabs processingmaster
parent
4fb26a0601
commit
5f393bd5d4
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,9 +44,3 @@ class DefaultBrowsingModeManager(
|
|||
modeDidChange(value)
|
||||
}
|
||||
}
|
||||
|
||||
class CustomTabBrowsingModeManager : BrowsingModeManager {
|
||||
override var mode
|
||||
get() = BrowsingMode.Normal
|
||||
set(_) { /* no-op */ }
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -121,9 +121,3 @@ class DefaultThemeManager(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CustomTabThemeManager : ThemeManager() {
|
||||
override var currentTheme
|
||||
get() = BrowsingMode.Normal
|
||||
set(_) { /* noop */ }
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) }
|
||||
|
|
Loading…
Reference in New Issue