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 ### Actual behavior
### Does toggling Tracking Protection fix the issue? (Press the lock icon in the toolbar while on the site to see toggle) ### 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 [Firefox for Android](https://play.google.com/store/apps/details?id=org.mozilla.firefox)?
### Can you reproduce in Chrome (or other non-Mozilla browser)? ### 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. --> <!--- 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. 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! ## 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. 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 android.content.Context
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
import androidx.test.espresso.IdlingRegistry
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import mozilla.components.browser.storage.sync.PlacesHistoryStorage import mozilla.components.browser.storage.sync.PlacesHistoryStorage
import okhttp3.mockwebserver.MockWebServer import okhttp3.mockwebserver.MockWebServer
@ -13,8 +14,10 @@ import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.mozilla.fenix.R
import org.mozilla.fenix.helpers.AndroidAssetDispatcher import org.mozilla.fenix.helpers.AndroidAssetDispatcher
import org.mozilla.fenix.helpers.HomeActivityTestRule import org.mozilla.fenix.helpers.HomeActivityTestRule
import org.mozilla.fenix.helpers.RecyclerViewIdlingResource
import org.mozilla.fenix.helpers.TestAssetHelper import org.mozilla.fenix.helpers.TestAssetHelper
import org.mozilla.fenix.helpers.TestHelper.longTapSelectItem import org.mozilla.fenix.helpers.TestHelper.longTapSelectItem
import org.mozilla.fenix.ui.robots.historyMenu import org.mozilla.fenix.ui.robots.historyMenu
@ -30,6 +33,7 @@ import org.mozilla.fenix.ui.robots.navigationToolbar
class HistoryTest { class HistoryTest {
/* ktlint-disable no-blank-line-before-rbrace */ // This imposes unreadable grouping. /* ktlint-disable no-blank-line-before-rbrace */ // This imposes unreadable grouping.
private lateinit var mockWebServer: MockWebServer private lateinit var mockWebServer: MockWebServer
private var historyListIdlingResource: RecyclerViewIdlingResource? = null
@get:Rule @get:Rule
val activityTestRule = HomeActivityTestRule() val activityTestRule = HomeActivityTestRule()
@ -52,6 +56,10 @@ class HistoryTest {
runBlocking { runBlocking {
historyStorage.deleteEverything() historyStorage.deleteEverything()
} }
if (historyListIdlingResource != null) {
IdlingRegistry.getInstance().unregister(historyListIdlingResource!!)
}
} }
@Test @Test
@ -74,6 +82,9 @@ class HistoryTest {
mDevice.waitForIdle() mDevice.waitForIdle()
}.openThreeDotMenu { }.openThreeDotMenu {
}.openHistory { }.openHistory {
historyListIdlingResource =
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list))
IdlingRegistry.getInstance().register(historyListIdlingResource!!)
verifyHistoryMenuView() verifyHistoryMenuView()
verifyVisitedTimeTitle() verifyVisitedTimeTitle()
verifyFirstTestPageTitle("Test_Page_1") verifyFirstTestPageTitle("Test_Page_1")
@ -90,6 +101,9 @@ class HistoryTest {
mDevice.waitForIdle() mDevice.waitForIdle()
}.openThreeDotMenu { }.openThreeDotMenu {
}.openHistory { }.openHistory {
historyListIdlingResource =
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list))
IdlingRegistry.getInstance().register(historyListIdlingResource!!)
}.openThreeDotMenu { }.openThreeDotMenu {
}.clickCopy { }.clickCopy {
verifyCopySnackBarText() verifyCopySnackBarText()
@ -105,6 +119,9 @@ class HistoryTest {
mDevice.waitForIdle() mDevice.waitForIdle()
}.openThreeDotMenu { }.openThreeDotMenu {
}.openHistory { }.openHistory {
historyListIdlingResource =
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list))
IdlingRegistry.getInstance().register(historyListIdlingResource!!)
}.openThreeDotMenu { }.openThreeDotMenu {
}.clickShare { }.clickShare {
verifyShareOverlay() verifyShareOverlay()
@ -123,6 +140,9 @@ class HistoryTest {
mDevice.waitForIdle() mDevice.waitForIdle()
}.openThreeDotMenu { }.openThreeDotMenu {
}.openHistory { }.openHistory {
historyListIdlingResource =
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list))
IdlingRegistry.getInstance().register(historyListIdlingResource!!)
}.openThreeDotMenu { }.openThreeDotMenu {
}.clickOpenInNormalTab { }.clickOpenInNormalTab {
verifyUrl(firstWebPage.url.toString()) verifyUrl(firstWebPage.url.toString())
@ -140,6 +160,9 @@ class HistoryTest {
mDevice.waitForIdle() mDevice.waitForIdle()
}.openThreeDotMenu { }.openThreeDotMenu {
}.openHistory { }.openHistory {
historyListIdlingResource =
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list))
IdlingRegistry.getInstance().register(historyListIdlingResource!!)
}.openThreeDotMenu { }.openThreeDotMenu {
}.clickOpenInPrivateTab { }.clickOpenInPrivateTab {
verifyUrl(firstWebPage.url.toString()) verifyUrl(firstWebPage.url.toString())
@ -157,7 +180,11 @@ class HistoryTest {
mDevice.waitForIdle() mDevice.waitForIdle()
}.openThreeDotMenu { }.openThreeDotMenu {
}.openHistory { }.openHistory {
historyListIdlingResource =
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list))
IdlingRegistry.getInstance().register(historyListIdlingResource!!)
}.openThreeDotMenu { }.openThreeDotMenu {
IdlingRegistry.getInstance().unregister(historyListIdlingResource!!)
}.clickDelete { }.clickDelete {
verifyDeleteSnackbarText("Deleted") verifyDeleteSnackbarText("Deleted")
verifyEmptyHistoryView() verifyEmptyHistoryView()
@ -173,7 +200,11 @@ class HistoryTest {
mDevice.waitForIdle() mDevice.waitForIdle()
}.openThreeDotMenu { }.openThreeDotMenu {
}.openHistory { }.openHistory {
historyListIdlingResource =
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list))
IdlingRegistry.getInstance().register(historyListIdlingResource!!)
clickDeleteHistoryButton() clickDeleteHistoryButton()
IdlingRegistry.getInstance().unregister(historyListIdlingResource!!)
verifyDeleteConfirmationMessage() verifyDeleteConfirmationMessage()
confirmDeleteAllHistory() confirmDeleteAllHistory()
verifyDeleteSnackbarText("Browsing data deleted") verifyDeleteSnackbarText("Browsing data deleted")
@ -190,6 +221,9 @@ class HistoryTest {
mDevice.waitForIdle() mDevice.waitForIdle()
}.openThreeDotMenu { }.openThreeDotMenu {
}.openHistory { }.openHistory {
historyListIdlingResource =
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list))
IdlingRegistry.getInstance().register(historyListIdlingResource!!)
longTapSelectItem(firstWebPage.url) longTapSelectItem(firstWebPage.url)
} }
@ -216,6 +250,9 @@ class HistoryTest {
homeScreen { }.openThreeDotMenu { homeScreen { }.openThreeDotMenu {
}.openHistory { }.openHistory {
historyListIdlingResource =
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list))
IdlingRegistry.getInstance().register(historyListIdlingResource!!)
longTapSelectItem(firstWebPage.url) longTapSelectItem(firstWebPage.url)
openActionBarOverflowOrOptionsMenu(activityTestRule.activity) openActionBarOverflowOrOptionsMenu(activityTestRule.activity)
} }
@ -236,6 +273,9 @@ class HistoryTest {
mDevice.waitForIdle() mDevice.waitForIdle()
}.openThreeDotMenu { }.openThreeDotMenu {
}.openHistory { }.openHistory {
historyListIdlingResource =
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list))
IdlingRegistry.getInstance().register(historyListIdlingResource!!)
longTapSelectItem(firstWebPage.url) longTapSelectItem(firstWebPage.url)
openActionBarOverflowOrOptionsMenu(activityTestRule.activity) openActionBarOverflowOrOptionsMenu(activityTestRule.activity)
} }
@ -259,9 +299,13 @@ class HistoryTest {
}.submitQuery(secondWebPage.url.toString()) { }.submitQuery(secondWebPage.url.toString()) {
}.openThreeDotMenu { }.openThreeDotMenu {
}.openHistory { }.openHistory {
historyListIdlingResource =
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list), 1)
IdlingRegistry.getInstance().register(historyListIdlingResource!!)
longTapSelectItem(firstWebPage.url) longTapSelectItem(firstWebPage.url)
longTapSelectItem(secondWebPage.url) longTapSelectItem(secondWebPage.url)
openActionBarOverflowOrOptionsMenu(activityTestRule.activity) openActionBarOverflowOrOptionsMenu(activityTestRule.activity)
IdlingRegistry.getInstance().unregister(historyListIdlingResource!!)
} }
multipleSelectionToolbar { multipleSelectionToolbar {
@ -282,6 +326,9 @@ class HistoryTest {
mDevice.waitForIdle() mDevice.waitForIdle()
}.openThreeDotMenu { }.openThreeDotMenu {
}.openHistory { }.openHistory {
historyListIdlingResource =
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list))
IdlingRegistry.getInstance().register(historyListIdlingResource!!)
longTapSelectItem(firstWebPage.url) longTapSelectItem(firstWebPage.url)
} }

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

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

