Copione merged onto master
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
commit
3e5e5865a5
|
@ -509,6 +509,7 @@ dependencies {
|
|||
implementation Deps.mozilla_service_location
|
||||
|
||||
implementation Deps.mozilla_support_base
|
||||
implementation Deps.mozilla_support_images
|
||||
implementation Deps.mozilla_support_ktx
|
||||
implementation Deps.mozilla_support_rustlog
|
||||
implementation Deps.mozilla_support_utils
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.ui
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.uiautomator.UiDevice
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
|
||||
import org.mozilla.fenix.helpers.HomeActivityTestRule
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||
import org.mozilla.fenix.ui.robots.homeScreen
|
||||
import org.mozilla.fenix.ui.robots.navigationToolbar
|
||||
|
||||
/**
|
||||
* Test Suite that contains tests defined as part of the Smoke and Sanity check defined in Test rail.
|
||||
* These tests will verify different functionalities of the app as a way to quickly detect regressions in main areas
|
||||
*/
|
||||
class SmokeTest {
|
||||
private val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||
private lateinit var mockWebServer: MockWebServer
|
||||
|
||||
@get:Rule
|
||||
val activityTestRule = HomeActivityTestRule()
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mockWebServer = MockWebServer().apply {
|
||||
setDispatcher(AndroidAssetDispatcher())
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
mockWebServer.shutdown()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyBasicNavigationToolbarFunctionality() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
homeScreen {
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
verifyPageContent(defaultWebPage.content)
|
||||
verifyNavURLBarItems()
|
||||
}.openNavigationToolbar {
|
||||
}.goBackToWebsite {
|
||||
// Check disabled due to intermittent failures
|
||||
// verifyPageContent(defaultWebPage.content)
|
||||
}.openTabDrawer {
|
||||
verifyExistingTabList()
|
||||
}.openHomeScreen {
|
||||
verifyHomeScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.Espresso.pressBack
|
||||
import androidx.test.espresso.action.ViewActions
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.intent.Intents
|
||||
|
@ -17,6 +18,7 @@ import androidx.test.espresso.intent.matcher.BundleMatchers
|
|||
import androidx.test.espresso.intent.matcher.IntentMatchers
|
||||
import androidx.test.espresso.matcher.ViewMatchers
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed
|
||||
import androidx.test.espresso.matcher.ViewMatchers.Visibility
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||
|
@ -134,6 +136,32 @@ class BrowserRobot {
|
|||
)
|
||||
}
|
||||
|
||||
fun verifyNavURLBar() = assertNavURLBar()
|
||||
|
||||
fun verifySecureConnectionLockIcon() = assertSecureConnectionLockIcon()
|
||||
|
||||
fun verifyEnhancedTrackingProtectionSwitch() = assertEnhancedTrackingProtectionSwitch()
|
||||
|
||||
fun verifyProtectionSettingsButton() = assertProtectionSettingsButton()
|
||||
|
||||
fun verifyEnhancedTrackingOptions() {
|
||||
clickEnhancedTrackingProtectionPanel()
|
||||
verifyEnhancedTrackingProtectionSwitch()
|
||||
verifyProtectionSettingsButton()
|
||||
}
|
||||
|
||||
fun verifyMenuButton() = assertMenuButton()
|
||||
|
||||
fun verifyNavURLBarItems() {
|
||||
verifyEnhancedTrackingOptions()
|
||||
pressBack()
|
||||
waitingTime
|
||||
verifySecureConnectionLockIcon()
|
||||
verifyTabCounter("1")
|
||||
verifyNavURLBar()
|
||||
verifyMenuButton()
|
||||
}
|
||||
|
||||
fun verifyNoLinkImageContextMenuItems(containsTitle: String) {
|
||||
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||
mDevice.waitNotNull(Until.findObject(By.textContains(containsTitle)))
|
||||
|
@ -147,6 +175,8 @@ class BrowserRobot {
|
|||
)
|
||||
}
|
||||
|
||||
fun clickEnhancedTrackingProtectionPanel() = enhancedTrackingProtectionPanel().click()
|
||||
|
||||
fun clickContextOpenLinkInNewTab() {
|
||||
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||
mDevice.waitNotNull(
|
||||
|
@ -336,8 +366,7 @@ class BrowserRobot {
|
|||
}
|
||||
|
||||
fun openTabDrawer(interact: TabDrawerRobot.() -> Unit): TabDrawerRobot.Transition {
|
||||
mDevice.waitForIdle()
|
||||
|
||||
mDevice.waitForIdle(waitingTime)
|
||||
tabsCounter().click()
|
||||
|
||||
mDevice.waitNotNull(
|
||||
|
@ -372,7 +401,34 @@ fun dismissTrackingOnboarding() {
|
|||
|
||||
fun navURLBar() = onView(withId(R.id.mozac_browser_toolbar_url_view))
|
||||
|
||||
private fun tabsCounter() = onView(withId(R.id.mozac_browser_toolbar_browser_actions))
|
||||
private fun assertNavURLBar() = navURLBar()
|
||||
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||
|
||||
fun enhancedTrackingProtectionPanel() = onView(withId(R.id.mozac_browser_toolbar_tracking_protection_indicator))
|
||||
|
||||
private fun assertEnhancedTrackingProtectionSwitch() {
|
||||
withText(R.id.trackingProtectionSwitch)
|
||||
.matches(withEffectiveVisibility(Visibility.VISIBLE))
|
||||
}
|
||||
|
||||
private fun assertProtectionSettingsButton() {
|
||||
onView(withId(R.id.protection_settings))
|
||||
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||
}
|
||||
|
||||
private fun assertSecureConnectionLockIcon() {
|
||||
onView(withId(R.id.mozac_browser_toolbar_security_indicator))
|
||||
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||
}
|
||||
|
||||
private fun menuButton() = onView(withId(R.id.icon))
|
||||
|
||||
private fun assertMenuButton() {
|
||||
menuButton()
|
||||
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||
}
|
||||
|
||||
private fun tabsCounter() = onView(withId(R.id.counter_box))
|
||||
|
||||
private fun mediaPlayerPlayButton() =
|
||||
mDevice.findObject(
|
||||
|
|
|
@ -514,13 +514,13 @@ private fun assertCollectionsHeader() =
|
|||
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||
|
||||
private fun assertNoCollectionsHeader() =
|
||||
onView(allOf(withText("No collections")))
|
||||
onView(allOf(withText("Collect the things that matter to you")))
|
||||
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||
|
||||
private fun assertNoCollectionsText() =
|
||||
onView(
|
||||
allOf(
|
||||
withText("Collect the things that matter to you. To start, save open tabs to a new collection.")
|
||||
withText("Group together similar searches, sites, and tabs for quick access later.")
|
||||
)
|
||||
)
|
||||
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||
|
|
|
@ -24,6 +24,7 @@ import androidx.test.uiautomator.By
|
|||
import androidx.test.uiautomator.UiDevice
|
||||
import androidx.test.uiautomator.Until
|
||||
import org.hamcrest.CoreMatchers.anyOf
|
||||
import org.hamcrest.CoreMatchers.containsString
|
||||
import org.hamcrest.CoreMatchers.not
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.helpers.SessionLoadedIdlingResource
|
||||
|
@ -51,6 +52,24 @@ class NavigationToolbarRobot {
|
|||
private lateinit var sessionLoadedIdlingResource: SessionLoadedIdlingResource
|
||||
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||
|
||||
fun goBackToWebsite(interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
|
||||
mDevice.waitNotNull(
|
||||
Until.findObject(By.res("org.mozilla.fenix.debug:id/toolbar")),
|
||||
waitingTime
|
||||
)
|
||||
urlBar().click()
|
||||
mDevice.waitNotNull(
|
||||
Until.findObject(By.res("org.mozilla.fenix.debug:id/mozac_browser_toolbar_edit_url_view")),
|
||||
waitingTime
|
||||
)
|
||||
clearAddressBar().click()
|
||||
awesomeBar().check((matches(withText(containsString("")))))
|
||||
goBackButton()
|
||||
|
||||
BrowserRobot().interact()
|
||||
return BrowserRobot.Transition()
|
||||
}
|
||||
|
||||
fun enterURLAndEnterToBrowser(
|
||||
url: Uri,
|
||||
interact: BrowserRobot.() -> Unit
|
||||
|
|
|
@ -34,6 +34,7 @@ import mozilla.components.browser.session.SessionManager
|
|||
import mozilla.components.browser.state.state.WebExtensionState
|
||||
import mozilla.components.browser.state.store.BrowserStore
|
||||
import mozilla.components.browser.tabstray.BrowserTabsTray
|
||||
import mozilla.components.browser.thumbnails.loader.ThumbnailLoader
|
||||
import mozilla.components.concept.engine.EngineView
|
||||
import mozilla.components.concept.tabstray.TabsTray
|
||||
import mozilla.components.feature.contextmenu.DefaultSelectionActionDelegate
|
||||
|
@ -242,13 +243,10 @@ open class HomeActivity : LocaleAwareAppCompatActivity() {
|
|||
stackFromEnd = true
|
||||
}
|
||||
|
||||
val adapter = FenixTabsAdapter(context)
|
||||
BrowserTabsTray(
|
||||
context,
|
||||
attrs,
|
||||
tabsAdapter = adapter,
|
||||
layout = layout
|
||||
)
|
||||
val thumbnailLoader = ThumbnailLoader(components.core.thumbnailStorage)
|
||||
val adapter = FenixTabsAdapter(context, thumbnailLoader)
|
||||
|
||||
BrowserTabsTray(context, attrs, 0, adapter, layout)
|
||||
}
|
||||
else -> super.onCreateView(parent, name, context, attrs)
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import android.view.ViewGroup
|
|||
import androidx.annotation.CallSuper
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
|
@ -24,15 +25,19 @@ import kotlinx.android.synthetic.main.fragment_browser.*
|
|||
import kotlinx.android.synthetic.main.fragment_browser.view.*
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import mozilla.appservices.places.BookmarkRoot
|
||||
import mozilla.components.browser.session.Session
|
||||
import mozilla.components.browser.session.SessionManager
|
||||
import mozilla.components.browser.session.runWithSessionIdOrSelected
|
||||
import mozilla.components.browser.state.action.ContentAction
|
||||
import mozilla.components.browser.state.selector.findTabOrCustomTabOrSelectedTab
|
||||
import mozilla.components.browser.state.state.SessionState
|
||||
import mozilla.components.browser.state.state.content.DownloadState
|
||||
import mozilla.components.browser.state.store.BrowserStore
|
||||
import mozilla.components.browser.thumbnails.BrowserThumbnails
|
||||
|
@ -59,12 +64,14 @@ import mozilla.components.feature.session.behavior.EngineViewBottomBehavior
|
|||
import mozilla.components.feature.sitepermissions.SitePermissions
|
||||
import mozilla.components.feature.sitepermissions.SitePermissionsFeature
|
||||
import mozilla.components.feature.sitepermissions.SitePermissionsRules
|
||||
import mozilla.components.lib.state.ext.flowScoped
|
||||
import mozilla.components.service.sync.logins.DefaultLoginValidationDelegate
|
||||
import mozilla.components.support.base.feature.PermissionsFeature
|
||||
import mozilla.components.support.base.feature.UserInteractionHandler
|
||||
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
|
||||
import mozilla.components.support.ktx.android.view.exitImmersiveModeIfNeeded
|
||||
import mozilla.components.support.ktx.android.view.hideKeyboard
|
||||
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
|
||||
import org.mozilla.fenix.FeatureFlags
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.IntentReceiverActivity
|
||||
|
@ -105,6 +112,7 @@ import java.lang.ref.WeakReference
|
|||
* This class only contains shared code focused on the main browsing content.
|
||||
* UI code specific to the app or to custom tabs can be found in the subclasses.
|
||||
*/
|
||||
@ExperimentalCoroutinesApi
|
||||
@Suppress("TooManyFunctions", "LargeClass")
|
||||
abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, SessionManager.Observer {
|
||||
protected lateinit var browserFragmentStore: BrowserFragmentStore
|
||||
|
@ -143,7 +151,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
|||
|
||||
private var browserInitialized: Boolean = false
|
||||
private var initUIJob: Job? = null
|
||||
private var enteredPip = false
|
||||
protected var webAppToolbarShouldBeVisible = true
|
||||
|
||||
private val sharedViewModel: SharedViewModel by activityViewModels()
|
||||
|
||||
|
@ -383,11 +391,10 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
|||
resumeDownloadDialogState(session, store, view, context, toolbarHeight)
|
||||
|
||||
pipFeature = PictureInPictureFeature(
|
||||
requireComponents.core.sessionManager,
|
||||
requireActivity(),
|
||||
requireComponents.analytics.crashReporter,
|
||||
customTabSessionId,
|
||||
::pipModeChanged
|
||||
store = store,
|
||||
activity = requireActivity(),
|
||||
crashReporting = context.components.analytics.crashReporter,
|
||||
tabId = customTabSessionId
|
||||
)
|
||||
|
||||
appLinksFeature.set(
|
||||
|
@ -528,6 +535,12 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
|||
}
|
||||
}, owner = viewLifecycleOwner)
|
||||
|
||||
store.flowScoped(viewLifecycleOwner) { flow ->
|
||||
flow.mapNotNull { state -> state.findTabOrCustomTabOrSelectedTab(customTabSessionId) }
|
||||
.ifChanged { tab -> tab.content.pictureInPictureEnabled }
|
||||
.collect { tab -> pipModeChanged(tab) }
|
||||
}
|
||||
|
||||
@Suppress("ConstantConditionIf")
|
||||
if (FeatureFlags.pullToRefreshEnabled) {
|
||||
val primaryTextColor =
|
||||
|
@ -694,11 +707,11 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
|||
@CallSuper
|
||||
final override fun onPause() {
|
||||
super.onPause()
|
||||
val session = requireComponents.core.store.state.findTabOrCustomTabOrSelectedTab(customTabSessionId)
|
||||
// If we didn't enter PiP, exit full screen on pause
|
||||
if (!enteredPip && fullScreenFeature.onBackPressed()) {
|
||||
if (session?.content?.pictureInPictureEnabled == false && fullScreenFeature.onBackPressed()) {
|
||||
fullScreenChanged(false)
|
||||
}
|
||||
enteredPip = false
|
||||
if (findNavController().currentDestination?.id != R.id.searchFragment) {
|
||||
view?.hideKeyboard()
|
||||
}
|
||||
|
@ -894,21 +907,13 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
|||
}
|
||||
}
|
||||
|
||||
override fun onHomePressed(): Boolean {
|
||||
if (pipFeature?.onHomePressed() == true) {
|
||||
enteredPip = true
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
override fun onHomePressed() = pipFeature?.onHomePressed() ?: false
|
||||
|
||||
private fun pipModeChanged(enabled: Boolean) {
|
||||
val fullScreenMode =
|
||||
requireComponents.core.sessionManager.runWithSessionIdOrSelected(customTabSessionId) { session ->
|
||||
session.fullScreenMode
|
||||
}
|
||||
// If we're exiting PIP mode and we're in fullscreen mode, then we should exit fullscreen mode as well.
|
||||
if (!enabled && fullScreenMode) {
|
||||
/**
|
||||
* Exit fullscreen mode when exiting PIP mode
|
||||
*/
|
||||
private fun pipModeChanged(session: SessionState) {
|
||||
if (!session.content.pictureInPictureEnabled && session.content.fullScreen) {
|
||||
onBackPressed()
|
||||
fullScreenChanged(false)
|
||||
}
|
||||
|
@ -938,7 +943,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
|||
.setText(getString(R.string.full_screen_notification))
|
||||
.show()
|
||||
activity?.enterToImmersiveMode()
|
||||
browserToolbarView.view.visibility = View.GONE
|
||||
browserToolbarView.view.isVisible = false
|
||||
|
||||
engineView.setDynamicToolbarMaxHeight(0)
|
||||
browserToolbarView.expand()
|
||||
|
@ -949,19 +954,14 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
|||
(activity as? HomeActivity)?.let { activity ->
|
||||
activity.themeManager.applyStatusBarTheme(activity)
|
||||
}
|
||||
browserToolbarView.view.visibility = View.VISIBLE
|
||||
val toolbarHeight = resources.getDimensionPixelSize(R.dimen.browser_toolbar_height)
|
||||
engineView.setDynamicToolbarMaxHeight(toolbarHeight)
|
||||
if (webAppToolbarShouldBeVisible) {
|
||||
browserToolbarView.view.isVisible = true
|
||||
val toolbarHeight = resources.getDimensionPixelSize(R.dimen.browser_toolbar_height)
|
||||
engineView.setDynamicToolbarMaxHeight(toolbarHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getListOfSessions(
|
||||
private: Boolean = (activity as HomeActivity).browsingModeManager.mode.isPrivate
|
||||
): List<Session> {
|
||||
return requireComponents.core.sessionManager.sessionsOfType(private = private)
|
||||
.toList()
|
||||
}
|
||||
|
||||
/*
|
||||
* Dereference these views when the fragment view is destroyed to prevent memory leaks
|
||||
*/
|
||||
|
|
|
@ -14,7 +14,6 @@ import androidx.core.content.ContextCompat
|
|||
import androidx.lifecycle.Observer
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.android.synthetic.main.fragment_browser.*
|
||||
import kotlinx.android.synthetic.main.fragment_browser.view.*
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import mozilla.components.browser.session.Session
|
||||
|
|
|
@ -7,6 +7,7 @@ package org.mozilla.fenix.components
|
|||
import android.content.Context
|
||||
import android.os.StrictMode
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.asLiveData
|
||||
import androidx.paging.DataSource
|
||||
import mozilla.components.browser.session.Session
|
||||
import mozilla.components.browser.session.SessionManager
|
||||
|
@ -71,7 +72,7 @@ class TabCollectionStorage(
|
|||
}
|
||||
|
||||
fun getCollections(limit: Int = 20): LiveData<List<TabCollection>> {
|
||||
return collectionStorage.getCollections(limit)
|
||||
return collectionStorage.getCollections(limit).asLiveData()
|
||||
}
|
||||
|
||||
fun getCollectionsPaged(): DataSource.Factory<Int, TabCollection> {
|
||||
|
|
|
@ -6,6 +6,7 @@ package org.mozilla.fenix.components
|
|||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.asLiveData
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -41,7 +42,7 @@ class TopSiteStorage(private val context: Context) {
|
|||
* Returns a [LiveData] list of all the [TopSite] instances.
|
||||
*/
|
||||
fun getTopSites(): LiveData<List<TopSite>> {
|
||||
return storage.getTopSites()
|
||||
return storage.getTopSites().asLiveData()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -147,7 +147,7 @@ class DefaultBrowserToolbarController(
|
|||
if (sessionManager.sessionsOfType(it.private).count() == 1) {
|
||||
// The tab tray always returns to normal mode so do that here too
|
||||
(activity as HomeActivity).browsingModeManager.mode = BrowsingMode.Normal
|
||||
navController.navigate(BrowserFragmentDirections.actionGlobalHome(it.id))
|
||||
navController.navigate(BrowserFragmentDirections.actionGlobalHome(sessionToDelete = it.id))
|
||||
} else {
|
||||
onCloseTab.invoke(it)
|
||||
activity.components.useCases.tabsUseCases.removeTab.invoke(it)
|
||||
|
|
|
@ -7,6 +7,7 @@ package org.mozilla.fenix.customtabs
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import kotlinx.android.synthetic.main.component_browser_top_toolbar.*
|
||||
import kotlinx.android.synthetic.main.fragment_browser.*
|
||||
|
@ -17,15 +18,12 @@ import mozilla.components.concept.engine.manifest.WebAppManifestParser
|
|||
import mozilla.components.concept.engine.manifest.getOrNull
|
||||
import mozilla.components.feature.contextmenu.ContextMenuCandidate
|
||||
import mozilla.components.feature.customtabs.CustomTabWindowFeature
|
||||
import mozilla.components.feature.pwa.ext.getTrustedScope
|
||||
import mozilla.components.feature.pwa.ext.trustedOrigins
|
||||
import mozilla.components.feature.pwa.feature.ManifestUpdateFeature
|
||||
import mozilla.components.feature.pwa.feature.WebAppActivityFeature
|
||||
import mozilla.components.feature.pwa.feature.WebAppHideToolbarFeature
|
||||
import mozilla.components.feature.pwa.feature.WebAppSiteControlsFeature
|
||||
import mozilla.components.feature.session.TrackingProtectionUseCases
|
||||
import mozilla.components.feature.sitepermissions.SitePermissions
|
||||
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
|
||||
|
@ -60,7 +58,6 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), UserInteractionHandler
|
|||
val manifest = args.webAppManifest?.let { json ->
|
||||
WebAppManifestParser().parse(json).getOrNull()
|
||||
}
|
||||
val trustedScopes = listOfNotNull(manifest?.getTrustedScope())
|
||||
|
||||
customTabSessionId?.let { customTabSessionId ->
|
||||
customTabsIntegration.set(
|
||||
|
@ -99,11 +96,13 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), UserInteractionHandler
|
|||
|
||||
hideToolbarFeature.set(
|
||||
feature = WebAppHideToolbarFeature(
|
||||
requireComponents.core.sessionManager,
|
||||
toolbar,
|
||||
customTabSessionId,
|
||||
trustedScopes
|
||||
store = requireComponents.core.store,
|
||||
customTabsStore = requireComponents.core.customTabsStore,
|
||||
tabId = customTabSessionId,
|
||||
manifest = manifest
|
||||
) { toolbarVisible ->
|
||||
browserToolbarView.view.isVisible = toolbarVisible
|
||||
webAppToolbarShouldBeVisible = toolbarVisible
|
||||
if (!toolbarVisible) { engineView.setDynamicToolbarMaxHeight(0) }
|
||||
},
|
||||
owner = this,
|
||||
|
@ -151,17 +150,6 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), UserInteractionHandler
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
consumeFrom(components.core.customTabsStore) { state ->
|
||||
getSessionById()
|
||||
?.let { session -> session.customTabConfig?.sessionToken }
|
||||
?.let { token -> state.tabs[token] }
|
||||
?.let { tabState ->
|
||||
hideToolbarFeature.withFeature {
|
||||
it.onTrustedScopesChange(tabState.trustedOrigins)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import android.view.Gravity
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.accessibility.AccessibilityEvent
|
||||
import android.widget.Button
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.PopupWindow
|
||||
|
@ -107,12 +108,12 @@ import kotlin.math.min
|
|||
|
||||
@SuppressWarnings("TooManyFunctions", "LargeClass")
|
||||
class HomeFragment : Fragment() {
|
||||
private val args by navArgs<HomeFragmentArgs>()
|
||||
|
||||
private val homeViewModel: HomeScreenViewModel by viewModels {
|
||||
ViewModelProvider.AndroidViewModelFactory(requireActivity().application)
|
||||
}
|
||||
|
||||
private val args by navArgs<HomeFragmentArgs>()
|
||||
|
||||
private val snackbarAnchorView: View?
|
||||
get() {
|
||||
return if (requireContext().settings().shouldUseBottomToolbar) {
|
||||
|
@ -359,6 +360,15 @@ class HomeFragment : Fragment() {
|
|||
SearchWidgetCFR(view.context) { view.toolbar_wrapper }.displayIfNecessary()
|
||||
}
|
||||
}
|
||||
|
||||
if (view.context.settings().accessibilityServicesEnabled && args.focusOnAddressBar) {
|
||||
// We cannot put this in the fragment_home.xml file as it breaks tests
|
||||
view.toolbar_wrapper.isFocusableInTouchMode = true
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
view.toolbar_wrapper?.requestFocus()
|
||||
view.toolbar_wrapper?.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
|
|
|
@ -21,8 +21,8 @@ import org.mozilla.fenix.home.OnboardingState
|
|||
import org.mozilla.fenix.components.tips.Tip
|
||||
|
||||
val noCollectionMessage = AdapterItem.NoContentMessage(
|
||||
R.string.no_collections_header,
|
||||
R.string.collections_description
|
||||
R.string.no_collections_header1,
|
||||
R.string.no_collections_description1
|
||||
)
|
||||
|
||||
// This method got a little complex with the addition of the tab tray feature flag
|
||||
|
|
|
@ -8,17 +8,20 @@ import android.content.Context
|
|||
import android.view.LayoutInflater
|
||||
import mozilla.components.browser.tabstray.TabsAdapter
|
||||
import mozilla.components.concept.tabstray.Tabs
|
||||
import mozilla.components.support.images.loader.ImageLoader
|
||||
import org.mozilla.fenix.R
|
||||
|
||||
class FenixTabsAdapter(
|
||||
context: Context
|
||||
context: Context,
|
||||
imageLoader: ImageLoader
|
||||
) : TabsAdapter(
|
||||
viewHolderProvider = { parentView, _ ->
|
||||
TabTrayViewHolder(
|
||||
LayoutInflater.from(context).inflate(
|
||||
R.layout.tab_tray_item,
|
||||
parentView,
|
||||
false)
|
||||
false),
|
||||
imageLoader
|
||||
)
|
||||
}
|
||||
) {
|
||||
|
|
|
@ -110,7 +110,8 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), TabTrayInteractor {
|
|||
view.tabLayout,
|
||||
this,
|
||||
isPrivate,
|
||||
requireContext().resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
|
||||
requireContext().resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE,
|
||||
viewLifecycleOwner.lifecycleScope
|
||||
) { tabsFeature.get()?.filterTabs(it) }
|
||||
|
||||
tabsFeature.set(
|
||||
|
@ -196,7 +197,7 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), TabTrayInteractor {
|
|||
|
||||
override fun onNewTabTapped(private: Boolean) {
|
||||
(activity as HomeActivity).browsingModeManager.mode = BrowsingMode.fromBoolean(private)
|
||||
findNavController().popBackStack(R.id.homeFragment, false)
|
||||
findNavController().navigate(TabTrayDialogFragmentDirections.actionGlobalHome(focusOnAddressBar = true))
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
|
||||
|
@ -311,7 +312,9 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), TabTrayInteractor {
|
|||
.setText(requireContext().getString(R.string.create_collection_tabs_saved))
|
||||
.setAction(requireContext().getString(R.string.create_collection_view)) {
|
||||
dismissAllowingStateLoss()
|
||||
findNavController().navigate(TabTrayDialogFragmentDirections.actionGlobalHome())
|
||||
findNavController().navigate(
|
||||
TabTrayDialogFragmentDirections.actionGlobalHome(focusOnAddressBar = false)
|
||||
)
|
||||
}
|
||||
|
||||
snackbar.view.elevation = ELEVATION
|
||||
|
|
|
@ -8,14 +8,19 @@ import android.content.Context
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.accessibility.AccessibilityEvent
|
||||
import androidx.cardview.widget.CardView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.LifecycleCoroutineScope
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.component_tabstray.view.*
|
||||
import kotlinx.android.synthetic.main.component_tabstray_fab.view.*
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import mozilla.components.browser.menu.BrowserMenuBuilder
|
||||
import mozilla.components.browser.menu.item.SimpleBrowserMenuItem
|
||||
import mozilla.components.browser.state.selector.normalTabs
|
||||
|
@ -25,6 +30,7 @@ import mozilla.components.browser.state.state.TabSessionState
|
|||
import mozilla.components.browser.tabstray.BrowserTabsTray
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.settings
|
||||
|
||||
interface TabTrayInteractor {
|
||||
fun onNewTabTapped(private: Boolean)
|
||||
|
@ -36,11 +42,13 @@ interface TabTrayInteractor {
|
|||
/**
|
||||
* View that contains and configures the BrowserAwesomeBar
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
class TabTrayView(
|
||||
private val container: ViewGroup,
|
||||
private val interactor: TabTrayInteractor,
|
||||
isPrivate: Boolean,
|
||||
startingInLandscape: Boolean,
|
||||
lifecycleScope: LifecycleCoroutineScope,
|
||||
private val filterTabs: ((TabSessionState) -> Boolean) -> Unit
|
||||
) : LayoutContainer, TabLayout.OnTabSelectedListener {
|
||||
val fabView = LayoutInflater.from(container.context)
|
||||
|
@ -61,14 +69,18 @@ class TabTrayView(
|
|||
get() = container
|
||||
|
||||
init {
|
||||
val hasAccessibilityEnabled = view.context.settings().accessibilityServicesEnabled
|
||||
|
||||
toggleFabText(isPrivate)
|
||||
|
||||
behavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
|
||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {
|
||||
if (slideOffset >= SLIDE_OFFSET) {
|
||||
fabView.new_tab_button.show()
|
||||
} else {
|
||||
fabView.new_tab_button.hide()
|
||||
if (!hasAccessibilityEnabled) {
|
||||
if (slideOffset >= SLIDE_OFFSET) {
|
||||
fabView.new_tab_button.show()
|
||||
} else {
|
||||
fabView.new_tab_button.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,6 +125,19 @@ class TabTrayView(
|
|||
if (!hasLoaded) {
|
||||
hasLoaded = true
|
||||
tray.layoutManager?.scrollToPosition(selectedBrowserTabIndex)
|
||||
if (view.context.settings().accessibilityServicesEnabled) {
|
||||
lifecycleScope.launch {
|
||||
delay(SELECTION_DELAY.toLong())
|
||||
lifecycleScope.launch(Main) {
|
||||
tray.layoutManager?.findViewByPosition(selectedBrowserTabIndex)
|
||||
?.requestFocus()
|
||||
tray.layoutManager?.findViewByPosition(selectedBrowserTabIndex)
|
||||
?.sendAccessibilityEvent(
|
||||
AccessibilityEvent.TYPE_VIEW_FOCUSED
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -142,8 +167,18 @@ class TabTrayView(
|
|||
}
|
||||
}
|
||||
|
||||
fabView.new_tab_button.setOnClickListener {
|
||||
interactor.onNewTabTapped(isPrivateModeSelected)
|
||||
view.tab_tray_new_tab.apply {
|
||||
isVisible = hasAccessibilityEnabled
|
||||
setOnClickListener {
|
||||
interactor.onNewTabTapped(isPrivateModeSelected)
|
||||
}
|
||||
}
|
||||
|
||||
fabView.new_tab_button.apply {
|
||||
isVisible = !hasAccessibilityEnabled
|
||||
setOnClickListener {
|
||||
interactor.onNewTabTapped(isPrivateModeSelected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -214,6 +249,7 @@ class TabTrayView(
|
|||
private const val PRIVATE_TAB_ID = 1
|
||||
private const val EXPAND_AT_SIZE = 3
|
||||
private const val SLIDE_OFFSET = 0
|
||||
private const val SELECTION_DELAY = 500
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ package org.mozilla.fenix.tabtray
|
|||
|
||||
import android.view.View
|
||||
import android.widget.ImageButton
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.appcompat.widget.AppCompatImageButton
|
||||
|
@ -20,6 +19,7 @@ import mozilla.components.concept.tabstray.TabsTray
|
|||
import mozilla.components.feature.media.ext.pauseIfPlaying
|
||||
import mozilla.components.feature.media.ext.playIfPaused
|
||||
import mozilla.components.support.base.observer.Observable
|
||||
import mozilla.components.support.images.loader.ImageLoader
|
||||
import mozilla.components.support.ktx.kotlin.tryGetHostFromUrl
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
|
@ -35,9 +35,9 @@ import org.mozilla.fenix.ext.toTab
|
|||
*/
|
||||
class TabTrayViewHolder(
|
||||
itemView: View,
|
||||
private val imageLoader: ImageLoader,
|
||||
val getSelectedTabId: () -> String? = { itemView.context.components.core.store.state.selectedTabId }
|
||||
) : TabViewHolder(itemView) {
|
||||
private val iconView: ImageView? = itemView.findViewById(R.id.mozac_browser_tabstray_icon)
|
||||
private val titleView: TextView = itemView.findViewById(R.id.mozac_browser_tabstray_title)
|
||||
private val closeView: AppCompatImageButton =
|
||||
itemView.findViewById(R.id.mozac_browser_tabstray_close)
|
||||
|
@ -69,13 +69,8 @@ class TabTrayViewHolder(
|
|||
|
||||
if (tab.thumbnail != null) {
|
||||
thumbnailView.setImageBitmap(tab.thumbnail)
|
||||
thumbnailView.visibility = View.VISIBLE
|
||||
iconView?.visibility = View.INVISIBLE
|
||||
} else {
|
||||
thumbnailView.setImageBitmap(null)
|
||||
iconView?.setImageBitmap(tab.icon)
|
||||
thumbnailView.visibility = View.INVISIBLE
|
||||
iconView?.visibility = View.VISIBLE
|
||||
imageLoader.loadIntoView(thumbnailView, tab.id)
|
||||
}
|
||||
|
||||
// Media state
|
||||
|
|
|
@ -70,6 +70,18 @@
|
|||
|
||||
</com.google.android.material.tabs.TabLayout>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/tab_tray_new_tab"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:visibility="gone"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/add_tab"
|
||||
app:srcCompat="@drawable/ic_new"
|
||||
app:layout_constraintTop_toTopOf="@id/tab_layout"
|
||||
app:layout_constraintEnd_toStartOf="@id/tab_tray_overflow"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tab_layout" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/tab_tray_overflow"
|
||||
android:layout_width="48dp"
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/tab_item"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="88dp">
|
||||
android:layout_height="88dp"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/play_pause_button"
|
||||
|
@ -33,15 +35,6 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/mozac_browser_tabstray_icon"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="28dp"
|
||||
android:layout_marginStart="21dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:visibility="invisible" />
|
||||
|
||||
<mozilla.components.browser.tabstray.thumbnail.TabThumbnailView
|
||||
android:id="@+id/mozac_browser_tabstray_thumbnail"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -66,6 +66,11 @@
|
|||
app:destination="@id/browserFragment"
|
||||
app:exitAnim="@anim/zoom_in_fade"
|
||||
app:popEnterAnim="@anim/zoom_out_fade" />
|
||||
|
||||
<argument
|
||||
android:name="focusOnAddressBar"
|
||||
android:defaultValue="false"
|
||||
app:argType="boolean" />
|
||||
<argument
|
||||
android:name="session_to_delete"
|
||||
app:argType="string"
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
<string name="content_description_disable_private_browsing_button">Disable private browsing</string>
|
||||
<!-- Placeholder text shown in the search bar before a user enters text -->
|
||||
<string name="search_hint">Search or enter address</string>
|
||||
<!-- No Open Tabs Message Header -->
|
||||
<string name="no_open_tabs_header_2">No open tabs</string>
|
||||
<!-- No Open Tabs Message Description -->
|
||||
<string name="no_open_tabs_description">Your open tabs will be shown here.</string>
|
||||
<!-- No Private Tabs Message Description -->
|
||||
|
@ -477,7 +475,7 @@
|
|||
<!-- Open tabs menu item to share all tabs -->
|
||||
<string name="tabs_menu_share_tabs">Share tabs</string>
|
||||
<!-- Open tabs menu item to save tabs to collection -->
|
||||
<string name="tabs_menu_save_to_collection">Save to collection</string>
|
||||
<string name="tabs_menu_save_to_collection1">Save tabs to collection</string>
|
||||
<!-- Content description (not visible, for screen readers etc.): Opens the tab menu when pressed -->
|
||||
<string name="tab_menu">Tab menu</string>
|
||||
<!-- Tab menu item to share the tab -->
|
||||
|
@ -686,16 +684,14 @@
|
|||
<string name="delete_browsing_data_quit_off">Off</string>
|
||||
|
||||
<!-- Collections -->
|
||||
<!-- Label to describe what collections are to a new user without any collections -->
|
||||
<string name="collections_description">Collect the things that matter to you. To start, save open tabs to a new collection.</string>
|
||||
<!-- Collections header on home fragment -->
|
||||
<string name="collections_header">Collections</string>
|
||||
<!-- Content description (not visible, for screen readers etc.): Opens the collection menu when pressed -->
|
||||
<string name="collection_menu_button_content_description">Collection menu</string>
|
||||
<!-- No Open Tabs Message Header -->
|
||||
<string name="no_collections_header">No collections</string>
|
||||
<!-- No Open Tabs Message Description -->
|
||||
<string name="no_collections_description">Your collections will be shown here.</string>
|
||||
<string name="no_collections_header1">Collect the things that matter to you</string>
|
||||
<!-- Label to describe what collections are to a new user without any collections -->
|
||||
<string name="no_collections_description1">Group together similar searches, sites, and tabs for quick access later.</string>
|
||||
<!-- Title for the "select tabs" step of the collection creator -->
|
||||
<string name="create_collection_select_tabs">Select Tabs</string>
|
||||
<!-- Title for the "select collection" step of the collection creator -->
|
||||
|
|
|
@ -569,7 +569,7 @@ class DefaultBrowserToolbarControllerTest {
|
|||
every { activity.browsingModeManager } returns browsingModeManager
|
||||
|
||||
controller.handleTabCounterItemInteraction(item)
|
||||
verify { navController.navigate(BrowserFragmentDirections.actionGlobalHome("1")) }
|
||||
verify { navController.navigate(BrowserFragmentDirections.actionGlobalHome(sessionToDelete = "1")) }
|
||||
assertEquals(BrowsingMode.Normal, browsingModeManager.mode)
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import io.mockk.mockk
|
|||
import io.mockk.spyk
|
||||
import mozilla.components.browser.toolbar.MAX_URI_LENGTH
|
||||
import mozilla.components.concept.tabstray.Tab
|
||||
import mozilla.components.support.images.loader.ImageLoader
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
@ -25,15 +26,18 @@ class TabTrayViewHolderTest {
|
|||
@Test
|
||||
fun `extremely long URLs are truncated to prevent slowing down the UI`() {
|
||||
val view = LayoutInflater.from(ApplicationProvider.getApplicationContext()).inflate(
|
||||
R.layout.tab_tray_item, null, false)
|
||||
|
||||
val tabViewHolder = spyk(TabTrayViewHolder(view) { null })
|
||||
R.layout.tab_tray_item, null, false
|
||||
)
|
||||
val imageLoader: ImageLoader = mockk()
|
||||
every { imageLoader.loadIntoView(any(), any(), any(), any()) } just Runs
|
||||
val tabViewHolder = spyk(TabTrayViewHolder(view, imageLoader) { null })
|
||||
every { tabViewHolder.updateBackgroundColor(false) } just Runs
|
||||
|
||||
val extremelyLongUrl = "m".repeat(MAX_URI_LENGTH + 1)
|
||||
val tab = Tab(
|
||||
id = "123",
|
||||
url = extremelyLongUrl)
|
||||
url = extremelyLongUrl
|
||||
)
|
||||
tabViewHolder.bind(tab, false, mockk())
|
||||
|
||||
assertEquals("m".repeat(MAX_URI_LENGTH), tabViewHolder.urlView?.text)
|
||||
|
|
|
@ -3,5 +3,5 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
object AndroidComponents {
|
||||
const val VERSION = "45.0.20200610130052"
|
||||
const val VERSION = "45.0.20200613130120"
|
||||
}
|
||||
|
|
|
@ -133,6 +133,7 @@ object Deps {
|
|||
const val mozilla_ui_publicsuffixlist = "org.mozilla.components:lib-publicsuffixlist:${Versions.mozilla_android_components}"
|
||||
|
||||
const val mozilla_support_base = "org.mozilla.components:support-base:${Versions.mozilla_android_components}"
|
||||
const val mozilla_support_images = "org.mozilla.components:support-images:${Versions.mozilla_android_components}"
|
||||
const val mozilla_support_ktx = "org.mozilla.components:support-ktx:${Versions.mozilla_android_components}"
|
||||
const val mozilla_support_rusthttp = "org.mozilla.components:support-rusthttp:${Versions.mozilla_android_components}"
|
||||
const val mozilla_support_rustlog = "org.mozilla.components:support-rustlog:${Versions.mozilla_android_components}"
|
||||
|
|
Loading…
Reference in New Issue