Merge pull request #4 from fork-maintainers/fork

Upstream update
pull/91/head
bbigam 4 years ago committed by GitHub
commit 19253cb693
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -13,9 +13,7 @@ assignees: ''
### Actual behavior
### Does toggling Tracking Protection fix the issue? (Press the lock icon in the toolbar while on the site to see toggle)
### Can you reproduce in [Firefox for Android](https://play.google.com/store/apps/details?id=org.mozilla.firefox)?
### Does toggling Tracking Protection fix the issue? (Press the shield icon in the toolbar while on the site to see toggle)
### Can you reproduce in Chrome (or other non-Mozilla browser)?
<!--- Note: If you can reproduce the same issue in Firefox for Android AND Chrome, this issue is very unlikely to be fixed by our teams. -->

@ -83,9 +83,6 @@ Therefore, everyone should feel free to open issues and pull requests. Join the
Developers are especially welcome, wanted, and needed.
We are also [looking for maintainers](https://github.com/interfect/fenix/issues/23).
## I want to open a Pull Request!
We encourage you to participate in this open source project. We love Pull Requests, Bug Reports, ideas, (security) code reviews or any other kind of positive contribution.

@ -6,6 +6,7 @@ package org.mozilla.fenix.ui
import android.content.Context
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
import androidx.test.espresso.IdlingRegistry
import kotlinx.coroutines.runBlocking
import mozilla.components.browser.storage.sync.PlacesHistoryStorage
import okhttp3.mockwebserver.MockWebServer
@ -13,8 +14,10 @@ import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mozilla.fenix.R
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
import org.mozilla.fenix.helpers.HomeActivityTestRule
import org.mozilla.fenix.helpers.RecyclerViewIdlingResource
import org.mozilla.fenix.helpers.TestAssetHelper
import org.mozilla.fenix.helpers.TestHelper.longTapSelectItem
import org.mozilla.fenix.ui.robots.historyMenu
@ -30,6 +33,7 @@ import org.mozilla.fenix.ui.robots.navigationToolbar
class HistoryTest {
/* ktlint-disable no-blank-line-before-rbrace */ // This imposes unreadable grouping.
private lateinit var mockWebServer: MockWebServer
private var historyListIdlingResource: RecyclerViewIdlingResource? = null
@get:Rule
val activityTestRule = HomeActivityTestRule()
@ -52,6 +56,10 @@ class HistoryTest {
runBlocking {
historyStorage.deleteEverything()
}
if (historyListIdlingResource != null) {
IdlingRegistry.getInstance().unregister(historyListIdlingResource!!)
}
}
@Test
@ -74,6 +82,9 @@ class HistoryTest {
mDevice.waitForIdle()
}.openThreeDotMenu {
}.openHistory {
historyListIdlingResource =
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list))
IdlingRegistry.getInstance().register(historyListIdlingResource!!)
verifyHistoryMenuView()
verifyVisitedTimeTitle()
verifyFirstTestPageTitle("Test_Page_1")
@ -90,6 +101,9 @@ class HistoryTest {
mDevice.waitForIdle()
}.openThreeDotMenu {
}.openHistory {
historyListIdlingResource =
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list))
IdlingRegistry.getInstance().register(historyListIdlingResource!!)
}.openThreeDotMenu {
}.clickCopy {
verifyCopySnackBarText()
@ -105,6 +119,9 @@ class HistoryTest {
mDevice.waitForIdle()
}.openThreeDotMenu {
}.openHistory {
historyListIdlingResource =
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list))
IdlingRegistry.getInstance().register(historyListIdlingResource!!)
}.openThreeDotMenu {
}.clickShare {
verifyShareOverlay()
@ -123,6 +140,9 @@ class HistoryTest {
mDevice.waitForIdle()
}.openThreeDotMenu {
}.openHistory {
historyListIdlingResource =
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list))
IdlingRegistry.getInstance().register(historyListIdlingResource!!)
}.openThreeDotMenu {
}.clickOpenInNormalTab {
verifyUrl(firstWebPage.url.toString())
@ -140,6 +160,9 @@ class HistoryTest {
mDevice.waitForIdle()
}.openThreeDotMenu {
}.openHistory {
historyListIdlingResource =
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list))
IdlingRegistry.getInstance().register(historyListIdlingResource!!)
}.openThreeDotMenu {
}.clickOpenInPrivateTab {
verifyUrl(firstWebPage.url.toString())
@ -157,7 +180,11 @@ class HistoryTest {
mDevice.waitForIdle()
}.openThreeDotMenu {
}.openHistory {
historyListIdlingResource =
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list))
IdlingRegistry.getInstance().register(historyListIdlingResource!!)
}.openThreeDotMenu {
IdlingRegistry.getInstance().unregister(historyListIdlingResource!!)
}.clickDelete {
verifyDeleteSnackbarText("Deleted")
verifyEmptyHistoryView()
@ -173,7 +200,11 @@ class HistoryTest {
mDevice.waitForIdle()
}.openThreeDotMenu {
}.openHistory {
historyListIdlingResource =
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list))
IdlingRegistry.getInstance().register(historyListIdlingResource!!)
clickDeleteHistoryButton()
IdlingRegistry.getInstance().unregister(historyListIdlingResource!!)
verifyDeleteConfirmationMessage()
confirmDeleteAllHistory()
verifyDeleteSnackbarText("Browsing data deleted")
@ -190,6 +221,9 @@ class HistoryTest {
mDevice.waitForIdle()
}.openThreeDotMenu {
}.openHistory {
historyListIdlingResource =
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list))
IdlingRegistry.getInstance().register(historyListIdlingResource!!)
longTapSelectItem(firstWebPage.url)
}
@ -216,6 +250,9 @@ class HistoryTest {
homeScreen { }.openThreeDotMenu {
}.openHistory {
historyListIdlingResource =
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list))
IdlingRegistry.getInstance().register(historyListIdlingResource!!)
longTapSelectItem(firstWebPage.url)
openActionBarOverflowOrOptionsMenu(activityTestRule.activity)
}
@ -236,6 +273,9 @@ class HistoryTest {
mDevice.waitForIdle()
}.openThreeDotMenu {
}.openHistory {
historyListIdlingResource =
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list))
IdlingRegistry.getInstance().register(historyListIdlingResource!!)
longTapSelectItem(firstWebPage.url)
openActionBarOverflowOrOptionsMenu(activityTestRule.activity)
}
@ -259,9 +299,13 @@ class HistoryTest {
}.submitQuery(secondWebPage.url.toString()) {
}.openThreeDotMenu {
}.openHistory {
historyListIdlingResource =
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list), 1)
IdlingRegistry.getInstance().register(historyListIdlingResource!!)
longTapSelectItem(firstWebPage.url)
longTapSelectItem(secondWebPage.url)
openActionBarOverflowOrOptionsMenu(activityTestRule.activity)
IdlingRegistry.getInstance().unregister(historyListIdlingResource!!)
}
multipleSelectionToolbar {
@ -282,6 +326,9 @@ class HistoryTest {
mDevice.waitForIdle()
}.openThreeDotMenu {
}.openHistory {
historyListIdlingResource =
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list))
IdlingRegistry.getInstance().register(historyListIdlingResource!!)
longTapSelectItem(firstWebPage.url)
}