@ -29,10 +29,12 @@ import androidx.test.uiautomator.UiObject
import androidx.test.uiautomator.UiSelector import androidx.test.uiautomator.UiSelector
import androidx.test.uiautomator.Until import androidx.test.uiautomator.Until
import org.hamcrest.CoreMatchers.allOf import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.CoreMatchers.anyOf
import org.hamcrest.CoreMatchers.startsWith import org.hamcrest.CoreMatchers.startsWith
import org.hamcrest.Matchers import org.hamcrest.Matchers
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.helpers.SessionLoadedIdlingResource
import org.mozilla.fenix.helpers.TestAssetHelper import org.mozilla.fenix.helpers.TestAssetHelper
import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime
import org.mozilla.fenix.helpers.click import org.mozilla.fenix.helpers.click
@ -122,6 +124,7 @@ class SearchRobot {
class Transition { class Transition {
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
private lateinit var sessionLoadedIdlingResource: SessionLoadedIdlingResource
fun dismiss(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition { fun dismiss(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition {
mDevice.waitForIdle() mDevice.waitForIdle()
@ -139,9 +142,20 @@ class SearchRobot {
} }
fun submitQuery(query: String, interact: BrowserRobot.() -> Unit): BrowserRobot.Transition { fun submitQuery(query: String, interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
sessionLoadedIdlingResource = SessionLoadedIdlingResource()
mDevice.waitForIdle() mDevice.waitForIdle()
browserToolbarEditView().perform(typeText(query + "\n")) 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() BrowserRobot().interact()
return BrowserRobot.Transition() return BrowserRobot.Transition()
} }

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

@ -452,8 +452,9 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
order["org.mozilla.geckoview.COPY"] = 2 order["org.mozilla.geckoview.COPY"] = 2
order["CUSTOM_CONTEXT_MENU_SEARCH"] = 3 order["CUSTOM_CONTEXT_MENU_SEARCH"] = 3
order["CUSTOM_CONTEXT_MENU_SEARCH_PRIVATELY"] = 4 order["CUSTOM_CONTEXT_MENU_SEARCH_PRIVATELY"] = 4
order["org.mozilla.geckoview.SELECT_ALL"] = 5 order["org.mozilla.geckoview.PASTE"] = 5
order["CUSTOM_CONTEXT_MENU_SHARE"] = 6 order["org.mozilla.geckoview.SELECT_ALL"] = 6
order["CUSTOM_CONTEXT_MENU_SHARE"] = 7
return actions.sortedBy { actionName -> return actions.sortedBy { actionName ->
// Sort the actions in our preferred order, putting "other" actions unsorted at the end // 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.Session
import mozilla.components.browser.session.SessionManager import mozilla.components.browser.session.SessionManager
import mozilla.components.browser.state.action.ContentAction 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.selector.findTabOrCustomTabOrSelectedTab
import mozilla.components.browser.state.state.SessionState import mozilla.components.browser.state.state.SessionState
import mozilla.components.browser.state.state.content.DownloadState import mozilla.components.browser.state.state.content.DownloadState
@ -253,13 +254,11 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
BrowserFragmentDirections.actionGlobalTabTrayDialogFragment() BrowserFragmentDirections.actionGlobalTabTrayDialogFragment()
) )
}, },
onCloseTab = { onCloseTab = { closedSession ->
val snapshot = sessionManager.createSessionSnapshot(it) val tab = store.state.findTab(closedSession.id) ?: return@DefaultBrowserToolbarController
val state = snapshot.engineSession?.saveState() val isSelected = tab.id == context.components.core.store.state.selectedTabId
val isSelected =
it.id == context.components.core.store.state.selectedTabId ?: false
val snackbarMessage = if (snapshot.session.private) { val snackbarMessage = if (tab.content.private) {
requireContext().getString(R.string.snackbar_private_tab_closed) requireContext().getString(R.string.snackbar_private_tab_closed)
} else { } else {
requireContext().getString(R.string.snackbar_tab_closed) requireContext().getString(R.string.snackbar_tab_closed)
@ -271,9 +270,9 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
requireContext().getString(R.string.snackbar_deleted_undo), requireContext().getString(R.string.snackbar_deleted_undo),
{ {
sessionManager.add( sessionManager.add(
snapshot.session, closedSession,
isSelected, isSelected,
engineSessionState = state engineSessionState = tab.engineState.engineSessionState
) )
}, },
operation = { } operation = { }
@ -295,7 +294,8 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
openInFenixIntent = openInFenixIntent, openInFenixIntent = openInFenixIntent,
bookmarkTapped = { viewLifecycleOwner.lifecycleScope.launch { bookmarkTapped(it) } }, bookmarkTapped = { viewLifecycleOwner.lifecycleScope.launch { bookmarkTapped(it) } },
scope = viewLifecycleOwner.lifecycleScope, scope = viewLifecycleOwner.lifecycleScope,
tabCollectionStorage = requireComponents.core.tabCollectionStorage tabCollectionStorage = requireComponents.core.tabCollectionStorage,
topSitesStorage = requireComponents.core.topSitesStorage
) )
_browserInteractor = BrowserInteractor( _browserInteractor = BrowserInteractor(
@ -659,9 +659,9 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
} }
} }
view.swipeRefresh.isEnabled = FeatureFlags.pullToRefreshEnabled view.swipeRefresh.isEnabled =
@Suppress("ConstantConditionIf") FeatureFlags.pullToRefreshEnabled && context.settings().isPullToRefreshEnabledInBrowser
if (FeatureFlags.pullToRefreshEnabled) { if (view.swipeRefresh.isEnabled) {
val primaryTextColor = val primaryTextColor =
ThemeManager.resolveAttribute(R.attr.primaryText, context) ThemeManager.resolveAttribute(R.attr.primaryText, context)
view.swipeRefresh.setColorSchemeColors(primaryTextColor) view.swipeRefresh.setColorSchemeColors(primaryTextColor)
@ -964,11 +964,9 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
private fun showQuickSettingsDialog() { private fun showQuickSettingsDialog() {
val session = getSessionById() ?: return val session = getSessionById() ?: return
viewLifecycleOwner.lifecycleScope.launch(Main) { viewLifecycleOwner.lifecycleScope.launch(Main) {
val sitePermissions: SitePermissions? = withContext(IO) { val sitePermissions: SitePermissions? = session.url.toUri().host?.let { host ->
session.url.toUri().host?.let { host -> val storage = requireComponents.core.permissionStorage
val storage = requireContext().components.core.permissionStorage storage.findSitePermissionsBy(host)
storage.findSitePermissionsBy(host)
}
} }
view?.let { view?.let {

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

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

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

@ -54,7 +54,7 @@ class Components(private val context: Context) {
core.store, core.store,
search.searchEngineManager, search.searchEngineManager,
core.webAppShortcutManager, core.webAppShortcutManager,
core.topSiteStorage core.topSitesStorage
) )
} }
val intentProcessors by lazy { 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 pinnedSiteStorage by lazy { PinnedSiteStorage(context) }
val topSiteStorage by lazy { val topSitesStorage by lazy {
val defaultTopSites = mutableListOf<Pair<String, String>>() val defaultTopSites = mutableListOf<Pair<String, String>>()
StrictMode.allowThreadDiskReads().resetPoliciesAfter { StrictMode.allowThreadDiskReads().resetPoliciesAfter {

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

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

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

@ -214,7 +214,8 @@ class BrowserToolbarView(
when (settings.toolbarPosition) { when (settings.toolbarPosition) {
ToolbarPosition.BOTTOM -> { ToolbarPosition.BOTTOM -> {
(view.layoutParams as CoordinatorLayout.LayoutParams).apply { (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 -> { 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 * 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) { fun setScrollFlags(shouldDisableScroll: Boolean = false) {
when (settings.toolbarPosition) { when (settings.toolbarPosition) {
@ -236,7 +238,10 @@ class BrowserToolbarView(
} }
ToolbarPosition.TOP -> { ToolbarPosition.TOP -> {
view.updateLayoutParams<AppBarLayout.LayoutParams> { 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 // Force expand the toolbar so the user is not stuck with a hidden toolbar
expand() expand()
0 0

@ -81,20 +81,41 @@ class TabCounterMenu(
} }
@VisibleForTesting @VisibleForTesting
internal fun menuItems(showOnly: BrowsingMode?): List<MenuCandidate> { internal fun menuItems(showOnly: BrowsingMode): List<MenuCandidate> {
return when (showOnly) { return when (showOnly) {
BrowsingMode.Normal -> listOf(newTabItem) BrowsingMode.Normal -> listOf(newTabItem)
BrowsingMode.Private -> listOf(newPrivateTabItem) 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)) 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 { override fun createView(parent: ViewGroup): View {
val store = parent.context.components.core.store val store = parent.context.components.core.store
val metrics = parent.context.components.analytics.metrics val metrics = parent.context.components.analytics.metrics
val settings = parent.context.components.settings
store.flowScoped(lifecycleOwner) { flow -> store.flowScoped(lifecycleOwner) { flow ->
flow.map { state -> state.getNormalOrPrivateTabs(isPrivate).size } flow.map { state -> state.getNormalOrPrivateTabs(isPrivate).size }
@ -42,7 +43,7 @@ class TabCounterToolbarButton(
} }
val menu = TabCounterMenu(parent.context, metrics, onItemTapped) val menu = TabCounterMenu(parent.context, metrics, onItemTapped)
menu.updateMenu() menu.updateMenu(settings.toolbarPosition)
val view = TabCounter(parent.context).apply { val view = TabCounter(parent.context).apply {
reference = WeakReference(this) reference = WeakReference(this)

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

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

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

@ -58,6 +58,7 @@ class CustomizationFragment : PreferenceFragmentCompat() {
setupTabsTrayCategory() setupTabsTrayCategory()
setupFabCategory() setupFabCategory()
setupHomeCategory() setupHomeCategory()
setupGesturesCategory()
setupAddonsCustomizationCategory() 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() { private fun setupAddonsCustomizationCategory() {
requirePreference<EditTextPreference>(R.string.pref_key_addons_custom_account).apply { requirePreference<EditTextPreference>(R.string.pref_key_addons_custom_account).apply {
text = context.settings().customAddonsAccount text = context.settings().customAddonsAccount

@ -41,7 +41,7 @@ class AboutFragment : Fragment(), AboutPageListener {
private lateinit var headerAppName: String private lateinit var headerAppName: String
private lateinit var appName: String private lateinit var appName: String
private val aboutPageAdapter: AboutPageAdapter = AboutPageAdapter(this) private var aboutPageAdapter: AboutPageAdapter? = AboutPageAdapter(this)
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
@ -58,6 +58,10 @@ class AboutFragment : Fragment(), AboutPageListener {
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (aboutPageAdapter == null) {
aboutPageAdapter = AboutPageAdapter(this)
}
about_list.run { about_list.run {
adapter = aboutPageAdapter adapter = aboutPageAdapter
addItemDecoration( addItemDecoration(
@ -76,7 +80,12 @@ class AboutFragment : Fragment(), AboutPageListener {
) )
populateAboutHeader() populateAboutHeader()
aboutPageAdapter.submitList(populateAboutList()) aboutPageAdapter?.submitList(populateAboutList())
}
override fun onDestroyView() {
super.onDestroyView()
aboutPageAdapter = null
} }
private fun populateAboutHeader() { 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.android.synthetic.main.fragment_delete_browsing_data.view.*
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import mozilla.components.lib.state.ext.flowScoped import mozilla.components.lib.state.ext.flowScoped
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
import org.mozilla.fenix.R import org.mozilla.fenix.R
@ -139,7 +142,7 @@ class DeleteBrowsingDataFragment : Fragment(R.layout.fragment_delete_browsing_da
private fun deleteSelected() { private fun deleteSelected() {
startDeletion() startDeletion()
viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { viewLifecycleOwner.lifecycleScope.launch(IO) {
getCheckboxes().mapIndexed { i, v -> getCheckboxes().mapIndexed { i, v ->
if (v.isChecked) { if (v.isChecked) {
when (i) { when (i) {
@ -152,7 +155,7 @@ class DeleteBrowsingDataFragment : Fragment(R.layout.fragment_delete_browsing_da
} }
} }
launch(Dispatchers.Main) { withContext(Main) {
finishDeletion() finishDeletion()
requireComponents.analytics.metrics.track(Event.ClearedPrivateData) requireComponents.analytics.metrics.track(Event.ClearedPrivateData)
} }

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

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

@ -18,7 +18,6 @@ import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import mozilla.components.feature.sitepermissions.SitePermissions import mozilla.components.feature.sitepermissions.SitePermissions
@ -142,11 +141,9 @@ class SitePermissionsManageExceptionsPhoneFeatureFragment : Fragment() {
private fun updatedSitePermissions(status: SitePermissions.Status) { private fun updatedSitePermissions(status: SitePermissions.Status) {
val updatedSitePermissions = args.sitePermissions.update(args.phoneFeature, status) val updatedSitePermissions = args.sitePermissions.update(args.phoneFeature, status)
viewLifecycleOwner.lifecycleScope.launch(IO) { viewLifecycleOwner.lifecycleScope.launch(Main) {
requireComponents.core.permissionStorage.updateSitePermissions(updatedSitePermissions) 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.LayoutInflater
import android.view.View import android.view.View
import androidx.core.view.isVisible import androidx.core.view.isVisible
import kotlinx.android.synthetic.main.checkbox_item.view.*
import kotlinx.android.synthetic.main.tab_tray_item.view.* import kotlinx.android.synthetic.main.tab_tray_item.view.*
import mozilla.components.browser.tabstray.TabViewHolder import mozilla.components.browser.tabstray.TabViewHolder
import mozilla.components.browser.tabstray.TabsAdapter import mozilla.components.browser.tabstray.TabsAdapter
@ -67,7 +68,11 @@ class FenixTabsAdapter(
override fun onBindViewHolder(holder: TabViewHolder, position: Int) { override fun onBindViewHolder(holder: TabViewHolder, position: Int) {
super.onBindViewHolder(holder, position) super.onBindViewHolder(holder, position)
val newIndex = tabCount - position - 1 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 -> holder.tab?.let { tab ->
showCheckedIfSelected(tab, holder.itemView) showCheckedIfSelected(tab, holder.itemView)
@ -105,7 +110,6 @@ class FenixTabsAdapter(
private fun showCheckedIfSelected(tab: Tab, view: View) { private fun showCheckedIfSelected(tab: Tab, view: View) {
val shouldBeChecked = val shouldBeChecked =
mode is TabTrayDialogFragmentState.Mode.MultiSelect && selectedItems.contains(tab) mode is TabTrayDialogFragmentState.Mode.MultiSelect && selectedItems.contains(tab)
view.checkmark.isVisible = shouldBeChecked
view.selected_mask.isVisible = shouldBeChecked view.selected_mask.isVisible = shouldBeChecked
view.mozac_browser_tabstray_close.isVisible = mode is TabTrayDialogFragmentState.Mode.Normal 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.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import mozilla.components.browser.session.Session import mozilla.components.browser.session.Session
import mozilla.components.browser.state.selector.findTab
import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.browser.thumbnails.loader.ThumbnailLoader import mozilla.components.browser.thumbnails.loader.ThumbnailLoader
import mozilla.components.feature.tab.collections.TabCollection import mozilla.components.feature.tab.collections.TabCollection
@ -260,26 +261,22 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler
} }
private fun showUndoSnackbarForTab(sessionId: String) { 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 val tab = requireComponents.core.store.state.findTab(sessionId) ?: return
?.findSessionById(sessionId)?.let { val session = sessionManager.findSessionById(sessionId) ?: return
sessionManager.createSessionSnapshot(it)
} ?: return
// Check if this is the last tab of this session type // Check if this is the last tab of this session type
val isLastOpenTab = val isLastOpenTab = store.state.tabs.filter { it.content.private == tab.content.private }.size == 1
sessionManager.sessions.filter { snapshot.session.private == it.private }.size == 1
if (isLastOpenTab) { if (isLastOpenTab) {
dismissTabTrayAndNavigateHome(sessionId) dismissTabTrayAndNavigateHome(sessionId)
return return
} }
val state = snapshot.engineSession?.saveState()
val isSelected = sessionId == requireComponents.core.store.state.selectedTabId ?: false 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) getString(R.string.snackbar_private_tab_closed)
} else { } else {
getString(R.string.snackbar_tab_closed) getString(R.string.snackbar_tab_closed)
@ -290,8 +287,8 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler
snackbarMessage, snackbarMessage,
getString(R.string.snackbar_deleted_undo), getString(R.string.snackbar_deleted_undo),
{ {
sessionManager.add(snapshot.session, isSelected, engineSessionState = state) sessionManager.add(session, isSelected, engineSessionState = tab.engineState.engineSessionState)
_tabTrayView?.scrollToTab(snapshot.session.id) _tabTrayView?.scrollToTab(session.id)
}, },
operation = { }, operation = { },
elevation = ELEVATION, elevation = ELEVATION,
@ -378,7 +375,7 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler
val selectedCollection = val selectedCollection =
(list.adapter as CollectionsAdapter).getSelectedCollection() (list.adapter as CollectionsAdapter).getSelectedCollection()
val collection = tabCollectionStorage.cachedTabCollections[selectedCollection] val collection = tabCollectionStorage.cachedTabCollections[selectedCollection]
viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { viewLifecycleOwner.lifecycleScope.launch(Main) {
tabCollectionStorage.addTabsToCollection(collection, sessionList) tabCollectionStorage.addTabsToCollection(collection, sessionList)
it.metrics.track( it.metrics.track(
Event.CollectionTabsAdded( Event.CollectionTabsAdded(
@ -386,10 +383,8 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler
sessionList.size sessionList.size
) )
) )
launch(Main) { tabTrayDialogStore.dispatch(TabTrayDialogFragmentAction.ExitMultiSelectMode)
tabTrayDialogStore.dispatch(TabTrayDialogFragmentAction.ExitMultiSelectMode) dialog.dismiss()
dialog.dismiss()
}
} }
}.setNegativeButton(android.R.string.cancel) { dialog, _ -> }.setNegativeButton(android.R.string.cancel) { dialog, _ ->
tabTrayDialogStore.dispatch(TabTrayDialogFragmentAction.ExitMultiSelectMode) tabTrayDialogStore.dispatch(TabTrayDialogFragmentAction.ExitMultiSelectMode)

@ -9,6 +9,8 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo
import androidx.annotation.IdRes import androidx.annotation.IdRes
import androidx.cardview.widget.CardView import androidx.cardview.widget.CardView
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
@ -266,7 +268,7 @@ class TabTrayView(
// WARNING: Merging the upstream fix for this will cause lot of conflicts! // WARNING: Merging the upstream fix for this will cause lot of conflicts!
// //
// if (hasAccessibilityEnabled) { // if (hasAccessibilityEnabled) {
// tabsAdapter.notifyDataSetChanged() // tabsAdapter.notifyItemRangeChanged(0, tabs.size)
// } // }
if (!hasLoaded) { if (!hasLoaded) {
@ -633,6 +635,22 @@ class TabTrayView(
} else { } else {
view.context?.getString(R.string.open_tab_tray_plural, count.toString()) 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 { private fun updateTabCounter(count: Int): String {

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

@ -103,7 +103,7 @@ class Settings(private val appContext: Context) : PreferencesHolder {
var showTopFrecentSites by featureFlagPreference( var showTopFrecentSites by featureFlagPreference(
appContext.getPreferenceKey(R.string.pref_key_enable_top_frecent_sites), appContext.getPreferenceKey(R.string.pref_key_enable_top_frecent_sites),
default = false, default = true,
featureFlag = FeatureFlags.topFrecentSite featureFlag = FeatureFlags.topFrecentSite
) )
@ -937,4 +937,19 @@ class Settings(private val appContext: Context) : PreferencesHolder {
SavedLoginsSortingStrategyMenu.Item.LastUsedSort.strategyString 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:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path <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" /> 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> </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:backgroundTint="?accent"
android:text="@string/mozac_feature_downloads_button_open" android:text="@string/mozac_feature_downloads_button_open"
android:textAllCaps="false" android:textAllCaps="false"
android:padding="16dp"
android:textColor="?contrastText" android:textColor="?contrastText"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"

@ -52,25 +52,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:contentDescription="@string/mozac_browser_tabstray_open_tab" /> android:contentDescription="@string/mozac_browser_tabstray_open_tab" />
<View <include layout="@layout/checkbox_item" />
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" />
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
@ -104,7 +86,7 @@
android:paddingTop="22dp" android:paddingTop="22dp"
android:textColor="@color/tab_tray_item_text_normal_theme" android:textColor="@color/tab_tray_item_text_normal_theme"
android:textSize="16sp" 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_constraintEnd_toStartOf="@id/mozac_browser_tabstray_close"
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@id/mozac_browser_tabstray_icon_card" app:layout_constraintStart_toEndOf="@id/mozac_browser_tabstray_icon_card"
@ -122,7 +104,7 @@
android:paddingStart="8dp" android:paddingStart="8dp"
android:textColor="@color/tab_tray_item_url_normal_theme" android:textColor="@color/tab_tray_item_url_normal_theme"
android:textSize="14sp" android:textSize="14sp"
tools:text="https://www.google.com" tools:text="example.com"
app:layout_constraintEnd_toStartOf="@id/mozac_browser_tabstray_close" app:layout_constraintEnd_toStartOf="@id/mozac_browser_tabstray_close"
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@id/mozac_browser_tabstray_card" app:layout_constraintStart_toEndOf="@id/mozac_browser_tabstray_card"

@ -51,25 +51,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:contentDescription="@string/mozac_browser_tabstray_open_tab" /> android:contentDescription="@string/mozac_browser_tabstray_open_tab" />
<View <include layout="@layout/checkbox_item" />
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" />
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
@ -105,7 +87,7 @@
android:textColor="@color/tab_tray_item_text_normal_theme" android:textColor="@color/tab_tray_item_text_normal_theme"
android:textSize="14sp" android:textSize="14sp"
android:visibility="visible" 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_constraintEnd_toStartOf="@id/mozac_browser_tabstray_close"
app:layout_constraintStart_toEndOf="@id/mozac_browser_tabstray_icon_card" app:layout_constraintStart_toEndOf="@id/mozac_browser_tabstray_icon_card"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
@ -119,7 +101,7 @@
android:paddingStart="8dp" android:paddingStart="8dp"
android:textColor="@color/tab_tray_item_url_normal_theme" android:textColor="@color/tab_tray_item_url_normal_theme"
android:textSize="14sp" android:textSize="14sp"
tools:text="https://www.google.com" tools:text="example.com"
android:visibility="gone" android:visibility="gone"
app:layout_constraintEnd_toStartOf="@id/mozac_browser_tabstray_close" app:layout_constraintEnd_toStartOf="@id/mozac_browser_tabstray_close"
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"

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

@ -74,6 +74,9 @@
<!-- Text for the negative button --> <!-- Text for the negative button -->
<string name="search_widget_cfr_neg_button_text">Agora non</string> <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 --> <!-- Text for the positive action button -->
<string name="open_in_app_cfr_positive_button_text">Dir a Axustes</string> <string name="open_in_app_cfr_positive_button_text">Dir a Axustes</string>
<!-- Text for the negative action button --> <!-- Text for the negative action button -->

@ -44,6 +44,9 @@
<!-- Content description announcement when exiting multiselect mode in tab tray --> <!-- Content description announcement when exiting multiselect mode in tab tray -->
<string name="tab_tray_exit_multiselect_content_description">Τέλος λειτουργίας πολλαπλής επιλογής</string> <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) --> <!-- About content. The first parameter is the name of the application. (For example: Fenix) -->
<string name="about_content">Το %1$s αναπτύσσεται από τη @fork-maintainers.</string> <string name="about_content">Το %1$s αναπτύσσεται από τη @fork-maintainers.</string>
@ -58,6 +61,9 @@
<!-- Delete session button to erase your history in a private session --> <!-- Delete session button to erase your history in a private session -->
<string name="private_browsing_delete_session">Διαγραφή συνεδρίας</string> <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 --> <!-- Text for the positive button -->
<string name="cfr_pos_button_text">Προσθήκη συντόμευσης</string> <string name="cfr_pos_button_text">Προσθήκη συντόμευσης</string>
<!-- Text for the negative button --> <!-- Text for the negative button -->
@ -68,6 +74,9 @@
<!-- Text for the negative button --> <!-- Text for the negative button -->
<string name="search_widget_cfr_neg_button_text">Όχι τώρα</string> <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 --> <!-- Text for the positive action button -->
<string name="open_in_app_cfr_positive_button_text">Μετάβαση στις ρυθμίσεις</string> <string name="open_in_app_cfr_positive_button_text">Μετάβαση στις ρυθμίσεις</string>
<!-- Text for the negative action button --> <!-- Text for the negative action button -->
@ -259,6 +268,8 @@
<string name="preferences_override_fxa_server">Προσαρμοσμένος διακομιστής λογαριασμού Firefox</string> <string name="preferences_override_fxa_server">Προσαρμοσμένος διακομιστής λογαριασμού Firefox</string>
<!-- Preference to override the Sync token server --> <!-- Preference to override the Sync token server -->
<string name="preferences_override_sync_tokenserver">Προσαρμοσμένος διακομιστής Sync</string> <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 --> <!-- Preference category for account information -->
<string name="preferences_category_account">Λογαριασμός</string> <string name="preferences_category_account">Λογαριασμός</string>
<!-- Preference shown on banner to sign into account --> <!-- Preference shown on banner to sign into account -->
@ -769,8 +780,10 @@
<string name="collections_header">Συλλογές</string> <string name="collections_header">Συλλογές</string>
<!-- Content description (not visible, for screen readers etc.): Opens the collection menu when pressed --> <!-- Content description (not visible, for screen readers etc.): Opens the collection menu when pressed -->
<string name="collection_menu_button_content_description">Μενού συλλογής</string> <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 --> <!-- 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 --> <!-- Title for the "select collection" step of the collection creator -->
<string name="create_collection_select_collection">Επιλογή συλλογής</string> <string name="create_collection_select_collection">Επιλογή συλλογής</string>
<!-- Title for the "name collection" step of the collection creator --> <!-- 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) --> <!-- 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> <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 --> <!-- Text shown in snackbar when user deletes a collection -->
<string name="snackbar_collection_deleted">Η συλλογή διαγράφηκε</string> <string name="snackbar_collection_deleted">Η συλλογή διαγράφηκε</string>
<!-- Text shown in snackbar when user renames a collection --> <!-- Text shown in snackbar when user renames a collection -->
@ -897,6 +912,8 @@
<string name="qr_scanner_dialog_negative">ΑΡΝΗΣΗ</string> <string name="qr_scanner_dialog_negative">ΑΡΝΗΣΗ</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name --> <!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Θέλετε σίγουρα να διαγράψετε το %1$s;</string> <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 --> <!-- 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> <string name="delete_tab_and_collection_dialog_title">Διαγραφή του %1$s;</string>
<!-- Tab collection deletion prompt dialog option to delete the collection --> <!-- Tab collection deletion prompt dialog option to delete the collection -->
@ -1018,14 +1035,22 @@
<string name="onboarding_whats_new_description">Έχετε ερωτήσεις σχετικά με το επανασχεδιασμένο %s; Θέλετε να μάθετε τι έχει αλλάξει;</string> <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 --> <!-- 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> <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 --> <!-- Text for the button to learn more about signing in to your Firefox account -->
<string name="onboarding_manual_sign_in_learn_more">Μάθετε περισσότερα</string> <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 --> <!-- text for the button to confirm automatic sign-in -->
<string name="onboarding_firefox_account_auto_signin_confirm">Ναι, σύνδεση</string> <string name="onboarding_firefox_account_auto_signin_confirm">Ναι, σύνδεση</string>
<!-- text for the automatic sign-in button while signing in is in process --> <!-- text for the automatic sign-in button while signing in is in process -->
<string name="onboarding_firefox_account_signing_in">Σύνδεση…</string> <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 --> <!-- 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> <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 --> <!-- text to display in the snackbar once account is signed-in -->
<string name="onboarding_firefox_account_sync_is_on">Το Sync είναι ενεργό</string> <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 --> <!-- 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> <string name="onboarding_tracking_protection_strict_button">Αυστηρή (προτείνεται)</string>
<!-- text for tracking protection radio button option for strict level of blocking --> <!-- text for tracking protection radio button option for strict level of blocking -->
<string name="onboarding_tracking_protection_strict_option">Αυστηρή</string> <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 --> <!-- text for the private browsing onboarding card header -->
<string name="onboarding_private_browsing_header">Ιδιωτική περιήγηση</string> <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 --> <!-- text for the private browsing onbording card button, that launches settings -->
<string name="onboarding_private_browsing_button">Άνοιγμα ρυθμίσεων</string> <string name="onboarding_private_browsing_button">Άνοιγμα ρυθμίσεων</string>
<!-- text for the privacy notice onboarding card header --> <!-- text for the privacy notice onboarding card header -->
<string name="onboarding_privacy_notice_header">Το απόρρητό σας</string> <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 --> <!-- Text for the button to read the privacy notice -->
<string name="onboarding_privacy_notice_read_button">Διαβάστε τη σημείωση απορρήτου μας</string> <string name="onboarding_privacy_notice_read_button">Διαβάστε τη σημείωση απορρήτου μας</string>
<!-- Content description (not visible, for screen readers etc.): Close onboarding screen --> <!-- Content description (not visible, for screen readers etc.): Close onboarding screen -->
@ -1166,6 +1207,8 @@
<string name="etp_fingerprinters_title">Fingerprinters</string> <string name="etp_fingerprinters_title">Fingerprinters</string>
<!-- Category of trackers (tracking content) that can be blocked by Enhanced Tracking Protection --> <!-- Category of trackers (tracking content) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_tracking_content_title">Περιεχόμενο καταγραφής</string> <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 --> <!-- Enhanced Tracking Protection message that protection is currently on for this site -->
<string name="etp_panel_on">Η προστασία είναι ΕΝΕΡΓΗ για αυτή τη σελίδα</string> <string name="etp_panel_on">Η προστασία είναι ΕΝΕΡΓΗ για αυτή τη σελίδα</string>
<!-- Enhanced Tracking Protection message that protection is currently off for this site --> <!-- 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 --> <!-- Placeholder text for the TextView in the Add to Homescreen dialog -->
<string name="add_to_homescreen_text_placeholder">Όνομα συντόμευσης</string> <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 --> <!-- Preference for managing the settings for logins and passwords in Fenix -->
<string name="preferences_passwords_logins_and_passwords">Συνδέσεις και κωδικοί πρόσβασης</string> <string name="preferences_passwords_logins_and_passwords">Συνδέσεις και κωδικοί πρόσβασης</string>
<!-- Preference for managing the saving of logins and passwords in Fenix --> <!-- Preference for managing the saving of logins and passwords in Fenix -->
@ -1476,6 +1522,8 @@
<!-- Top Sites --> <!-- Top Sites -->
<!-- Title text displayed in the dialog when top sites limit is reached. --> <!-- Title text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_title">Έχετε φτάσει το ανώτατο όριο κορυφαίων ιστοσελίδων</string> <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. --> <!-- Confirmation dialog button text when top sites limit is reached. -->
<string name="top_sites_max_limit_confirmation_button">OK, το κατάλαβα</string> <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 --> <!-- 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> <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) --> <!-- 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> <string name="about_content">%1$s estas kreita de @fork-maintainers.</string>
@ -53,6 +76,28 @@
<!-- Text for the negative button --> <!-- Text for the negative button -->
<string name="search_widget_cfr_neg_button_text">Ne nun</string> <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 --> <!-- Home screen icons - Long press shortcuts -->
<!-- Shortcut action to open new tab --> <!-- Shortcut action to open new tab -->
<string name="home_screen_shortcut_open_new_tab_2">Nova langeto</string> <string name="home_screen_shortcut_open_new_tab_2">Nova langeto</string>
@ -146,14 +191,12 @@
<!-- Search Fragment --> <!-- Search Fragment -->
<!-- Button in the search view that lets a user search by scanning a QR code --> <!-- Button in the search view that lets a user search by scanning a QR code -->
<string name="search_scan_button">Skani</string> <string name="search_scan_button">Skani</string>
<!-- Button in the search view that lets a user search by using a shortcut --> <!-- Button in the search view that lets a user change their search engine -->
<string name="search_shortcuts_button">Ŝparvojoj</string> <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 --> <!-- 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> <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 --> <!-- 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 --> <!-- 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> <string name="awesomebar_clipboard_title">Alglui ligilon el la tondujo</string>
<!-- Button in the search suggestions onboarding that allows search suggestions in private sessions --> <!-- Button in the search suggestions onboarding that allows search suggestions in private sessions -->
@ -241,6 +284,8 @@
<string name="preferences_toolbar">Ilaro</string> <string name="preferences_toolbar">Ilaro</string>
<!-- Preference for changing default theme to dark or light mode --> <!-- Preference for changing default theme to dark or light mode -->
<string name="preferences_theme">Etoso</string> <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 --> <!-- Preference for settings related to visual options -->
<string name="preferences_customize">Personecigi</string> <string name="preferences_customize">Personecigi</string>
<!-- Preference description for banner about signing in --> <!-- Preference description for banner about signing in -->
@ -261,8 +306,8 @@
<string name="developer_tools_category">Iloj por programistoj</string> <string name="developer_tools_category">Iloj por programistoj</string>
<!-- Preference for developers --> <!-- Preference for developers -->
<string name="preferences_remote_debugging">Fora sencimigo per USB</string> <string name="preferences_remote_debugging">Fora sencimigo per USB</string>
<!-- Preference title for switch preference to show search shortcuts --> <!-- Preference title for switch preference to show search engines -->
<string name="preferences_show_search_shortcuts">Montri serĉajn ŝparvojojn</string> <string name="preferences_show_search_engines">Montri serĉilojn</string>
<!-- Preference title for switch preference to show search suggestions --> <!-- Preference title for switch preference to show search suggestions -->
<string name="preferences_show_search_suggestions">Montri serĉajn sugestojn</string> <string name="preferences_show_search_suggestions">Montri serĉajn sugestojn</string>
<!-- Preference title for switch preference to show voice search button --> <!-- Preference title for switch preference to show voice search button -->
@ -280,9 +325,14 @@
<!-- Preference for open links in third party apps --> <!-- Preference for open links in third party apps -->
<string name="preferences_open_links_in_apps">Malfermi ligilojn per programoj</string> <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 --> <!-- Preference for add_ons -->
<string name="preferences_addons">Aldonaĵoj</string> <string name="preferences_addons">Aldonaĵoj</string>
<!-- Preference for notifications -->
<string name="preferences_notifications">Sciigoj</string>
<!-- Account Preferences --> <!-- Account Preferences -->
<!-- Preference for triggering sync --> <!-- Preference for triggering sync -->
<string name="preferences_sync_now">Speguli nun</string> <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" --> <!-- Content description (not visible, for screen readers etc.): "Close button for library settings" -->
<string name="content_description_close_button">Fermi</string> <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 --> <!-- Sessions -->
<!-- Title for the list of tabs --> <!-- Title for the list of tabs -->
<string name="tab_header_label">Malfermitaj langetoj</string> <string name="tab_header_label">Malfermitaj langetoj</string>
@ -462,6 +538,10 @@
<string name="tab_tray_menu_item_save">Konservi en kolekto</string> <string name="tab_tray_menu_item_save">Konservi en kolekto</string>
<!-- Text shown in the menu for sharing all tabs --> <!-- Text shown in the menu for sharing all tabs -->
<string name="tab_tray_menu_item_share">Dividi ĉiujn langetojn</string> <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 --> <!-- Text shown in the menu for closing all tabs -->
<string name="tab_tray_menu_item_close">Fermi ĉiujn langetojn</string> <string name="tab_tray_menu_item_close">Fermi ĉiujn langetojn</string>
<!-- Shortcut action to open new tab --> <!-- Shortcut action to open new tab -->
@ -510,9 +590,14 @@
<!-- Text for the menu button to remove a top site --> <!-- Text for the menu button to remove a top site -->
<string name="remove_top_site">Forigi</string> <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 --> <!-- Postfix for private WebApp titles, placeholder is replaced with app name -->
<string name="pwa_site_controls_title_private">%1$s (Privata reĝimo)</string> <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 --> <!-- History -->
<!-- Text for the button to clear all history --> <!-- Text for the button to clear all history -->
<string name="history_delete_all">Forigi historion</string> <string name="history_delete_all">Forigi historion</string>
@ -551,6 +636,13 @@
<!-- Text shown when no history exists --> <!-- Text shown when no history exists -->
<string name="history_empty_message">Neniu historio estas ĉi tie</string> <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 --> <!-- Crashes -->
<!-- Title text displayed on the tab crash page. This first parameter is the name of the application (For example: Fenix) --> <!-- 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> <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> <string name="bookmark_select_folder">Elekti dosierujon</string>
<!-- Confirmation message for a dialog confirming if the user wants to delete the selected folder --> <!-- 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> <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 --> <!-- 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> <string name="bookmark_delete_folder_snackbar">%1$s forigita</string>
<!-- Screen title for adding a bookmarks folder --> <!-- Screen title for adding a bookmarks folder -->
@ -634,8 +728,10 @@
<!-- Bookmark snackbar message on deletion <!-- Bookmark snackbar message on deletion
The first parameter is the host part of the URL of the bookmark deleted, if any --> 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> <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> <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 --> <!-- Bookmark undo button for deletion snackbar action -->
<string name="bookmark_undo_deletion">MALFARI</string> <string name="bookmark_undo_deletion">MALFARI</string>
@ -703,10 +799,8 @@
<string name="collections_header">Kolektoj</string> <string name="collections_header">Kolektoj</string>
<!-- Content description (not visible, for screen readers etc.): Opens the collection menu when pressed --> <!-- 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> <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 --> <!-- 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 --> <!-- Title for the "select tabs" step of the collection creator -->
<string name="create_collection_select_tabs">Elektitaj langetoj</string> <string name="create_collection_select_tabs">Elektitaj langetoj</string>
<!-- Title for the "select collection" step of the collection creator --> <!-- 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> <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 --> <!-- Text shown in snackbar when multiple tabs have been saved in a collection -->
<string name="create_collection_tabs_saved">Langetoj konservitaj!</string> <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 --> <!-- Text shown in snackbar when one tab has been saved in a collection -->
<string name="create_collection_tab_saved">Langeto konservita!</string> <string name="create_collection_tab_saved">Langeto konservita!</string>
<!-- Content description (not visible, for screen readers etc.): button to close the collection creator --> <!-- 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> <string name="qr_scanner_dialog_negative">RIFUZI</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name --> <!-- 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> <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 --> <!-- Tab collection deletion prompt dialog option to delete the collection -->
<string name="tab_collection_dialog_positive">Forigi</string> <string name="tab_collection_dialog_positive">Forigi</string>
<!-- Tab collection deletion prompt dialog option to cancel deleting the collection --> <!-- 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> <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 --> <!-- 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> <string name="onboarding_whats_new_description_linktext">Trovu respondojn ĉi tie</string>
<!-- text for the firefox account onboarding card header <!-- text for the Firefox account onboarding sign in card header -->
The first parameter is the name of the app (e.g. Firefox Preview) --> <string name="onboarding_account_sign_in_header">Komenci speguli legosignojn, pasvortojn kaj aliajn aferojn per via konto de Firefox.</string>
<string name="onboarding_firefox_account_header">Eltiru la maksimumon el %s.</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 <!-- 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) another Firefox browser. (The word `Firefox` should not be translated)
The first parameter is the email of the detected user's account --> 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> <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 --> <!-- 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> <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 --> <!-- Hint for search box in logins list -->
<string name="preferences_passwords_saved_logins_search">Serĉi legitimilojn</string> <string name="preferences_passwords_saved_logins_search">Serĉi legitimilojn</string>
<!-- Option to sort logins list A-Z, alphabetically --> <!-- 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> <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 --> <!-- Content Description (for screenreaders etc) read for the button to copy a site in logins -->
<string name="saved_login_copy_site">Kopii retejon</string> <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 --> <!-- Content Description (for screenreaders etc) read for the button to reveal a password in logins -->
<string name="saved_login_reveal_password">Montri pasvorton</string> <string name="saved_login_reveal_password">Montri pasvorton</string>
<!-- Content Description (for screenreaders etc) read for the button to hide a password in logins --> <!-- 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> <string name="saved_login_duplicate">Jam ekzistas legitimilo kun tiu nomo de uzanto</string>
<!-- Synced Tabs --> <!-- Synced Tabs -->
<!-- Text displayed when user is not logged into a Firefox Account --> <!-- Text displayed to ask user to connect another device as no devices found with 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 -->
<string name="synced_tabs_connect_another_device">Konekti alian aparaton</string> <string name="synced_tabs_connect_another_device">Konekti alian aparaton</string>
<!-- Text displayed asking user to re-authenticate --> <!-- 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 --> <!-- 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> <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 --> <!-- Top Sites -->
<!-- Title text displayed in the dialog when top sites limit is reached. --> <!-- 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> <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. --> <!-- Confirmation dialog button text when top sites limit is reached. -->
<string name="top_sites_max_limit_confirmation_button">En ordo, mi komprenis</string> <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 --> <!-- Preference for developers -->
<string name="preferences_remote_debugging">USB 経由でリモートデバッグする</string> <string name="preferences_remote_debugging">USB 経由でリモートデバッグする</string>
<!-- Preference title for switch preference to show search engines --> <!-- 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 --> <!-- Preference title for switch preference to show search suggestions -->
<string name="preferences_show_search_suggestions">検索語句の候補を表示する</string> <string name="preferences_show_search_suggestions">検索語句の候補を表示する</string>
<!-- Preference title for switch preference to show voice search button --> <!-- 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 --> <!-- Preference title for switch preference to show search suggestions also in private mode -->
<string name="preferences_show_search_suggestions_in_private">プライベートセッションで表示する</string> <string name="preferences_show_search_suggestions_in_private">プライベートセッションで表示する</string>
<!-- Preference title for switch preference to show a clipboard suggestion when searching --> <!-- Preference title for switch preference to show a clipboard suggestion when searching -->
@ -969,7 +969,7 @@
<!-- Title for Accessibility Text Automatic Size Scaling Preference --> <!-- Title for Accessibility Text Automatic Size Scaling Preference -->
<string name="preference_accessibility_auto_size_2">フォントサイズを自動調整</string> <string name="preference_accessibility_auto_size_2">フォントサイズを自動調整</string>
<!-- Summary for Accessibility Text Automatic Size Scaling Preference --> <!-- 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 --> <!-- Title for the Delete browsing data preference -->
<string name="preferences_delete_browsing_data">ブラウジングデータを削除</string> <string name="preferences_delete_browsing_data">ブラウジングデータを削除</string>

@ -1391,9 +1391,9 @@
<string name="logins_biometric_prompt_message_pin">Shkyçni pajisjen tuaj</string> <string name="logins_biometric_prompt_message_pin">Shkyçni pajisjen tuaj</string>
<!-- Title for Accessibility Force Enable Zoom Preference --> <!-- 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 --> <!-- 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) --> <!-- 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> <string name="saved_logins_sort_strategy_alphabetically">Emrash (A-Z)</string>

@ -518,7 +518,7 @@
<!-- Tab Management --> <!-- Tab Management -->
<!-- Title of preference that allows a user to auto close tabs after a specified amount of time --> <!-- 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 --> <!-- Option for auto closing tabs that will never auto close tabs, always allows user to manually close tabs -->
<string name="close_tabs_manually">手動</string> <string name="close_tabs_manually">手動</string>
<!-- Option for auto closing tabs that will auto close tabs after one day --> <!-- Option for auto closing tabs that will auto close tabs after one day -->

@ -126,6 +126,12 @@
<!-- Customization Settings --> <!-- Customization Settings -->
<string name="pref_home_category" translatable="false">pref_home_category</string> <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 --> <!-- Tabs Tray Customization Settings -->
<string name="pref_tabs_tray_settings_category" translatable="false">pref_tabs_tray_settings_category</string> <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> <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> <string name="preferences_theme">Theme</string>
<!-- Preference for customizing the home screen --> <!-- Preference for customizing the home screen -->
<string name="preferences_home">Home</string> <string name="preferences_home">Home</string>
<!-- Preference for gestures based actions -->
<string name="preferences_gestures">Gestures</string>
<!-- Preference for customizing the tabs tray --> <!-- Preference for customizing the tabs tray -->
<string name="preferences_tabs_tray">Tabs tray</string> <string name="preferences_tabs_tray">Tabs tray</string>
<!-- Preference for customizing the tabs tray FAB --> <!-- Preference for customizing the tabs tray FAB -->
@ -463,6 +465,16 @@
<!-- Preference for using following device theme --> <!-- Preference for using following device theme -->
<string name="preference_follow_device_theme">Follow device theme</string> <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 --> <!-- Library -->
<!-- Option in Library to open Sessions page --> <!-- Option in Library to open Sessions page -->
<string name="library_sessions">Sessions</string> <string name="library_sessions">Sessions</string>
@ -1548,7 +1560,7 @@
<!-- Title text displayed in the dialog when top sites limit is reached. --> <!-- Title text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_title">Top site limit reached</string> <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. --> <!-- 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. --> <!-- Confirmation dialog button text when top sites limit is reached. -->
<string name="top_sites_max_limit_confirmation_button">OK, Got It</string> <string name="top_sites_max_limit_confirmation_button">OK, Got It</string>
<!-- Label for the show most visited sites preference --> <!-- Label for the show most visited sites preference -->

@ -104,6 +104,26 @@
android:title="@string/top_sites_toggle_top_frecent_sites" /> android:title="@string/top_sites_toggle_top_frecent_sites" />
</androidx.preference.PreferenceCategory> </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 <androidx.preference.PreferenceCategory
android:key="@string/pref_addons_settings_category" android:key="@string/pref_addons_settings_category"
android:layout="@layout/preference_cat_style" android:layout="@layout/preference_cat_style"
@ -117,4 +137,5 @@
android:key="@string/pref_key_addons_custom_collection" android:key="@string/pref_key_addons_custom_collection"
android:title="@string/addons_custom_source_collection" /> android:title="@string/addons_custom_source_collection" />
</androidx.preference.PreferenceCategory> </androidx.preference.PreferenceCategory>
</androidx.preference.PreferenceScreen> </androidx.preference.PreferenceScreen>

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

@ -24,7 +24,7 @@ class TestComponents(private val context: Context) : Components(context) {
core.store, core.store,
search.searchEngineManager, search.searchEngineManager,
core.webAppShortcutManager, core.webAppShortcutManager,
core.topSiteStorage core.topSitesStorage
) )
} }
override val intentProcessors by lazy { mockk<IntentProcessors>(relaxed = true) } 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 client = mockk<Client>()
override val webAppShortcutManager = mockk<WebAppShortcutManager>() override val webAppShortcutManager = mockk<WebAppShortcutManager>()
override val thumbnailStorage = mockk<ThumbnailStorage>() 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.SessionFeature
import mozilla.components.feature.session.SessionUseCases import mozilla.components.feature.session.SessionUseCases
import mozilla.components.feature.tab.collections.TabCollection import mozilla.components.feature.tab.collections.TabCollection
import mozilla.components.feature.top.sites.DefaultTopSitesStorage
import mozilla.components.feature.top.sites.TopSitesUseCases import mozilla.components.feature.top.sites.TopSitesUseCases
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
import mozilla.components.support.test.rule.MainCoroutineRule import mozilla.components.support.test.rule.MainCoroutineRule
@ -83,6 +84,7 @@ class DefaultBrowserToolbarMenuControllerTest {
@RelaxedMockK private lateinit var readerModeController: ReaderModeController @RelaxedMockK private lateinit var readerModeController: ReaderModeController
@MockK private lateinit var sessionFeatureWrapper: ViewBoundFeatureWrapper<SessionFeature> @MockK private lateinit var sessionFeatureWrapper: ViewBoundFeatureWrapper<SessionFeature>
@RelaxedMockK private lateinit var sessionFeature: SessionFeature @RelaxedMockK private lateinit var sessionFeature: SessionFeature
@RelaxedMockK private lateinit var topSitesStorage: DefaultTopSitesStorage
@Before @Before
fun setUp() { fun setUp() {
@ -105,6 +107,7 @@ class DefaultBrowserToolbarMenuControllerTest {
every { id } returns R.id.browserFragment every { id } returns R.id.browserFragment
} }
every { currentSession.id } returns "1" every { currentSession.id } returns "1"
every { settings.topSitesMaxLimit } returns 16
val onComplete = slot<() -> Unit>() val onComplete = slot<() -> Unit>()
every { browserAnimator.captureEngineViewAndDrawStatically(capture(onComplete)) } answers { onComplete.captured.invoke() } every { browserAnimator.captureEngineViewAndDrawStatically(capture(onComplete)) } answers { onComplete.captured.invoke() }
@ -487,7 +490,8 @@ class DefaultBrowserToolbarMenuControllerTest {
bookmarkTapped = bookmarkTapped, bookmarkTapped = bookmarkTapped,
readerModeController = readerModeController, readerModeController = readerModeController,
sessionManager = sessionManager, sessionManager = sessionManager,
sessionFeature = sessionFeatureWrapper sessionFeature = sessionFeatureWrapper,
topSitesStorage = topSitesStorage
).apply { ).apply {
ioScope = scope ioScope = scope
} }

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

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

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

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

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

Loading…
Cancel
Save