@ -8,7 +8,6 @@ import androidx.test.uiautomator.UiSelector
import okhttp3.mockwebserver.MockWebServer
import org.junit.After
import org.junit.Before
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
@ -61,6 +60,7 @@ class MediaNotificationTest {
}.enterURLAndEnterToBrowser(videoTestPage.url) {
mDevice.waitForIdle()
clickMediaPlayerPlayButton()
waitForPlaybackToStart()
}.openNotificationShade {
verifySystemNotificationExists(videoTestPage.title)
clickMediaSystemNotificationControlButton("Pause")
@ -136,7 +136,6 @@ class MediaNotificationTest {
}
}
@Ignore("Flaky test, temp disabled: https://github.com/mozilla-mobile/fenix/issues/12645")
@Test
fun mediaSystemNotificationInPrivateModeTest() {
val audioTestPage = TestAssetHelper.getAudioPageAsset(mockWebServer)
@ -160,6 +159,7 @@ class MediaNotificationTest {
verifyMediaIsPaused()
}.openTabDrawer {
closeTab()
verifySnackBarText("Private tab closed")
}
mDevice.openNotification()

@ -12,14 +12,12 @@ 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.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.matcher.BundleMatchers
import androidx.test.espresso.intent.matcher.IntentMatchers
import androidx.test.espresso.matcher.RootMatchers.isDialog
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
import androidx.test.espresso.matcher.ViewMatchers.Visibility
import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
@ -33,7 +31,6 @@ import androidx.test.uiautomator.By.text
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiSelector
import androidx.test.uiautomator.Until
import androidx.test.uiautomator.Until.hasObject
import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.CoreMatchers.containsString
import org.junit.Assert.assertTrue
@ -328,14 +325,7 @@ class BrowserRobot {
}
fun clickMediaPlayerPlayButton() {
mDevice.waitNotNull(
hasObject(
By
.clazz("android.widget.Button")
.textContains("Play")
),
waitingTime
)
mediaPlayerPlayButton().waitForExists(waitingTime)
mediaPlayerPlayButton().click()
}
@ -459,9 +449,9 @@ private fun tabsCounter() = onView(withId(R.id.counter_box))
private fun mediaPlayerPlayButton() =
mDevice.findObject(
By
.clazz("android.widget.Button")
.textContains("Play")
UiSelector()
.className("android.widget.Button")
.text("Play")
)
private fun assertBlueDot() {

@ -29,10 +29,12 @@ import androidx.test.uiautomator.UiObject
import androidx.test.uiautomator.UiSelector
import androidx.test.uiautomator.Until
import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.CoreMatchers.anyOf
import org.hamcrest.CoreMatchers.startsWith
import org.hamcrest.Matchers
import org.junit.Assert.assertEquals
import org.mozilla.fenix.R
import org.mozilla.fenix.helpers.SessionLoadedIdlingResource
import org.mozilla.fenix.helpers.TestAssetHelper
import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime
import org.mozilla.fenix.helpers.click
@ -122,6 +124,7 @@ class SearchRobot {
class Transition {
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
private lateinit var sessionLoadedIdlingResource: SessionLoadedIdlingResource
fun dismiss(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition {
mDevice.waitForIdle()
@ -139,9 +142,20 @@ class SearchRobot {
}
fun submitQuery(query: String, interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
sessionLoadedIdlingResource = SessionLoadedIdlingResource()
mDevice.waitForIdle()
browserToolbarEditView().perform(typeText(query + "\n"))
runWithIdleRes(sessionLoadedIdlingResource) {
onView(
anyOf(
ViewMatchers.withResourceName("browserLayout"),
ViewMatchers.withResourceName("onboarding_message") // Req ETP dialog
)
)
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
}
BrowserRobot().interact()
return BrowserRobot.Transition()
}

@ -29,7 +29,7 @@ object FeatureFlags {
/**
* Enables showing the top frequently visited sites
*/
val topFrecentSite = Config.channel.isNightlyOrDebug
const val topFrecentSite = true
/**
* Enables wait til first contentful paint

@ -452,8 +452,9 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
order["org.mozilla.geckoview.COPY"] = 2
order["CUSTOM_CONTEXT_MENU_SEARCH"] = 3
order["CUSTOM_CONTEXT_MENU_SEARCH_PRIVATELY"] = 4
order["org.mozilla.geckoview.SELECT_ALL"] = 5
order["CUSTOM_CONTEXT_MENU_SHARE"] = 6
order["org.mozilla.geckoview.PASTE"] = 5
order["org.mozilla.geckoview.SELECT_ALL"] = 6
order["CUSTOM_CONTEXT_MENU_SHARE"] = 7
return actions.sortedBy { actionName ->
// Sort the actions in our preferred order, putting "other" actions unsorted at the end

@ -38,6 +38,7 @@ import mozilla.appservices.places.BookmarkRoot
import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager
import mozilla.components.browser.state.action.ContentAction
import mozilla.components.browser.state.selector.findTab
import mozilla.components.browser.state.selector.findTabOrCustomTabOrSelectedTab
import mozilla.components.browser.state.state.SessionState
import mozilla.components.browser.state.state.content.DownloadState
@ -253,13 +254,11 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
BrowserFragmentDirections.actionGlobalTabTrayDialogFragment()
)
},
onCloseTab = {
val snapshot = sessionManager.createSessionSnapshot(it)
val state = snapshot.engineSession?.saveState()
val isSelected =
it.id == context.components.core.store.state.selectedTabId ?: false
onCloseTab = { closedSession ->
val tab = store.state.findTab(closedSession.id) ?: return@DefaultBrowserToolbarController
val isSelected = tab.id == context.components.core.store.state.selectedTabId
val snackbarMessage = if (snapshot.session.private) {
val snackbarMessage = if (tab.content.private) {
requireContext().getString(R.string.snackbar_private_tab_closed)
} else {
requireContext().getString(R.string.snackbar_tab_closed)
@ -271,9 +270,9 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
requireContext().getString(R.string.snackbar_deleted_undo),
{
sessionManager.add(
snapshot.session,
closedSession,
isSelected,
engineSessionState = state
engineSessionState = tab.engineState.engineSessionState
)
},
operation = { }
@ -295,7 +294,8 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
openInFenixIntent = openInFenixIntent,
bookmarkTapped = { viewLifecycleOwner.lifecycleScope.launch { bookmarkTapped(it) } },
scope = viewLifecycleOwner.lifecycleScope,
tabCollectionStorage = requireComponents.core.tabCollectionStorage
tabCollectionStorage = requireComponents.core.tabCollectionStorage,
topSitesStorage = requireComponents.core.topSitesStorage
)
_browserInteractor = BrowserInteractor(
@ -659,9 +659,9 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
}
}
view.swipeRefresh.isEnabled = FeatureFlags.pullToRefreshEnabled
@Suppress("ConstantConditionIf")
if (FeatureFlags.pullToRefreshEnabled) {
view.swipeRefresh.isEnabled =
FeatureFlags.pullToRefreshEnabled && context.settings().isPullToRefreshEnabledInBrowser
if (view.swipeRefresh.isEnabled) {
val primaryTextColor =
ThemeManager.resolveAttribute(R.attr.primaryText, context)
view.swipeRefresh.setColorSchemeColors(primaryTextColor)
@ -964,11 +964,9 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
private fun showQuickSettingsDialog() {
val session = getSessionById() ?: return
viewLifecycleOwner.lifecycleScope.launch(Main) {
val sitePermissions: SitePermissions? = withContext(IO) {
session.url.toUri().host?.let { host ->
val storage = requireContext().components.core.permissionStorage
storage.findSitePermissionsBy(host)
}
val sitePermissions: SitePermissions? = session.url.toUri().host?.let { host ->
val storage = requireComponents.core.permissionStorage
storage.findSitePermissionsBy(host)
}
view?.let {

@ -72,15 +72,17 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
val components = context.components
return super.initializeUI(view)?.also {
gestureLayout.addGestureListener(
ToolbarGestureHandler(
activity = requireActivity(),
contentLayout = browserLayout,
tabPreview = tabPreview,
toolbarLayout = browserToolbarView.view,
sessionManager = components.core.sessionManager
if (context.settings().isSwipeToolbarToSwitchTabsEnabled) {
gestureLayout.addGestureListener(
ToolbarGestureHandler(
activity = requireActivity(),
contentLayout = browserLayout,
tabPreview = tabPreview,
toolbarLayout = browserToolbarView.view,
sessionManager = components.core.sessionManager
)
)
)
}
val readerModeAction =
BrowserToolbar.ToggleButton(

@ -69,7 +69,7 @@ fun List<Tab>.toSessionBundle(sessionManager: SessionManager): List<Session> {
* @param metrics Controller that handles telemetry events.
* @param tabCollectionStorage Storage used to save tab collections to disk.
* @param sessionManager Used to query and serialize tabs.
* @param ioScope Coroutine scope that launches on the IO thread.
* @param scope Coroutine scope to launch coroutines.
*/
class DefaultCollectionCreationController(
private val store: CollectionCreationStore,
@ -77,7 +77,7 @@ class DefaultCollectionCreationController(
private val metrics: MetricController,
private val tabCollectionStorage: TabCollectionStorage,
private val sessionManager: SessionManager,
private val ioScope: CoroutineScope
private val scope: CoroutineScope
) : CollectionCreationController {
companion object {
@ -89,7 +89,7 @@ class DefaultCollectionCreationController(
dismiss()
val sessionBundle = tabs.toSessionBundle(sessionManager)
ioScope.launch {
scope.launch {
tabCollectionStorage.createCollection(name, sessionBundle)
}
@ -100,7 +100,7 @@ class DefaultCollectionCreationController(
override fun renameCollection(collection: TabCollection, name: String) {
dismiss()
ioScope.launch {
scope.launch {
tabCollectionStorage.renameCollection(collection, name)
}
metrics.track(Event.CollectionRenamed)
@ -130,7 +130,7 @@ class DefaultCollectionCreationController(
override fun selectCollection(collection: TabCollection, tabs: List<Tab>) {
dismiss()
val sessionBundle = tabs.toList().toSessionBundle(sessionManager)
ioScope.launch {
scope.launch {
tabCollectionStorage
.addTabsToCollection(collection, sessionBundle)
}

@ -14,9 +14,7 @@ import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.navArgs
import kotlinx.android.synthetic.main.fragment_create_collection.view.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.plus
import mozilla.components.browser.state.selector.findTab
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.TabSessionState
@ -80,7 +78,7 @@ class CollectionCreationFragment : DialogFragment() {
requireComponents.analytics.metrics,
requireComponents.core.tabCollectionStorage,
requireComponents.core.sessionManager,
ioScope = lifecycleScope + Dispatchers.IO
scope = lifecycleScope
)
)
collectionCreationView = CollectionCreationView(

@ -54,7 +54,7 @@ class Components(private val context: Context) {
core.store,
search.searchEngineManager,
core.webAppShortcutManager,
core.topSiteStorage
core.topSitesStorage
)
}
val intentProcessors by lazy {

@ -265,7 +265,7 @@ class Core(private val context: Context, private val crashReporter: CrashReporti
val pinnedSiteStorage by lazy { PinnedSiteStorage(context) }
val topSiteStorage by lazy {
val topSitesStorage by lazy {
val defaultTopSites = mutableListOf<Pair<String, String>>()
StrictMode.allowThreadDiskReads().resetPoliciesAfter {

@ -6,6 +6,8 @@ package org.mozilla.fenix.components
import android.content.Context
import androidx.paging.DataSource
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import mozilla.components.feature.sitepermissions.SitePermissions
import mozilla.components.feature.sitepermissions.SitePermissions.Status
import mozilla.components.feature.sitepermissions.SitePermissionsStorage
@ -38,11 +40,11 @@ class PermissionStorage(private val context: Context) {
return sitePermissions
}
fun findSitePermissionsBy(origin: String): SitePermissions? {
return permissionsStorage.findSitePermissionsBy(origin)
suspend fun findSitePermissionsBy(origin: String): SitePermissions? = withContext(Dispatchers.IO) {
permissionsStorage.findSitePermissionsBy(origin)
}
fun updateSitePermissions(sitePermissions: SitePermissions) {
suspend fun updateSitePermissions(sitePermissions: SitePermissions) = withContext(Dispatchers.IO) {
permissionsStorage.update(sitePermissions)
}
@ -50,11 +52,11 @@ class PermissionStorage(private val context: Context) {
return permissionsStorage.getSitePermissionsPaged()
}
fun deleteSitePermissions(sitePermissions: SitePermissions) {
suspend fun deleteSitePermissions(sitePermissions: SitePermissions) = withContext(Dispatchers.IO) {
permissionsStorage.remove(sitePermissions)
}
fun deleteAllSitePermissions() {
suspend fun deleteAllSitePermissions() = withContext(Dispatchers.IO) {
permissionsStorage.removeAll()
}
}

@ -9,6 +9,9 @@ import android.os.StrictMode
import androidx.lifecycle.LiveData
import androidx.lifecycle.asLiveData
import androidx.paging.DataSource
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager
import mozilla.components.feature.tab.collections.Tab
@ -49,6 +52,7 @@ class TabCollectionStorage(
fun onCollectionRenamed(tabCollection: TabCollection, title: String) = Unit
}
private val ioScope = CoroutineScope(Dispatchers.IO)
var cachedTabCollections = listOf<TabCollection>()
private val collectionStorage by lazy {
@ -57,15 +61,15 @@ class TabCollectionStorage(
}
}
fun createCollection(title: String, sessions: List<Session>) {
suspend fun createCollection(title: String, sessions: List<Session>) = ioScope.launch {
collectionStorage.createCollection(title, sessions)
notifyObservers { onCollectionCreated(title, sessions) }
}
}.join()
fun addTabsToCollection(tabCollection: TabCollection, sessions: List<Session>) {
suspend fun addTabsToCollection(tabCollection: TabCollection, sessions: List<Session>) = ioScope.launch {
collectionStorage.addTabsToCollection(tabCollection, sessions)
notifyObservers { onTabsAdded(tabCollection, sessions) }
}
}.join()
fun getTabCollectionsCount(): Int {
return collectionStorage.getTabCollectionsCount()
@ -79,18 +83,18 @@ class TabCollectionStorage(
return collectionStorage.getCollectionsPaged()
}
fun removeCollection(tabCollection: TabCollection) {
suspend fun removeCollection(tabCollection: TabCollection) = ioScope.launch {
collectionStorage.removeCollection(tabCollection)
}
}.join()
fun removeTabFromCollection(tabCollection: TabCollection, tab: Tab) {
suspend fun removeTabFromCollection(tabCollection: TabCollection, tab: Tab) = ioScope.launch {
collectionStorage.removeTabFromCollection(tabCollection, tab)
}
}.join()
fun renameCollection(tabCollection: TabCollection, title: String) {
suspend fun renameCollection(tabCollection: TabCollection, title: String) = ioScope.launch {
collectionStorage.renameCollection(tabCollection, title)
notifyObservers { onCollectionRenamed(tabCollection, title) }
}
}.join()
}
fun TabCollection.description(context: Context): String {

@ -6,6 +6,7 @@ package org.mozilla.fenix.components.toolbar
import android.content.Intent
import androidx.annotation.VisibleForTesting
import androidx.appcompat.app.AlertDialog
import androidx.navigation.NavController
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.google.android.material.snackbar.Snackbar
@ -19,6 +20,8 @@ import mozilla.components.browser.session.SessionManager
import mozilla.components.concept.engine.EngineSession.LoadUrlFlags
import mozilla.components.concept.engine.prompt.ShareData
import mozilla.components.feature.session.SessionFeature
import mozilla.components.feature.top.sites.DefaultTopSitesStorage
import mozilla.components.feature.top.sites.TopSite
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.NavGraphDirections
@ -62,7 +65,8 @@ class DefaultBrowserToolbarMenuController(
private val openInFenixIntent: Intent,
private val bookmarkTapped: (Session) -> Unit,
private val scope: CoroutineScope,
private val tabCollectionStorage: TabCollectionStorage
private val tabCollectionStorage: TabCollectionStorage,
private val topSitesStorage: DefaultTopSitesStorage
) : BrowserToolbarMenuController {
private val currentSession
@ -124,23 +128,38 @@ class DefaultBrowserToolbarMenuController(
)
ToolbarMenu.Item.AddToTopSites -> {
scope.launch {
ioScope.launch {
currentSession?.let {
with(activity.components.useCases.topSitesUseCase) {
addPinnedSites(it.title, it.url)
val context = swipeRefresh.context
val numPinnedSites =
topSitesStorage.cachedTopSites.filter { it.type != TopSite.Type.FRECENT }.size
if (numPinnedSites >= settings.topSitesMaxLimit) {
AlertDialog.Builder(swipeRefresh.context).apply {
setTitle(R.string.top_sites_max_limit_title)
setMessage(R.string.top_sites_max_limit_content_2)
setPositiveButton(R.string.top_sites_max_limit_confirmation_button) { dialog, _ ->
dialog.dismiss()
}
}
}.join()
create()
}.show()
} else {
ioScope.launch {
currentSession?.let {
with(activity.components.useCases.topSitesUseCase) {
addPinnedSites(it.title, it.url)
}
}
}.join()
FenixSnackbar.make(
view = swipeRefresh,
duration = Snackbar.LENGTH_SHORT,
isDisplayedWithBrowserToolbar = true
)
.setText(
swipeRefresh.context.getString(R.string.snackbar_added_to_top_sites)
FenixSnackbar.make(
view = swipeRefresh,
duration = Snackbar.LENGTH_SHORT,
isDisplayedWithBrowserToolbar = true
)
.show()
.setText(
context.getString(R.string.snackbar_added_to_top_sites)
)
.show()
}
}
}
ToolbarMenu.Item.AddToHomeScreen, ToolbarMenu.Item.InstallToHomeScreen -> {

@ -214,7 +214,8 @@ class BrowserToolbarView(
when (settings.toolbarPosition) {
ToolbarPosition.BOTTOM -> {
(view.layoutParams as CoordinatorLayout.LayoutParams).apply {
(behavior as BrowserToolbarBottomBehavior).forceExpand(view)
// behavior can be null if the "Scroll to hide toolbar" setting is toggled off.
(behavior as? BrowserToolbarBottomBehavior)?.forceExpand(view)
}
}
ToolbarPosition.TOP -> {
@ -225,7 +226,8 @@ class BrowserToolbarView(
/**
* Dynamically sets scroll flags for the toolbar when the user does not have a screen reader enabled
* Note that the bottom toolbar has a feature flag for being dynamic, so it may not get flags set.
* Note that the toolbar will have the flags set and be able to be hidden
* only if the user didn't disabled this behavior in app's settings.
*/
fun setScrollFlags(shouldDisableScroll: Boolean = false) {
when (settings.toolbarPosition) {
@ -236,7 +238,10 @@ class BrowserToolbarView(
}
ToolbarPosition.TOP -> {
view.updateLayoutParams<AppBarLayout.LayoutParams> {
scrollFlags = if (settings.shouldUseFixedTopToolbar || shouldDisableScroll) {
scrollFlags =
if (settings.shouldUseFixedTopToolbar ||
!settings.isDynamicToolbarEnabled ||
shouldDisableScroll) {
// Force expand the toolbar so the user is not stuck with a hidden toolbar
expand()
0

@ -81,20 +81,41 @@ class TabCounterMenu(
}
@VisibleForTesting
internal fun menuItems(showOnly: BrowsingMode?): List<MenuCandidate> {
internal fun menuItems(showOnly: BrowsingMode): List<MenuCandidate> {
return when (showOnly) {
BrowsingMode.Normal -> listOf(newTabItem)
BrowsingMode.Private -> listOf(newPrivateTabItem)
null -> listOf(
newTabItem,
newPrivateTabItem,
DividerMenuCandidate(),
closeTabItem
)
}
}
fun updateMenu(showOnly: BrowsingMode? = null) {
@VisibleForTesting
internal fun menuItems(toolbarPosition: ToolbarPosition): List<MenuCandidate> {
val items = listOf(
newTabItem,
newPrivateTabItem,
DividerMenuCandidate(),
closeTabItem
)
return when (toolbarPosition) {
ToolbarPosition.BOTTOM -> items.reversed()
ToolbarPosition.TOP -> items
}
}
/**
* Update the displayed menu items.
* @param showOnly Show only the new tab item corresponding to the given [BrowsingMode].
*/
fun updateMenu(showOnly: BrowsingMode) {
menuController.submitList(menuItems(showOnly))
}
/**
* Update the displayed menu items.
* @param toolbarPosition Return a list that is ordered based on the given [ToolbarPosition].
*/
fun updateMenu(toolbarPosition: ToolbarPosition) {
menuController.submitList(menuItems(toolbarPosition))
}
}

@ -34,6 +34,7 @@ class TabCounterToolbarButton(
override fun createView(parent: ViewGroup): View {
val store = parent.context.components.core.store
val metrics = parent.context.components.analytics.metrics
val settings = parent.context.components.settings
store.flowScoped(lifecycleOwner) { flow ->
flow.map { state -> state.getNormalOrPrivateTabs(isPrivate).size }
@ -42,7 +43,7 @@ class TabCounterToolbarButton(
}
val menu = TabCounterMenu(parent.context, metrics, onItemTapped)
menu.updateMenu()
menu.updateMenu(settings.toolbarPosition)
val view = TabCounter(parent.context).apply {
reference = WeakReference(this)

@ -55,6 +55,7 @@ import mozilla.appservices.places.BookmarkRoot
import mozilla.components.browser.menu.view.MenuButton
import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager
import mozilla.components.browser.state.selector.findTab
import mozilla.components.browser.state.selector.getNormalOrPrivateTabs
import mozilla.components.browser.state.selector.normalTabs
import mozilla.components.browser.state.selector.privateTabs
@ -84,7 +85,6 @@ import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.tips.FenixTipManager
import org.mozilla.fenix.components.tips.Tip
import org.mozilla.fenix.components.tips.providers.MasterPasswordTipProvider
import org.mozilla.fenix.components.tips.providers.MigrationTipProvider
import org.mozilla.fenix.components.toolbar.TabCounterMenu
import org.mozilla.fenix.components.toolbar.ToolbarPosition
import org.mozilla.fenix.ext.components
@ -199,7 +199,7 @@ class HomeFragment : Fragment() {
collections = components.core.tabCollectionStorage.cachedTabCollections,
expandedCollections = emptySet(),
mode = currentMode.getCurrentMode(),
topSites = components.core.topSiteStorage.cachedTopSites,
topSites = components.core.topSitesStorage.cachedTopSites,
tip = StrictMode.allowThreadDiskReads().resetPoliciesAfter {
FenixTipManager(
listOf(
@ -207,8 +207,7 @@ class HomeFragment : Fragment() {
requireContext(),
::navToSavedLogins,
::dismissTip
),
MigrationTipProvider(requireContext())
)
)
).getTip()
},
@ -220,7 +219,7 @@ class HomeFragment : Fragment() {
topSitesFeature.set(
feature = TopSitesFeature(
view = DefaultTopSitesView(homeFragmentStore),
storage = components.core.topSiteStorage,
storage = components.core.topSitesStorage,
config = ::getTopSitesConfig
),
owner = this,
@ -471,16 +470,10 @@ class HomeFragment : Fragment() {
private fun removeAllTabsAndShowSnackbar(sessionCode: String) {
val tabs = sessionManager.sessionsOfType(private = sessionCode == ALL_PRIVATE_TABS).toList()
val selectedIndex = sessionManager
.selectedSession?.let { sessionManager.sessions.indexOf(it) } ?: 0
.selectedSession?.let { sessionManager.sessions.indexOf(it) } ?: SessionManager.NO_SELECTION
val snapshot = tabs
.map(sessionManager::createSessionSnapshot)
.map {
it.copy(
engineSession = null,
engineSessionState = it.engineSession?.saveState()
)
}
.let { SessionManager.Snapshot(it, selectedIndex) }
tabs.forEach {
@ -508,7 +501,7 @@ class HomeFragment : Fragment() {
private fun removeTabAndShowSnackbar(sessionId: String) {
sessionManager.findSessionById(sessionId)?.let { session ->
val snapshot = sessionManager.createSessionSnapshot(session)
val state = snapshot.engineSession?.saveState()
val state = store.state.findTab(sessionId)?.engineState?.engineSessionState
val isSelected =
session.id == requireComponents.core.store.state.selectedTabId ?: false
@ -559,7 +552,7 @@ class HomeFragment : Fragment() {
HomeFragmentAction.Change(
collections = components.core.tabCollectionStorage.cachedTabCollections,
mode = currentMode.getCurrentMode(),
topSites = components.core.topSiteStorage.cachedTopSites,
topSites = components.core.topSitesStorage.cachedTopSites,
tip = StrictMode.allowThreadDiskReads().resetPoliciesAfter {
FenixTipManager(
listOf(
@ -567,8 +560,7 @@ class HomeFragment : Fragment() {
requireContext(),
::navToSavedLogins,
::dismissTip
),
MigrationTipProvider(requireContext())
)
)
).getTip()
},

@ -240,7 +240,7 @@ class DefaultSessionControlController(
handleSwipedItemDeletionCancel
)
} else {
viewLifecycleScope.launch(Dispatchers.IO) {
viewLifecycleScope.launch {
tabCollectionStorage.removeTabFromCollection(collection, tab)
}
}

@ -11,10 +11,12 @@ import android.text.SpannableString
import android.text.Spanned
import android.text.style.ImageSpan
import android.view.View
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import androidx.appcompat.content.res.AppCompatResources
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.onboarding_private_browsing.view.*
import mozilla.components.support.ktx.android.content.getColorFromAttr
import mozilla.components.support.ktx.android.content.getDrawableWithTint
import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.components
@ -33,7 +35,8 @@ class OnboardingPrivateBrowsingViewHolder(
val inlineIcon = PrivateBrowsingImageSpan(
view.context,
R.drawable.ic_private_browsing,
view.description_text_once.lineHeight
tint = view.context.getColorFromAttr(R.attr.primaryText),
size = view.description_text_once.lineHeight
)
val text = SpannableString(view.context.getString(R.string.onboarding_private_browsing_description1)).apply {
@ -57,9 +60,10 @@ class OnboardingPrivateBrowsingViewHolder(
class PrivateBrowsingImageSpan(
context: Context,
@DrawableRes drawableId: Int,
@ColorInt tint: Int,
size: Int
) : ImageSpan(
AppCompatResources.getDrawable(context, drawableId)!!.apply { setBounds(size) }
context.getDrawableWithTint(drawableId, tint)!!.apply { setBounds(size) }
) {
override fun draw(
canvas: Canvas,

@ -58,6 +58,7 @@ class CustomizationFragment : PreferenceFragmentCompat() {
setupTabsTrayCategory()
setupFabCategory()
setupHomeCategory()
setupGesturesCategory()
setupAddonsCustomizationCategory()
}
@ -225,6 +226,23 @@ class CustomizationFragment : PreferenceFragmentCompat() {
}
}
private fun setupGesturesCategory() {
requirePreference<SwitchPreference>(R.string.pref_key_website_pull_to_refresh).apply {
isVisible = FeatureFlags.pullToRefreshEnabled
isChecked = context.settings().isPullToRefreshEnabledInBrowser
onPreferenceChangeListener = SharedPreferenceUpdater()
}
requirePreference<SwitchPreference>(R.string.pref_key_dynamic_toolbar).apply {
isChecked = context.settings().isDynamicToolbarEnabled
onPreferenceChangeListener = SharedPreferenceUpdater()
}
requirePreference<SwitchPreference>(R.string.pref_key_swipe_toolbar_switch_tabs).apply {
isChecked = context.settings().isSwipeToolbarToSwitchTabsEnabled
onPreferenceChangeListener = SharedPreferenceUpdater()
}
}
private fun setupAddonsCustomizationCategory() {
requirePreference<EditTextPreference>(R.string.pref_key_addons_custom_account).apply {
text = context.settings().customAddonsAccount

@ -41,7 +41,7 @@ class AboutFragment : Fragment(), AboutPageListener {
private lateinit var headerAppName: String
private lateinit var appName: String
private val aboutPageAdapter: AboutPageAdapter = AboutPageAdapter(this)
private var aboutPageAdapter: AboutPageAdapter? = AboutPageAdapter(this)
override fun onCreateView(
inflater: LayoutInflater,
@ -58,6 +58,10 @@ class AboutFragment : Fragment(), AboutPageListener {
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (aboutPageAdapter == null) {
aboutPageAdapter = AboutPageAdapter(this)
}
about_list.run {
adapter = aboutPageAdapter
addItemDecoration(
@ -76,7 +80,12 @@ class AboutFragment : Fragment(), AboutPageListener {
)
populateAboutHeader()
aboutPageAdapter.submitList(populateAboutList())
aboutPageAdapter?.submitList(populateAboutList())
}
override fun onDestroyView() {
super.onDestroyView()
aboutPageAdapter = null
}
private fun populateAboutHeader() {

@ -15,11 +15,14 @@ import kotlinx.android.synthetic.main.fragment_delete_browsing_data.*
import kotlinx.android.synthetic.main.fragment_delete_browsing_data.view.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import mozilla.components.lib.state.ext.flowScoped
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
import org.mozilla.fenix.R
@ -139,7 +142,7 @@ class DeleteBrowsingDataFragment : Fragment(R.layout.fragment_delete_browsing_da
private fun deleteSelected() {
startDeletion()
viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
viewLifecycleOwner.lifecycleScope.launch(IO) {
getCheckboxes().mapIndexed { i, v ->
if (v.isChecked) {
when (i) {
@ -152,7 +155,7 @@ class DeleteBrowsingDataFragment : Fragment(R.layout.fragment_delete_browsing_da
}
}
launch(Dispatchers.Main) {
withContext(Main) {
finishDeletion()
requireComponents.analytics.metrics.track(Event.ClearedPrivateData)
}

@ -18,6 +18,7 @@ import kotlinx.coroutines.withContext
import mozilla.components.feature.sitepermissions.SitePermissions
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.showToolbar
import org.mozilla.fenix.settings.PhoneFeature
import org.mozilla.fenix.settings.PhoneFeature.CAMERA
@ -44,13 +45,10 @@ class SitePermissionsDetailsExceptionsFragment : PreferenceFragmentCompat() {
override fun onResume() {
super.onResume()
showToolbar(sitePermissions.origin)
viewLifecycleOwner.lifecycleScope.launch(IO) {
val context = requireContext()
viewLifecycleOwner.lifecycleScope.launch(Main) {
sitePermissions =
requireNotNull(context.components.core.permissionStorage.findSitePermissionsBy(sitePermissions.origin))
withContext(Main) {
bindCategoryPhoneFeatures()
}
requireNotNull(requireComponents.core.permissionStorage.findSitePermissionsBy(sitePermissions.origin))
bindCategoryPhoneFeatures()
}
}

@ -24,7 +24,6 @@ import androidx.paging.PagedListAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch
import mozilla.components.feature.sitepermissions.SitePermissions
@ -108,13 +107,12 @@ class SitePermissionsExceptionsFragment :
}
private fun deleteAllSitePermissions() {
viewLifecycleOwner.lifecycleScope.launch(IO) {
viewLifecycleOwner.lifecycleScope.launch(Main) {
requireContext().components.core.permissionStorage.deleteAllSitePermissions()
launch(Main) {
showEmptyListMessage()
// Reload the selected session.
requireContext().components.useCases.sessionUseCases.reload()
}
showEmptyListMessage()
// Reload the selected session.
requireContext().components.useCases.sessionUseCases.reload()
}
}

@ -18,7 +18,6 @@ import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.navArgs
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch
import mozilla.components.feature.sitepermissions.SitePermissions
@ -142,11 +141,9 @@ class SitePermissionsManageExceptionsPhoneFeatureFragment : Fragment() {
private fun updatedSitePermissions(status: SitePermissions.Status) {
val updatedSitePermissions = args.sitePermissions.update(args.phoneFeature, status)
viewLifecycleOwner.lifecycleScope.launch(IO) {
viewLifecycleOwner.lifecycleScope.launch(Main) {
requireComponents.core.permissionStorage.updateSitePermissions(updatedSitePermissions)
launch(Main) {
requireComponents.tryReloadTabBy(updatedSitePermissions.origin)
}
requireComponents.tryReloadTabBy(updatedSitePermissions.origin)
}
}
}

@ -8,6 +8,7 @@ import android.content.Context
import android.view.LayoutInflater
import android.view.View
import androidx.core.view.isVisible
import kotlinx.android.synthetic.main.checkbox_item.view.*
import kotlinx.android.synthetic.main.tab_tray_item.view.*
import mozilla.components.browser.tabstray.TabViewHolder
import mozilla.components.browser.tabstray.TabsAdapter
@ -67,7 +68,11 @@ class FenixTabsAdapter(
override fun onBindViewHolder(holder: TabViewHolder, position: Int) {
super.onBindViewHolder(holder, position)
val newIndex = tabCount - position - 1
(holder as TabTrayViewHolder).updateAccessibilityRowIndex(holder.itemView, newIndex)
(holder as TabTrayViewHolder).updateAccessibilityRowInfo(
holder.itemView,
newIndex,
selectedItems.contains(holder.tab)
)
holder.tab?.let { tab ->
showCheckedIfSelected(tab, holder.itemView)
@ -105,7 +110,6 @@ class FenixTabsAdapter(
private fun showCheckedIfSelected(tab: Tab, view: View) {
val shouldBeChecked =
mode is TabTrayDialogFragmentState.Mode.MultiSelect && selectedItems.contains(tab)
view.checkmark.isVisible = shouldBeChecked
view.selected_mask.isVisible = shouldBeChecked
view.mozac_browser_tabstray_close.isVisible = mode is TabTrayDialogFragmentState.Mode.Normal
}

@ -30,6 +30,7 @@ import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
import mozilla.components.browser.session.Session
import mozilla.components.browser.state.selector.findTab
import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.browser.thumbnails.loader.ThumbnailLoader
import mozilla.components.feature.tab.collections.TabCollection
@ -260,26 +261,22 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler
}
private fun showUndoSnackbarForTab(sessionId: String) {
val sessionManager = view?.context?.components?.core?.sessionManager
val store = requireComponents.core.store
val sessionManager = requireComponents.core.sessionManager
val snapshot = sessionManager
?.findSessionById(sessionId)?.let {
sessionManager.createSessionSnapshot(it)
} ?: return
val tab = requireComponents.core.store.state.findTab(sessionId) ?: return
val session = sessionManager.findSessionById(sessionId) ?: return
// Check if this is the last tab of this session type
val isLastOpenTab =
sessionManager.sessions.filter { snapshot.session.private == it.private }.size == 1
val isLastOpenTab = store.state.tabs.filter { it.content.private == tab.content.private }.size == 1
if (isLastOpenTab) {
dismissTabTrayAndNavigateHome(sessionId)
return
}
val state = snapshot.engineSession?.saveState()
val isSelected = sessionId == requireComponents.core.store.state.selectedTabId ?: false
val snackbarMessage = if (snapshot.session.private) {
val snackbarMessage = if (tab.content.private) {
getString(R.string.snackbar_private_tab_closed)
} else {
getString(R.string.snackbar_tab_closed)
@ -290,8 +287,8 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler
snackbarMessage,
getString(R.string.snackbar_deleted_undo),
{
sessionManager.add(snapshot.session, isSelected, engineSessionState = state)
_tabTrayView?.scrollToTab(snapshot.session.id)
sessionManager.add(session, isSelected, engineSessionState = tab.engineState.engineSessionState)
_tabTrayView?.scrollToTab(session.id)
},
operation = { },
elevation = ELEVATION,
@ -378,7 +375,7 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler
val selectedCollection =
(list.adapter as CollectionsAdapter).getSelectedCollection()
val collection = tabCollectionStorage.cachedTabCollections[selectedCollection]
viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
viewLifecycleOwner.lifecycleScope.launch(Main) {
tabCollectionStorage.addTabsToCollection(collection, sessionList)
it.metrics.track(
Event.CollectionTabsAdded(
@ -386,10 +383,8 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler
sessionList.size
)
)
launch(Main) {
tabTrayDialogStore.dispatch(TabTrayDialogFragmentAction.ExitMultiSelectMode)
dialog.dismiss()
}
tabTrayDialogStore.dispatch(TabTrayDialogFragmentAction.ExitMultiSelectMode)
dialog.dismiss()
}
}.setNegativeButton(android.R.string.cancel) { dialog, _ ->
tabTrayDialogStore.dispatch(TabTrayDialogFragmentAction.ExitMultiSelectMode)

@ -9,6 +9,8 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo
import androidx.annotation.IdRes
import androidx.cardview.widget.CardView
import androidx.constraintlayout.widget.ConstraintLayout
@ -266,7 +268,7 @@ class TabTrayView(
// WARNING: Merging the upstream fix for this will cause lot of conflicts!
//
// if (hasAccessibilityEnabled) {
// tabsAdapter.notifyDataSetChanged()
// tabsAdapter.notifyItemRangeChanged(0, tabs.size)
// }
if (!hasLoaded) {
@ -633,6 +635,22 @@ class TabTrayView(
} else {
view.context?.getString(R.string.open_tab_tray_plural, count.toString())
}
view.tabsTray.accessibilityDelegate = object : View.AccessibilityDelegate() {
override fun onInitializeAccessibilityNodeInfo(
host: View?,
info: AccessibilityNodeInfo?
) {
super.onInitializeAccessibilityNodeInfo(host, info)
info?.let {
info.collectionInfo = CollectionInfo.obtain(
tabsAdapter.tabCount,
1,
false
)
}
}
}
}
private fun updateTabCounter(count: Int): String {

@ -201,25 +201,22 @@ class TabTrayViewHolder(
imageLoader.loadIntoView(thumbnailView, ImageLoadRequest(id, thumbnailSize))
}
internal fun updateAccessibilityRowIndex(item: View, newIndex: Int) {
internal fun updateAccessibilityRowInfo(item: View, newIndex: Int, isSelected: Boolean) {
item.accessibilityDelegate = object : View.AccessibilityDelegate() {
override fun onInitializeAccessibilityNodeInfo(
host: View?,
info: AccessibilityNodeInfo?
) {
super.onInitializeAccessibilityNodeInfo(host, info)
info?.let {
info.collectionItemInfo = info.collectionItemInfo?.let { initialInfo ->
AccessibilityNodeInfo.CollectionItemInfo.obtain(
newIndex,
initialInfo.rowSpan,
initialInfo.columnIndex,
initialInfo.columnSpan,
false,
initialInfo.isSelected
)
}
}
info?.collectionItemInfo =
AccessibilityNodeInfo.CollectionItemInfo.obtain(
newIndex,
1,
1,
1,
false,
isSelected
)
}
}
}

@ -103,7 +103,7 @@ class Settings(private val appContext: Context) : PreferencesHolder {
var showTopFrecentSites by featureFlagPreference(
appContext.getPreferenceKey(R.string.pref_key_enable_top_frecent_sites),
default = false,
default = true,
featureFlag = FeatureFlags.topFrecentSite
)
@ -937,4 +937,19 @@ class Settings(private val appContext: Context) : PreferencesHolder {
SavedLoginsSortingStrategyMenu.Item.LastUsedSort.strategyString
}
}
var isPullToRefreshEnabledInBrowser by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_website_pull_to_refresh),
default = true
)
var isDynamicToolbarEnabled by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_dynamic_toolbar),
default = true
)
var isSwipeToolbarToSwitchTabsEnabled by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_swipe_toolbar_switch_tabs),
default = true
)
}

@ -8,6 +8,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/primaryText"
android:fillColor="@color/photonWhite"
android:pathData="M17.5 17c-2.1 0-3.5-2.5-5.5-2.5S8.4 17 6.5 17C3.9 17 2 14.6 2 10.4 2 7.8 2.8 7 6.1 7s4.3 1.4 5.9 1.4c1.6 0 2.6-1.4 5.9-1.4 3.3 0 4.1 0.8 4.1 3.4 0 4.2-1.9 6.6-4.5 6.6zm-9.8-6.8c-2 0.1-2.9 1.3-2.9 1.6 0 0.3 1.3 1.1 2.7 1.1 1.3 0 2.9-0.5 2.9-0.9 0-0.5-0.8-1.9-2.7-1.8zm8.6 0c-1.9-0.1-2.7 1.3-2.7 1.8 0 0.4 1.5 0.9 2.9 0.9s2.7-0.8 2.7-1.1c-0.1-0.3-0.9-1.5-2.9-1.6z" />
</vector>

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?><!-- 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/. -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/selected_mask"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/tab_tray_selected_mask_normal_theme"
android:visibility="gone"
tools:visibility="visible">
<ImageView
android:id="@+id/checkmark"
android:contentDescription="@string/tab_tray_multiselect_selected_content_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/favicon_background"
android:backgroundTint="@color/accent_normal_theme"
android:elevation="1dp"
android:padding="8dp"
app:srcCompat="@drawable/mozac_ic_check" />
</FrameLayout>

@ -78,6 +78,7 @@
android:backgroundTint="?accent"
android:text="@string/mozac_feature_downloads_button_open"
android:textAllCaps="false"
android:padding="16dp"
android:textColor="?contrastText"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"

@ -52,25 +52,7 @@
android:layout_height="match_parent"
android:contentDescription="@string/mozac_browser_tabstray_open_tab" />
<View
android:id="@+id/selected_mask"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/tab_tray_selected_mask_normal_theme"
android:visibility="gone" />
<ImageView
android:id="@+id/checkmark"
android:contentDescription="@string/tab_tray_multiselect_selected_content_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|center_vertical"
android:background="@drawable/favicon_background"
android:backgroundTint="@color/accent_normal_theme"
android:elevation="1dp"
android:padding="10dp"
android:visibility="gone"
app:srcCompat="@drawable/mozac_ic_check" />
<include layout="@layout/checkbox_item" />
</androidx.cardview.widget.CardView>
@ -104,7 +86,7 @@
android:paddingTop="22dp"
android:textColor="@color/tab_tray_item_text_normal_theme"
android:textSize="16sp"
tools:text="This is the title of the tab and it is long"
tools:text="Example Domain"
app:layout_constraintEnd_toStartOf="@id/mozac_browser_tabstray_close"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@id/mozac_browser_tabstray_icon_card"
@ -122,7 +104,7 @@
android:paddingStart="8dp"
android:textColor="@color/tab_tray_item_url_normal_theme"
android:textSize="14sp"
tools:text="https://www.google.com"
tools:text="example.com"
app:layout_constraintEnd_toStartOf="@id/mozac_browser_tabstray_close"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@id/mozac_browser_tabstray_card"

@ -51,25 +51,7 @@
android:layout_height="match_parent"
android:contentDescription="@string/mozac_browser_tabstray_open_tab" />
<View
android:id="@+id/selected_mask"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/tab_tray_selected_mask_normal_theme"
android:visibility="gone" />
<ImageView
android:id="@+id/checkmark"
android:contentDescription="@string/tab_tray_multiselect_selected_content_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|center_vertical"
android:background="@drawable/favicon_background"
android:backgroundTint="@color/accent_normal_theme"
android:elevation="1dp"
android:padding="10dp"
android:visibility="gone"
app:srcCompat="@drawable/mozac_ic_check" />
<include layout="@layout/checkbox_item" />
</androidx.cardview.widget.CardView>
@ -105,7 +87,7 @@
android:textColor="@color/tab_tray_item_text_normal_theme"
android:textSize="14sp"
android:visibility="visible"
tools:text="Webpage tile that is long and will overflow textview widget"
tools:text="Example Domain"
app:layout_constraintEnd_toStartOf="@id/mozac_browser_tabstray_close"
app:layout_constraintStart_toEndOf="@id/mozac_browser_tabstray_icon_card"
app:layout_constraintTop_toTopOf="parent" />
@ -119,7 +101,7 @@
android:paddingStart="8dp"
android:textColor="@color/tab_tray_item_url_normal_theme"
android:textSize="14sp"
tools:text="https://www.google.com"
tools:text="example.com"
android:visibility="gone"
app:layout_constraintEnd_toStartOf="@id/mozac_browser_tabstray_close"
app:layout_constraintHorizontal_bias="0.0"

@ -19,10 +19,11 @@
<ImageView
android:id="@+id/refresh_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="60dp"
android:layout_marginEnd="16dp"
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="12dp"
android:layout_marginTop="48dp"
android:layout_marginEnd="4dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
app:srcCompat="@drawable/mozac_ic_refresh"
app:tint="?primaryText" />

@ -74,6 +74,9 @@
<!-- Text for the negative button -->
<string name="search_widget_cfr_neg_button_text">Agora non</string>
<!-- Open in App "contextual feature recommendation" (CFR) -->
<!-- Text for the info message. 'Firefox' intentionally hardcoded here.-->
<string name="open_in_app_cfr_info_message">Pues afitar que Firefox abra automáticamente enllaces nes aplicaciones.</string>
<!-- Text for the positive action button -->
<string name="open_in_app_cfr_positive_button_text">Dir a Axustes</string>
<!-- Text for the negative action button -->

@ -44,6 +44,9 @@
<!-- Content description announcement when exiting multiselect mode in tab tray -->
<string name="tab_tray_exit_multiselect_content_description">Τέλος λειτουργίας πολλαπλής επιλογής</string>
<!-- Content description announcement when entering multiselect mode in tab tray -->
<string name="tab_tray_enter_multiselect_content_description">Σε λειτουργία πολλαπλών επιλογών, επιλέξτε καρτέλες για αποθήκευση σε συλλογή</string>
<!-- About content. The first parameter is the name of the application. (For example: Fenix) -->
<string name="about_content">Το %1$s αναπτύσσεται από τη @fork-maintainers.</string>
@ -58,6 +61,9 @@
<!-- Delete session button to erase your history in a private session -->
<string name="private_browsing_delete_session">Διαγραφή συνεδρίας</string>
<!-- Private mode shortcut "contextual feature recommendation" (CFR) -->
<!-- Text for the main message -->
<string name="cfr_message">Προσθέστε συντόμευση για άνοιγμα ιδιωτικών καρτελών από την αρχική οθόνη.</string>
<!-- Text for the positive button -->
<string name="cfr_pos_button_text">Προσθήκη συντόμευσης</string>
<!-- Text for the negative button -->
@ -68,6 +74,9 @@
<!-- Text for the negative button -->
<string name="search_widget_cfr_neg_button_text">Όχι τώρα</string>
<!-- Open in App "contextual feature recommendation" (CFR) -->
<!-- Text for the info message. 'Firefox' intentionally hardcoded here.-->
<string name="open_in_app_cfr_info_message">Μπορείτε να ρυθμίσετε το Firefox να ανοίγει αυτόματα συνδέσμους σε εφαρμογές.</string>
<!-- Text for the positive action button -->
<string name="open_in_app_cfr_positive_button_text">Μετάβαση στις ρυθμίσεις</string>
<!-- Text for the negative action button -->
@ -259,6 +268,8 @@
<string name="preferences_override_fxa_server">Προσαρμοσμένος διακομιστής λογαριασμού Firefox</string>
<!-- Preference to override the Sync token server -->
<string name="preferences_override_sync_tokenserver">Προσαρμοσμένος διακομιστής Sync</string>
<!-- Toast shown after updating the FxA/Sync server override preferences -->
<string name="toast_override_fxa_sync_server_done">Ο διακομιστής λογαριασμού Firefox/Sync τροποποιήθηκε. Τερματισμός εφαρμογής για εφαρμογή αλλαγών…</string>
<!-- Preference category for account information -->
<string name="preferences_category_account">Λογαριασμός</string>
<!-- Preference shown on banner to sign into account -->
@ -769,8 +780,10 @@
<string name="collections_header">Συλλογές</string>
<!-- Content description (not visible, for screen readers etc.): Opens the collection menu when pressed -->
<string name="collection_menu_button_content_description">Μενού συλλογής</string>
<!-- Label to describe what collections are to a new user without any collections -->
<string name="no_collections_description2">Συλλέξτε αυτά που σας ενδιαφέρουν.\nΟμαδοποιήστε παρόμοιες αναζητήσεις, σελίδες και καρτέλες για γρήγορη πρόσβαση αργότερα.</string>
<!-- Title for the "select tabs" step of the collection creator -->
<string name="create_collection_select_tabs">Επιλέξτε καρτέλες</string>
<string name="create_collection_select_tabs">Επιλογή καρτελών</string>
<!-- Title for the "select collection" step of the collection creator -->
<string name="create_collection_select_collection">Επιλογή συλλογής</string>
<!-- Title for the "name collection" step of the collection creator -->
@ -860,6 +873,8 @@
<!-- Notification action to delete all current private browsing sessions AND switch to Fenix (bring it to the foreground) -->
<string name="notification_pbm_action_delete_and_open">Διαγραφή και άνοιγμα</string>
<!-- Name of the "Powered by Fenix" notification channel. Displayed in the "App notifications" system settings for the app -->
<string name="notification_powered_by_channel_name">Με την υποστήριξη του</string>
<!-- Text shown in snackbar when user deletes a collection -->
<string name="snackbar_collection_deleted">Η συλλογή διαγράφηκε</string>
<!-- Text shown in snackbar when user renames a collection -->
@ -897,6 +912,8 @@
<string name="qr_scanner_dialog_negative">ΑΡΝΗΣΗ</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Θέλετε σίγουρα να διαγράψετε το %1$s;</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->
<string name="delete_tab_and_collection_dialog_message">Η αφαίρεση της καρτέλας θα διαγράψει ολόκληρη τη συλλογή. Μπορείτε να δημιουργήσετε νέες συλλογές ανά πάσα στιγμή.</string>
<!-- Collection and tab deletion prompt dialog title. Placeholder will be replaced with the collection name. This will show when the last tab from a collection is deleted -->
<string name="delete_tab_and_collection_dialog_title">Διαγραφή του %1$s;</string>
<!-- Tab collection deletion prompt dialog option to delete the collection -->
@ -1018,14 +1035,22 @@
<string name="onboarding_whats_new_description">Έχετε ερωτήσεις σχετικά με το επανασχεδιασμένο %s; Θέλετε να μάθετε τι έχει αλλάξει;</string>
<!-- text for underlined clickable link that is part of "what's new" onboarding card description that links to an FAQ -->
<string name="onboarding_whats_new_description_linktext">Λάβετε απαντήσεις εδώ</string>
<!-- text for the Firefox account onboarding sign in card header -->
<string name="onboarding_account_sign_in_header">Ξεκινήστε το συγχρονισμό σελιδοδεικτών, κωδικών πρόσβασης και άλλων δεδομένων με το λογαριασμό Firefox σας.</string>
<!-- Text for the button to learn more about signing in to your Firefox account -->
<string name="onboarding_manual_sign_in_learn_more">Μάθετε περισσότερα</string>
<!-- text for the firefox account onboarding card header when we detect you're already signed in to
another Firefox browser. (The word `Firefox` should not be translated)
The first parameter is the email of the detected user's account -->
<string name="onboarding_firefox_account_auto_signin_header_2">Έχετε συνδεθεί ως %s σε άλλο πρόγραμμα περιήγησης Firefox σε αυτό το τηλέφωνο. Θέλετε να συνδεθείτε με αυτό το λογαριασμό;</string>
<!-- text for the button to confirm automatic sign-in -->
<string name="onboarding_firefox_account_auto_signin_confirm">Ναι, σύνδεση</string>
<!-- text for the automatic sign-in button while signing in is in process -->
<string name="onboarding_firefox_account_signing_in">Σύνδεση…</string>
<!-- text for the button to manually sign into Firefox account. The word "Firefox" should not be translated -->
<string name="onboarding_firefox_account_sign_in">Σύνδεση στο Firefox</string>
<!-- text for the button to stay signed out when presented with an option to automatically sign-in. -->
<string name="onboarding_firefox_account_stay_signed_out">Παραμονή εκτός σύνδεσης</string>
<!-- text to display in the snackbar once account is signed-in -->
<string name="onboarding_firefox_account_sync_is_on">Το Sync είναι ενεργό</string>
<!-- text to display in the snackbar if automatic sign-in fails. user may try again -->
@ -1042,12 +1067,28 @@
<string name="onboarding_tracking_protection_strict_button">Αυστηρή (προτείνεται)</string>
<!-- text for tracking protection radio button option for strict level of blocking -->
<string name="onboarding_tracking_protection_strict_option">Αυστηρή</string>
<!-- text for the toolbar position card header
In English this is an idiom for "choose a side as in an argument or fight"
but it is ok to make this more literally about "choosing a position in a physical space -->
<string name="onboarding_toolbar_position_header">Πάρτε θέση</string>
<!-- text for the toolbar position card description -->
<string name="onboarding_toolbar_position_description">Δοκιμάστε την περιήγηση με ένα χέρι με την κάτω γραμμή εργαλείων ή μετακινήστε την στο πάνω μέρος.</string>
<!-- text for the private browsing onboarding card header -->
<string name="onboarding_private_browsing_header">Ιδιωτική περιήγηση</string>
<!-- text for the private browsing onboarding card description
The first parameter is an icon that represents private browsing -->
<string name="onboarding_private_browsing_description1">Άνοιγμα ιδιωτικής καρτέλας μία φορά: Αγγίξτε το εικονίδιο %s.</string>
<!-- text for the private browsing onboarding card description, explaining how to always using private browsing -->
<string name="onboarding_private_browsing_always_description">Άνοιγμα ιδιωτικών καρτελών κάθε φορά: Ενημερώστε τις ρυθμίσεις ιδιωτικής περιήγησης.</string>
<!-- text for the private browsing onbording card button, that launches settings -->
<string name="onboarding_private_browsing_button">Άνοιγμα ρυθμίσεων</string>
<!-- text for the privacy notice onboarding card header -->
<string name="onboarding_privacy_notice_header">Το απόρρητό σας</string>
<!-- text for the privacy notice onboarding card description
The first parameter is the name of the app (e.g. Firefox Preview) -->
<string name="onboarding_privacy_notice_description">Έχουμε σχεδιάσει το %s ώστε να έχετε τον έλεγχο του τι θα κοινοποιείτε
στο διαδίκτυο και σε εμάς.
</string>
<!-- Text for the button to read the privacy notice -->
<string name="onboarding_privacy_notice_read_button">Διαβάστε τη σημείωση απορρήτου μας</string>
<!-- Content description (not visible, for screen readers etc.): Close onboarding screen -->
@ -1166,6 +1207,8 @@
<string name="etp_fingerprinters_title">Fingerprinters</string>
<!-- Category of trackers (tracking content) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_tracking_content_title">Περιεχόμενο καταγραφής</string>
<!-- Enhanced Tracking Protection Onboarding Message shown in a dialog above the toolbar. The first parameter is the name of the application (For example: Fenix) -->
<string name="etp_onboarding_cfr_message">Κάθε φορά που η ασπίδα είναι μωβ, το %s έχει αποκλείσει ιχνηλάτες σε μια σελίδα. Πατήστε για περισσότερες πληροφορίες.</string>
<!-- Enhanced Tracking Protection message that protection is currently on for this site -->
<string name="etp_panel_on">Η προστασία είναι ΕΝΕΡΓΗ για αυτή τη σελίδα</string>
<!-- Enhanced Tracking Protection message that protection is currently off for this site -->
@ -1233,6 +1276,9 @@
<!-- Placeholder text for the TextView in the Add to Homescreen dialog -->
<string name="add_to_homescreen_text_placeholder">Όνομα συντόμευσης</string>
<!-- Describes the add to homescreen functionality -->
<string name="add_to_homescreen_description">Μπορείτε εύκολα να προσθέσετε αυτή την ιστοσελίδα στην αρχική οθόνη για άμεση πρόσβαση και ταχύτερη περιήγηση, σαν να ήταν εφαρμογή.</string>
<!-- Preference for managing the settings for logins and passwords in Fenix -->
<string name="preferences_passwords_logins_and_passwords">Συνδέσεις και κωδικοί πρόσβασης</string>
<!-- Preference for managing the saving of logins and passwords in Fenix -->
@ -1476,6 +1522,8 @@
<!-- Top Sites -->
<!-- Title text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_title">Έχετε φτάσει το ανώτατο όριο κορυφαίων ιστοσελίδων</string>
<!-- Content description text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_content">Για να προσθέσετε μια νέα κορυφαία σελίδα, αφαιρέστε μια υπάρχουσα. Πατήστε παρατεταμένα στη σελίδα και επιλέξτε &quot;Αφαίρεση&quot;.</string>
<!-- Confirmation dialog button text when top sites limit is reached. -->
<string name="top_sites_max_limit_confirmation_button">OK, το κατάλαβα</string>

@ -24,6 +24,29 @@
<!-- Message announced to the user when tab tray is selected with 0 or 2+ tabs -->
<string name="open_tab_tray_plural">%1$s malfermitaj langetoj. Tuŝetu por ŝanĝi langetojn.</string>
<!-- Tab tray multi select title in app bar. The first parameter is the number of tabs selected -->
<string name="tab_tray_multi_select_title">%1$d elektitaj</string>
<!-- Label of button in create collection dialog for creating a new collection -->
<string name="tab_tray_add_new_collection">Aldoni novan kolekton</string>
<!-- Label of editable text in create collection dialog for naming a new collection -->
<string name="tab_tray_add_new_collection_name">Nomo</string>
<!-- Label of button in save to collection dialog for selecting a current collection -->
<string name="tab_tray_select_collection">Elekti kolekton</string>
<!-- Content description for close button while in multiselect mode in tab tray -->
<string name="tab_tray_close_multiselect_content_description">Eliri el la reĝimo de plurelekto</string>
<!-- Content description for save to collection button while in multiselect mode in tab tray -->
<string name="tab_tray_collection_button_multiselect_content_description">Konservi elektitajn langetojn en kolekto</string>
<!-- Content description for checkmark while tab is selected while in multiselect mode in tab tray. The first parameter is the title of the tab selected -->
<string name="tab_tray_item_selected_multiselect_content_description">%1$s elektita</string>
<!-- Content description when tab is unselected while in multiselect mode in tab tray. The first parameter is the title of the tab unselected -->
<string name="tab_tray_item_unselected_multiselect_content_description">%1$s ne elektita</string>
<!-- Content description announcement when exiting multiselect mode in tab tray -->
<string name="tab_tray_exit_multiselect_content_description">Fino de reĝimo de plurelekto</string>
<!-- Content description announcement when entering multiselect mode in tab tray -->
<string name="tab_tray_enter_multiselect_content_description">Komenco de reĝimo de plurelekto. Elektu langetojn por konservi en kolekto.</string>
<!-- Content description on checkmark while tab is selected in multiselect mode in tab tray -->
<string name="tab_tray_multiselect_selected_content_description">Elektita</string>
<!-- About content. The first parameter is the name of the application. (For example: Fenix) -->
<string name="about_content">%1$s estas kreita de @fork-maintainers.</string>
@ -53,6 +76,28 @@
<!-- Text for the negative button -->
<string name="search_widget_cfr_neg_button_text">Ne nun</string>
<!-- Open in App "contextual feature recommendation" (CFR) -->
<!-- Text for the info message. 'Firefox' intentionally hardcoded here.-->
<string name="open_in_app_cfr_info_message">Vi povas igi Firefox aŭtomate malfermi ligilojn en programoj.</string>
<!-- Text for the positive action button -->
<string name="open_in_app_cfr_positive_button_text">Iri al agordoj</string>
<!-- Text for the negative action button -->
<string name="open_in_app_cfr_negative_button_text">Ignori</string>
<!-- Text for the info dialog when camera permissions have been denied but user tries to access a camera feature. -->
<string name="camera_permissions_needed_message">Aliro al la fimilo postulata. Iru al agordoj de Android, tuŝetu Permesojn kaj poste Permesi.</string>
<!-- Text for the positive action button to go to Android Settings to grant permissions. -->
<string name="camera_permissions_needed_positive_button_text">Iri al agordoj</string>
<!-- Text for the negative action button to dismiss the dialog. -->
<string name="camera_permissions_needed_negative_button_text">Ignori</string>
<!-- Text for the banner message to tell users about our auto close feature. -->
<string name="tab_tray_close_tabs_banner_message">Aŭtomate fermi langetojn, kiuj ne estis viditaj en la lasta tago, semajno aŭ monato.</string>
<!-- Text for the positive action button to go to Settings for auto close tabs. -->
<string name="tab_tray_close_tabs_banner_positive_button_text">Vidi eblojn</string>
<!-- Text for the negative action button to dismiss the Close Tabs Banner. -->
<string name="tab_tray_close_tabs_banner_negative_button_text">Ignori</string>
<!-- Home screen icons - Long press shortcuts -->
<!-- Shortcut action to open new tab -->
<string name="home_screen_shortcut_open_new_tab_2">Nova langeto</string>
@ -146,14 +191,12 @@
<!-- Search Fragment -->
<!-- Button in the search view that lets a user search by scanning a QR code -->
<string name="search_scan_button">Skani</string>
<!-- Button in the search view that lets a user search by using a shortcut -->
<string name="search_shortcuts_button">Ŝparvojoj</string>
<!-- Button in the search view that lets a user change their search engine -->
<string name="search_engine_button">Serĉilo</string>
<!-- Button in the search view when shortcuts are displayed that takes a user to the search engine settings -->
<string name="search_shortcuts_engine_settings">Agordoj de serĉilo</string>
<!-- DEPRECATED: Header displayed when selecting a shortcut search engine -->
<string name="search_shortcuts_search_with">Serĉi per</string>
<!-- Header displayed when selecting a shortcut search engine -->
<string name="search_shortcuts_search_with_2">Ĉi foje serĉi per:</string>
<string name="search_engines_search_with">Ĉi foje serĉi per:</string>
<!-- Button in the search view that lets a user navigate to the site in their clipboard -->
<string name="awesomebar_clipboard_title">Alglui ligilon el la tondujo</string>
<!-- Button in the search suggestions onboarding that allows search suggestions in private sessions -->
@ -241,6 +284,8 @@
<string name="preferences_toolbar">Ilaro</string>
<!-- Preference for changing default theme to dark or light mode -->
<string name="preferences_theme">Etoso</string>
<!-- Preference for customizing the home screen -->
<string name="preferences_home">Eka paĝo</string>
<!-- Preference for settings related to visual options -->
<string name="preferences_customize">Personecigi</string>
<!-- Preference description for banner about signing in -->
@ -261,8 +306,8 @@
<string name="developer_tools_category">Iloj por programistoj</string>
<!-- Preference for developers -->
<string name="preferences_remote_debugging">Fora sencimigo per USB</string>
<!-- Preference title for switch preference to show search shortcuts -->
<string name="preferences_show_search_shortcuts">Montri serĉajn ŝparvojojn</string>
<!-- Preference title for switch preference to show search engines -->
<string name="preferences_show_search_engines">Montri serĉilojn</string>
<!-- Preference title for switch preference to show search suggestions -->
<string name="preferences_show_search_suggestions">Montri serĉajn sugestojn</string>
<!-- Preference title for switch preference to show voice search button -->
@ -280,9 +325,14 @@
<!-- Preference for open links in third party apps -->
<string name="preferences_open_links_in_apps">Malfermi ligilojn per programoj</string>
<!-- Preference for open download with an external download manager app -->
<string name="preferences_external_download_manager">Ekstera administranto de elŝutoj</string>
<!-- Preference for add_ons -->
<string name="preferences_addons">Aldonaĵoj</string>
<!-- Preference for notifications -->
<string name="preferences_notifications">Sciigoj</string>
<!-- Account Preferences -->
<!-- Preference for triggering sync -->
<string name="preferences_sync_now">Speguli nun</string>
@ -443,6 +493,32 @@
<!-- Content description (not visible, for screen readers etc.): "Close button for library settings" -->
<string name="content_description_close_button">Fermi</string>
<!-- Option in library for Recently Closed Tabs -->
<string name="library_recently_closed_tabs">Ĵuse fermitaj langetoj</string>
<!-- Option in library to open Recently Closed Tabs page -->
<string name="recently_closed_show_full_history">Montri tutan historion</string>
<!-- Text to show users they have multiple tabs saved in the Recently Closed Tabs section of history.
%d is a placeholder for the number of tabs selected. -->
<string name="recently_closed_tabs">%d langetoj</string>
<!-- Text to show users they have one tab saved in the Recently Closed Tabs section of history.
%d is a placeholder for the number of tabs selected. -->
<string name="recently_closed_tab">%d langeto</string>
<!-- Recently closed tabs screen message when there are no recently closed tabs -->
<string name="recently_closed_empty_message">Estas neniu ĵuse fermita langeto ĉi tie</string>
<!-- Tab Management -->
<!-- Title of preference that allows a user to auto close tabs after a specified amount of time -->
<string name="preferences_close_tabs">Fermi langetojn</string>
<!-- Option for auto closing tabs that will never auto close tabs, always allows user to manually close tabs -->
<string name="close_tabs_manually">Permane</string>
<!-- Option for auto closing tabs that will auto close tabs after one day -->
<string name="close_tabs_after_one_day">Post tago</string>
<!-- Option for auto closing tabs that will auto close tabs after one week -->
<string name="close_tabs_after_one_week">Post semajno</string>
<!-- Option for auto closing tabs that will auto close tabs after one month -->
<string name="close_tabs_after_one_month">Post monato</string>
<!-- Sessions -->
<!-- Title for the list of tabs -->
<string name="tab_header_label">Malfermitaj langetoj</string>
@ -462,6 +538,10 @@
<string name="tab_tray_menu_item_save">Konservi en kolekto</string>
<!-- Text shown in the menu for sharing all tabs -->
<string name="tab_tray_menu_item_share">Dividi ĉiujn langetojn</string>
<!-- Text shown in the menu to view recently closed tabs -->
<string name="tab_tray_menu_recently_closed">Ĵuse fermitaj langetoj</string>
<!-- Text shown in the menu to view tab settings -->
<string name="tab_tray_menu_tab_settings">Agordoj de langetoj</string>
<!-- Text shown in the menu for closing all tabs -->
<string name="tab_tray_menu_item_close">Fermi ĉiujn langetojn</string>
<!-- Shortcut action to open new tab -->
@ -510,9 +590,14 @@
<!-- Text for the menu button to remove a top site -->
<string name="remove_top_site">Forigi</string>
<!-- Text for the menu button to delete a top site from history -->
<string name="delete_from_history">Forigi el historio</string>
<!-- Postfix for private WebApp titles, placeholder is replaced with app name -->
<string name="pwa_site_controls_title_private">%1$s (Privata reĝimo)</string>
<!-- Button in the current tab tray header in multiselect mode. Saved the selected tabs to a collection when pressed. -->
<string name="tab_tray_save_to_collection">Konservi</string>
<!-- History -->
<!-- Text for the button to clear all history -->
<string name="history_delete_all">Forigi historion</string>
@ -551,6 +636,13 @@
<!-- Text shown when no history exists -->
<string name="history_empty_message">Neniu historio estas ĉi tie</string>
<!-- Downloads -->
<!-- Text shown when no download exists -->
<string name="download_empty_message">Neniu elŝuto ĉi tie</string>
<!-- History multi select title in app bar
The first parameter is the number of downloads selected -->
<string name="download_multi_select_title">%1$d elektitaj</string>
<!-- Crashes -->
<!-- Title text displayed on the tab crash page. This first parameter is the name of the application (For example: Fenix) -->
<string name="tab_crash_title_2">Bedaŭrinde %1$s ne povas ŝargi tiun paĝon.</string>
@ -579,6 +671,8 @@
<string name="bookmark_select_folder">Elekti dosierujon</string>
<!-- Confirmation message for a dialog confirming if the user wants to delete the selected folder -->
<string name="bookmark_delete_folder_confirmation_dialog">Ĉu vi certe volas forigi tiun ĉi dosierujon?</string>
<!-- Confirmation message for a dialog confirming if the user wants to delete multiple items including folders. Parameter will be replaced by app name. -->
<string name="bookmark_delete_multiple_folders_confirmation_dialog">%s forigos la elektitajn elementojn.</string>
<!-- Snackbar title shown after a folder has been deleted. This first parameter is the name of the deleted folder -->
<string name="bookmark_delete_folder_snackbar">%1$s forigita</string>
<!-- Screen title for adding a bookmarks folder -->
@ -634,8 +728,10 @@
<!-- Bookmark snackbar message on deletion
The first parameter is the host part of the URL of the bookmark deleted, if any -->
<string name="bookmark_deletion_snackbar_message">%1$s forigita</string>
<!-- Bookmark snackbar message on deleting multiple bookmarks -->
<!-- Bookmark snackbar message on deleting multiple bookmarks not including folders-->
<string name="bookmark_deletion_multiple_snackbar_message_2">Legosignoj forigitaj</string>
<!-- Bookmark snackbar message on deleting multiple bookmarks including folders-->
<string name="bookmark_deletion_multiple_snackbar_message_3">Elektitaj dosierujoj forigataj</string>
<!-- Bookmark undo button for deletion snackbar action -->
<string name="bookmark_undo_deletion">MALFARI</string>
@ -703,10 +799,8 @@
<string name="collections_header">Kolektoj</string>
<!-- Content description (not visible, for screen readers etc.): Opens the collection menu when pressed -->
<string name="collection_menu_button_content_description">Menuo de kolektoj</string>
<!-- No Open Tabs Message Header -->
<string name="no_collections_header1">Kolekti la aferojn kiu gravas por vi</string>
<!-- Label to describe what collections are to a new user without any collections -->
<string name="no_collections_description1">Grupigi similajn serĉojn, retejojn kaj langetojn por pli rapida posta aliro.</string>
<string name="no_collections_description2">Kolektu la aferojn kiuj gravas por vi.\nGrupigu similajn serĉojn, retejojn kaj langetojn por posta rapida aliro.</string>
<!-- Title for the "select tabs" step of the collection creator -->
<string name="create_collection_select_tabs">Elektitaj langetoj</string>
<!-- Title for the "select collection" step of the collection creator -->
@ -729,6 +823,8 @@
<string name="create_collection_save_to_collection_tab_selected">%d elektita langeto</string>
<!-- Text shown in snackbar when multiple tabs have been saved in a collection -->
<string name="create_collection_tabs_saved">Langetoj konservitaj!</string>
<!-- Text shown in snackbar when one or multiple tabs have been saved in a new collection -->
<string name="create_collection_tabs_saved_new_collection">Kolekto konservita!</string>
<!-- Text shown in snackbar when one tab has been saved in a collection -->
<string name="create_collection_tab_saved">Langeto konservita!</string>
<!-- Content description (not visible, for screen readers etc.): button to close the collection creator -->
@ -836,6 +932,10 @@
<string name="qr_scanner_dialog_negative">RIFUZI</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Ĉu vi certe volas forigi %1$s?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->
<string name="delete_tab_and_collection_dialog_message">Forigo de tiu ĉi langeto forigos la tutan kolekton. Vi povas krei novajn kolektojn iam ajn.</string>
<!-- Collection and tab deletion prompt dialog title. Placeholder will be replaced with the collection name. This will show when the last tab from a collection is deleted -->
<string name="delete_tab_and_collection_dialog_title">Ĉu forigi %1$s?</string>
<!-- Tab collection deletion prompt dialog option to delete the collection -->
<string name="tab_collection_dialog_positive">Forigi</string>
<!-- Tab collection deletion prompt dialog option to cancel deleting the collection -->
@ -952,9 +1052,10 @@
<string name="onboarding_whats_new_description">Ĉu vi havas demandojn pri la refasonita %s? Ĉu vi volas scii kio ŝanĝiĝis?</string>
<!-- text for underlined clickable link that is part of "what's new" onboarding card description that links to an FAQ -->
<string name="onboarding_whats_new_description_linktext">Trovu respondojn ĉi tie</string>
<!-- text for the firefox account onboarding card header
The first parameter is the name of the app (e.g. Firefox Preview) -->
<string name="onboarding_firefox_account_header">Eltiru la maksimumon el %s.</string>
<!-- text for the Firefox account onboarding sign in card header -->
<string name="onboarding_account_sign_in_header">Komenci speguli legosignojn, pasvortojn kaj aliajn aferojn per via konto de Firefox.</string>
<!-- Text for the button to learn more about signing in to your Firefox account -->
<string name="onboarding_manual_sign_in_learn_more">Pli da informo</string>
<!-- text for the firefox account onboarding card header when we detect you're already signed in to
another Firefox browser. (The word `Firefox` should not be translated)
The first parameter is the email of the detected user's account -->
@ -1238,6 +1339,8 @@ en la reto kaj kion vi dividas kun ni.</string>
<string name="preferences_passwords_exceptions_description_empty">Nekonservitaj nomoj de uzantoj kaj pasvortoj estos montritaj ĉi tie.</string>
<!-- Description of list of login exceptions that we never save logins for -->
<string name="preferences_passwords_exceptions_description">Nomoj de uzanto kaj pasvortoj por tiuj ĉi retejoj ne estos konservitaj.</string>
<!-- Text on button to remove all saved login exceptions -->
<string name="preferences_passwords_exceptions_remove_all">Forigi ĉiujn esceptojn</string>
<!-- Hint for search box in logins list -->
<string name="preferences_passwords_saved_logins_search">Serĉi legitimilojn</string>
<!-- Option to sort logins list A-Z, alphabetically -->
@ -1276,6 +1379,8 @@ en la reto kaj kion vi dividas kun ni.</string>
<string name="saved_login_copy_username">Kopii nomon de uzanto</string>
<!-- Content Description (for screenreaders etc) read for the button to copy a site in logins -->
<string name="saved_login_copy_site">Kopii retejon</string>
<!-- Content Description (for screenreaders etc) read for the button to open a site in logins -->
<string name="saved_login_open_site">Malfermi retejon en retumilo</string>
<!-- Content Description (for screenreaders etc) read for the button to reveal a password in logins -->
<string name="saved_login_reveal_password">Montri pasvorton</string>
<!-- Content Description (for screenreaders etc) read for the button to hide a password in logins -->
@ -1428,9 +1533,7 @@ en la reto kaj kion vi dividas kun ni.</string>
<string name="saved_login_duplicate">Jam ekzistas legitimilo kun tiu nomo de uzanto</string>
<!-- Synced Tabs -->
<!-- Text displayed when user is not logged into a Firefox Account -->
<string name="synced_tabs_connect_to_sync_account">Konekti per konto de Firefox.</string>
<!-- Text displayed to ask user to connect another device as no devices found with account -->
<!-- Text displayed to ask user to connect another device as no devices found with account -->
<string name="synced_tabs_connect_another_device">Konekti alian aparaton</string>
<!-- Text displayed asking user to re-authenticate -->
@ -1444,6 +1547,9 @@ en la reto kaj kion vi dividas kun ni.</string>
<!-- Text displayed on a button in the synced tabs screen to link users to sign in when a user is not signed in to Firefox Sync -->
<string name="synced_tabs_sign_in_button">Komenci seancon por speguli</string>
<!-- The text displayed when a synced device has no tabs to show in the list of Synced Tabs. -->
<string name="synced_tabs_no_open_tabs">Neniu malfermita langeto</string>
<!-- Top Sites -->
<!-- Title text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_title">Maksimuma nombro de plej vizititaj retejoj atingita</string>
@ -1452,4 +1558,18 @@ en la reto kaj kion vi dividas kun ni.</string>
<!-- Confirmation dialog button text when top sites limit is reached. -->
<string name="top_sites_max_limit_confirmation_button">En ordo, mi komprenis</string>
</resources>
<!-- Label for the show most visited sites preference -->
<string name="top_sites_toggle_top_frecent_sites">Montri pli vizititajn retejojn</string>
<!-- Content description for close button in collection placeholder. -->
<string name="remove_home_collection_placeholder_content_description">Forigi</string>
<!-- depcrecated: text for the firefox account onboarding card header
The first parameter is the name of the app (e.g. Firefox Preview) -->
<string name="onboarding_firefox_account_header">Eltiru la maksimumon el %s.</string>
<!-- Deprecated: No Open Tabs Message Header -->
<string name="no_collections_header1">Kolekti la aferojn kiu gravas por vi</string>
<!-- Deprecated: Label to describe what collections are to a new user without any collections -->
<string name="no_collections_description1">Grupigi similajn serĉojn, retejojn kaj langetojn por pli rapida posta aliro.</string>
</resources>

@ -315,11 +315,11 @@
<!-- Preference for developers -->
<string name="preferences_remote_debugging">USB 経由でリモートデバッグする</string>
<!-- Preference title for switch preference to show search engines -->
<string name="preferences_show_search_engines">検索エンジンを表示</string>
<string name="preferences_show_search_engines">検索エンジンを表示する</string>
<!-- Preference title for switch preference to show search suggestions -->
<string name="preferences_show_search_suggestions">検索語句の候補を表示する</string>
<!-- Preference title for switch preference to show voice search button -->
<string name="preferences_show_voice_search">音声検索を表示</string>
<string name="preferences_show_voice_search">音声検索を表示する</string>
<!-- Preference title for switch preference to show search suggestions also in private mode -->
<string name="preferences_show_search_suggestions_in_private">プライベートセッションで表示する</string>
<!-- Preference title for switch preference to show a clipboard suggestion when searching -->
@ -969,7 +969,7 @@
<!-- Title for Accessibility Text Automatic Size Scaling Preference -->
<string name="preference_accessibility_auto_size_2">フォントサイズを自動調整</string>
<!-- Summary for Accessibility Text Automatic Size Scaling Preference -->
<string name="preference_accessibility_auto_size_summary">フォントサイズは Android の設定に従います。このフォントサイズの管理は無効化されます</string>
<string name="preference_accessibility_auto_size_summary">フォントサイズは Android の設定に従います。ここでフォントサイズを管理するには無効化してください</string>
<!-- Title for the Delete browsing data preference -->
<string name="preferences_delete_browsing_data">ブラウジングデータを削除</string>

@ -1391,9 +1391,9 @@
<string name="logins_biometric_prompt_message_pin">Shkyçni pajisjen tuaj</string>
<!-- Title for Accessibility Force Enable Zoom Preference -->
<string name="preference_accessibility_force_enable_zoom">&lt;em&gt;Zoom&lt;/em&gt; në krejt sajtet</string>
<string name="preference_accessibility_force_enable_zoom">Zoom në krejt sajtet</string>
<!-- Summary for Accessibility Force Enable Zoom Preference -->
<string name="preference_accessibility_force_enable_zoom_summary">Aktivizojeni që të lejoni pickim dhe &lt;em&gt;zoom&lt;/em&gt;, edhe në sajte që e pengojnë këtë gjest.</string>
<string name="preference_accessibility_force_enable_zoom_summary">Aktivizojeni që të lejoni pickim dhe zoom, edhe në sajte që e pengojnë këtë gjest.</string>
<!-- Saved logins sorting strategy menu item -by name- (if selected, it will sort saved logins alphabetically) -->
<string name="saved_logins_sort_strategy_alphabetically">Emrash (A-Z)</string>

@ -518,7 +518,7 @@
<!-- Tab Management -->
<!-- Title of preference that allows a user to auto close tabs after a specified amount of time -->
<string name="preferences_close_tabs">多久後自動關閉分頁</string>
<string name="preferences_close_tabs">自動關閉分頁</string>
<!-- Option for auto closing tabs that will never auto close tabs, always allows user to manually close tabs -->
<string name="close_tabs_manually">手動</string>
<!-- Option for auto closing tabs that will auto close tabs after one day -->

@ -126,6 +126,12 @@
<!-- Customization Settings -->
<string name="pref_home_category" translatable="false">pref_home_category</string>
<!-- Customization Settings -->
<string name="pref_key_website_pull_to_refresh" translatable="false">pref_key_website_pull_to_refresh</string>
<string name="pref_key_dynamic_toolbar" translatable="false">pref_key_dynamic_toolbar</string>
<string name="pref_key_swipe_toolbar_switch_tabs" translatable="false">pref_key_swipe_toolbar_switch_tabs</string>
<string name="pref_key_swipe_toolbar_show_tabs" translatable="false">pref_key_swipe_toolbar_show_tabs</string>
<!-- Tabs Tray Customization Settings -->
<string name="pref_tabs_tray_settings_category" translatable="false">pref_tabs_tray_settings_category</string>
<string name="pref_key_tabs_tray_compact_tab" translatable="false">pref_key_tabs_tray_compact_tab</string>

@ -286,6 +286,8 @@
<string name="preferences_theme">Theme</string>
<!-- Preference for customizing the home screen -->
<string name="preferences_home">Home</string>
<!-- Preference for gestures based actions -->
<string name="preferences_gestures">Gestures</string>
<!-- Preference for customizing the tabs tray -->
<string name="preferences_tabs_tray">Tabs tray</string>
<!-- Preference for customizing the tabs tray FAB -->
@ -463,6 +465,16 @@
<!-- Preference for using following device theme -->
<string name="preference_follow_device_theme">Follow device theme</string>
<!-- Gestures Preferences-->
<!-- Preferences for using pull to refresh in a webpage -->
<string name="preference_gestures_website_pull_to_refresh">Pull to refresh</string>
<!-- Preference for using the dynamic toolbar -->
<string name="preference_gestures_dynamic_toolbar">Scroll to hide toolbar</string>
<!-- Preference for switching tabs by swiping horizontally on the toolbar -->
<string name="preference_gestures_swipe_toolbar_switch_tabs">Swipe toolbar sideways to switch tabs</string>
<!-- Preference for showing the opened tabs by swiping up on the toolbar-->
<string name="preference_gestures_swipe_toolbar_show_tabs">Swipe toolbar up to open tabs</string>
<!-- Library -->
<!-- Option in Library to open Sessions page -->
<string name="library_sessions">Sessions</string>
@ -1548,7 +1560,7 @@
<!-- Title text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_title">Top site limit reached</string>
<!-- Content description text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_content">To add a new top site, remove one. Long press the site and select remove.</string>
<string name="top_sites_max_limit_content_2">To add a new top site, remove one. Touch and hold the site and select remove.</string>
<!-- Confirmation dialog button text when top sites limit is reached. -->
<string name="top_sites_max_limit_confirmation_button">OK, Got It</string>
<!-- Label for the show most visited sites preference -->

@ -104,6 +104,26 @@
android:title="@string/top_sites_toggle_top_frecent_sites" />
</androidx.preference.PreferenceCategory>
<androidx.preference.PreferenceCategory
android:layout="@layout/preference_cat_style"
android:title="@string/preferences_gestures"
app:allowDividerAbove="false"
app:iconSpaceReserved="false">
<androidx.preference.SwitchPreference
android:key="@string/pref_key_website_pull_to_refresh"
android:title="@string/preference_gestures_website_pull_to_refresh" />
<androidx.preference.SwitchPreference
android:key="@string/pref_key_dynamic_toolbar"
android:title="@string/preference_gestures_dynamic_toolbar" />
<androidx.preference.SwitchPreference
android:key="@string/pref_key_swipe_toolbar_switch_tabs"
android:title="@string/preference_gestures_swipe_toolbar_switch_tabs" />
<androidx.preference.SwitchPreference
android:key="@string/pref_key_swipe_toolbar_show_tabs"
android:title="@string/preference_gestures_swipe_toolbar_show_tabs"
app:isPreferenceVisible="false"/>
</androidx.preference.PreferenceCategory>
<androidx.preference.PreferenceCategory
android:key="@string/pref_addons_settings_category"
android:layout="@layout/preference_cat_style"
@ -117,4 +137,5 @@
android:key="@string/pref_key_addons_custom_collection"
android:title="@string/addons_custom_source_collection" />
</androidx.preference.PreferenceCategory>
</androidx.preference.PreferenceScreen>

@ -1,6 +1,7 @@
package org.mozilla.fenix.collections
import io.mockk.MockKAnnotations
import io.mockk.coVerify
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.mockk
@ -69,7 +70,7 @@ class DefaultCollectionCreationControllerTest {
controller.saveCollectionName(tabs, "name")
verify { dismiss() }
verify { tabCollectionStorage.createCollection("name", listOf(session)) }
coVerify { tabCollectionStorage.createCollection("name", listOf(session)) }
verify { metrics.track(Event.CollectionSaved(2, 1)) }
}
@ -117,9 +118,9 @@ class DefaultCollectionCreationControllerTest {
verifyAll {
dismiss()
tabCollectionStorage.renameCollection(collection, "name")
metrics.track(Event.CollectionRenamed)
}
coVerify { tabCollectionStorage.renameCollection(collection, "name") }
}
@Test
@ -162,7 +163,7 @@ class DefaultCollectionCreationControllerTest {
controller.selectCollection(collection, tabs)
verify { dismiss() }
verify { tabCollectionStorage.addTabsToCollection(collection, listOf(session)) }
coVerify { tabCollectionStorage.addTabsToCollection(collection, listOf(session)) }
verify { metrics.track(Event.CollectionTabsAdded(2, 1)) }
}

@ -24,7 +24,7 @@ class TestComponents(private val context: Context) : Components(context) {
core.store,
search.searchEngineManager,
core.webAppShortcutManager,
core.topSiteStorage
core.topSitesStorage
)
}
override val intentProcessors by lazy { mockk<IntentProcessors>(relaxed = true) }

@ -27,5 +27,5 @@ class TestCore(context: Context, crashReporter: CrashReporting) : Core(context,
override val client = mockk<Client>()
override val webAppShortcutManager = mockk<WebAppShortcutManager>()
override val thumbnailStorage = mockk<ThumbnailStorage>()
override val topSiteStorage = mockk<DefaultTopSitesStorage>()
override val topSitesStorage = mockk<DefaultTopSitesStorage>()
}

@ -32,6 +32,7 @@ import mozilla.components.feature.search.SearchUseCases
import mozilla.components.feature.session.SessionFeature
import mozilla.components.feature.session.SessionUseCases
import mozilla.components.feature.tab.collections.TabCollection
import mozilla.components.feature.top.sites.DefaultTopSitesStorage
import mozilla.components.feature.top.sites.TopSitesUseCases
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
import mozilla.components.support.test.rule.MainCoroutineRule
@ -83,6 +84,7 @@ class DefaultBrowserToolbarMenuControllerTest {
@RelaxedMockK private lateinit var readerModeController: ReaderModeController
@MockK private lateinit var sessionFeatureWrapper: ViewBoundFeatureWrapper<SessionFeature>
@RelaxedMockK private lateinit var sessionFeature: SessionFeature
@RelaxedMockK private lateinit var topSitesStorage: DefaultTopSitesStorage
@Before
fun setUp() {
@ -105,6 +107,7 @@ class DefaultBrowserToolbarMenuControllerTest {
every { id } returns R.id.browserFragment
}
every { currentSession.id } returns "1"
every { settings.topSitesMaxLimit } returns 16
val onComplete = slot<() -> Unit>()
every { browserAnimator.captureEngineViewAndDrawStatically(capture(onComplete)) } answers { onComplete.captured.invoke() }
@ -487,7 +490,8 @@ class DefaultBrowserToolbarMenuControllerTest {
bookmarkTapped = bookmarkTapped,
readerModeController = readerModeController,
sessionManager = sessionManager,
sessionFeature = sessionFeatureWrapper
sessionFeature = sessionFeatureWrapper,
topSitesStorage = topSitesStorage
).apply {
ioScope = scope
}

@ -40,7 +40,7 @@ class TabCounterMenuTest {
@Test
fun `all items use primary text color styling`() {
val items = menu.menuItems(showOnly = null)
val items = menu.menuItems(ToolbarPosition.BOTTOM)
assertEquals(4, items.size)
val textItems = items.mapNotNull { it as? TextMenuCandidate }
@ -85,7 +85,7 @@ class TabCounterMenuTest {
@Test
fun `return two new tab items and a close button`() {
val (newTab, newPrivateTab, divider, closeTab) = menu.menuItems(showOnly = null)
val (newTab, newPrivateTab, divider, closeTab) = menu.menuItems(ToolbarPosition.TOP)
assertEquals("New tab", (newTab as TextMenuCandidate).text)
assertEquals("New private tab", (newPrivateTab as TextMenuCandidate).text)

@ -97,7 +97,7 @@ class DefaultDeleteBrowsingDataControllerTest {
fun deleteSitePermissions() = runBlockingTest {
controller.deleteSitePermissions()
verify {
coVerify {
engine.clearData(Engine.BrowsingData.select(Engine.BrowsingData.ALL_SITE_SETTINGS))
permissionStorage.deleteAllSitePermissions()
}

@ -9,7 +9,6 @@ package org.mozilla.fenix.settings.deletebrowsingdata
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import io.mockk.verifyOrder
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestCoroutineDispatcher
@ -72,7 +71,7 @@ class DeleteAndQuitTest {
activity.finish()
}
verify(exactly = 0) {
coVerify(exactly = 0) {
engine.clearData(
Engine.BrowsingData.select(
Engine.BrowsingData.COOKIES
@ -100,7 +99,7 @@ class DeleteAndQuitTest {
deleteAndQuit(activity, this, snackbar)
verify(exactly = 1) {
coVerify(exactly = 1) {
snackbar.show()
engine.clearData(Engine.BrowsingData.allCaches())

@ -7,12 +7,12 @@ package org.mozilla.fenix.settings.quicksettings
import androidx.navigation.NavController
import androidx.navigation.NavDirections
import io.mockk.Runs
import io.mockk.coVerifyOrder
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.spyk
import io.mockk.verify
import io.mockk.verifyOrder
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestCoroutineScope
import kotlinx.coroutines.test.runBlockingTest
@ -179,7 +179,7 @@ class DefaultQuickSettingsControllerTest {
controller.handlePermissionsChange(testPermissions)
advanceUntilIdle()
verifyOrder {
coVerifyOrder {
permissionStorage.updateSitePermissions(testPermissions)
reload(browserSession)
}

@ -3,5 +3,5 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
object AndroidComponents {
const val VERSION = "58.0.20200906130403"
const val VERSION = "58.0.20200908130811"
}

Loading…
Cancel
Save