# Conflicts:
#	app/src/main/java/org/mozilla/fenix/tabstray/browser/AbstractBrowserTrayList.kt
drag-tabs
Steven Knipe 3 years ago
commit 1db1c7d82f

@ -2,7 +2,7 @@
name: "⌛ Performance issue"
about: Create a performance issue if the app is slow or it uses too much memory, disk space, battery, or network data
title: ""
labels: "eng:performance"
labels: "performance"
assignees: ''
---

@ -42,6 +42,7 @@ android {
testInstrumentationRunnerArguments clearPackageData: 'true'
resValue "bool", "IS_DEBUG", "false"
buildConfigField "boolean", "USE_RELEASE_VERSIONING", "false"
buildConfigField "String", "GIT_HASH", "\"\"" // see override in release builds for why it's blank.
// This should be the "public" base URL of AMO.
buildConfigField "String", "AMO_BASE_URL", "\"https://addons.mozilla.org\""
buildConfigField "String", "AMO_COLLECTION_NAME", "\"7dfae8669acc4312a65e8ba5553036\""
@ -82,6 +83,10 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
matchingFallbacks = ['release'] // Use on the "release" build type in dependencies (AARs)
// Changing the build config can cause files that depend on BuildConfig.java to recompile
// so we only set the git hash in release builds to avoid possible recompilation in debug builds.
buildConfigField "String", "GIT_HASH", "\"${Config.getGitHash()}\""
if (gradle.hasProperty("localProperties.autosignReleaseWithDebugKey")) {
signingConfig signingConfigs.debug
}
@ -227,6 +232,7 @@ android {
composeOptions {
kotlinCompilerExtensionVersion = Versions.androidx_compose
}
}
// -------------------------------------------------------------------------------------------------

@ -140,11 +140,12 @@ events:
- https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
- https://github.com/mozilla-mobile/fenix/pull/18143
- https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789
- https://github.com/mozilla-mobile/fenix/pull/21316#issuecomment-944615938
data_sensitivity:
- interaction
notification_emails:
- android-probes@mozilla.com
expires: "2021-12-01"
expires: "2022-12-01"
default_browser_changed:
type: event
description: |
@ -270,11 +271,12 @@ events:
- https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
- https://github.com/mozilla-mobile/fenix/pull/18143
- https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789
- https://github.com/mozilla-mobile/fenix/pull/21316#issuecomment-944615938
data_sensitivity:
- interaction
notification_emails:
- android-probes@mozilla.com
expires: "2021-12-01"
expires: "2022-12-01"
opened_link:
type: event
description: |
@ -300,30 +302,6 @@ events:
- android-probes@mozilla.com
- erichards@mozilla.com
expires: never
tab_counter_menu_action:
type: event
description:
A tab counter menu item was tapped
extra_keys:
item:
description: |
A string containing the name of the item the user tapped. These items
are:
New tab, New private tab, Close tab
bugs:
- https://github.com/mozilla-mobile/fenix/issues/11442
- https://github.com/mozilla-mobile/fenix/issues/19923
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/11533
- https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
- https://github.com/mozilla-mobile/fenix/pull/18143
- https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789
data_sensitivity:
- interaction
notification_emails:
- android-probes@mozilla.com
expires: "2021-12-01"
synced_tab_opened:
type: event
description: |
@ -387,11 +365,12 @@ events:
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/19936
- https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789
- https://github.com/mozilla-mobile/fenix/pull/21316#issuecomment-944615938
data_sensitivity:
- interaction
notification_emails:
- android-probes@mozilla.com
expires: "2021-12-10"
expires: "2022-12-10"
tab_view_changed:
type: event
description: |
@ -620,47 +599,6 @@ toolbar_settings:
- android-probes@mozilla.com
expires: "2022-02-01"
crash_reporter:
opened:
type: event
description: |
The crash reporter was displayed
bugs:
- https://github.com/mozilla-mobile/fenix/issues/1040
- https://github.com/mozilla-mobile/fenix/issues/19923
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1214#issue-264756708
- https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
- https://github.com/mozilla-mobile/fenix/pull/18143
- https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789
data_sensitivity:
- interaction
notification_emails:
- android-probes@mozilla.com
expires: "2021-12-01"
closed:
type: event
description: |
The crash reporter was closed
extra_keys:
crash_submitted:
description: |
A boolean that tells us whether or not the user submitted a crash
report
bugs:
- https://github.com/mozilla-mobile/fenix/issues/1040
- https://github.com/mozilla-mobile/fenix/issues/19923
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1214#issue-264756708
- https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
- https://github.com/mozilla-mobile/fenix/pull/18143
- https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789
data_sensitivity:
- interaction
notification_emails:
- android-probes@mozilla.com
expires: "2021-12-01"
context_menu:
item_tapped:
type: event
@ -1320,6 +1258,7 @@ metrics:
lifetime: application
description: |
How many inactive tabs does the user have.
Value will be 0 if the feature is disabled.
send_in_pings:
- metrics
bugs:
@ -2160,11 +2099,12 @@ custom_tab:
- https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
- https://github.com/mozilla-mobile/fenix/pull/18143
- https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789
- https://github.com/mozilla-mobile/fenix/pull/21316#issuecomment-944615938
data_sensitivity:
- interaction
notification_emails:
- android-probes@mozilla.com
expires: "2021-12-01"
expires: "2022-06-01"
action_button:
type: event
description: |
@ -2177,11 +2117,12 @@ custom_tab:
- https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
- https://github.com/mozilla-mobile/fenix/pull/18143
- https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789
- https://github.com/mozilla-mobile/fenix/pull/21316#issuecomment-944615938
data_sensitivity:
- interaction
notification_emails:
- android-probes@mozilla.com
expires: "2021-12-01"
expires: "2022-06-01"
menu:
type: event
description: |
@ -2194,11 +2135,12 @@ custom_tab:
- https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
- https://github.com/mozilla-mobile/fenix/pull/18143
- https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789
- https://github.com/mozilla-mobile/fenix/pull/21316#issuecomment-944615938
data_sensitivity:
- interaction
notification_emails:
- android-probes@mozilla.com
expires: "2021-12-01"
expires: "2022-06-01"
activation:
identifier:
@ -2267,11 +2209,12 @@ error_page:
- https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
- https://github.com/mozilla-mobile/fenix/pull/18143
- https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789
- https://github.com/mozilla-mobile/fenix/pull/21316#issuecomment-944615938
data_sensitivity:
- interaction
notification_emails:
- android-probes@mozilla.com
expires: "2021-12-01"
expires: "2022-12-01"
sync_auth:
opened:
@ -2697,7 +2640,19 @@ history:
notification_emails:
- android-probes@mozilla.com
expires: "2022-11-01"
search_term_group_tapped:
type: event
description: |
A user tapped on a search term group in history
bugs:
- https://github.com/mozilla-mobile/fenix/issues/22299
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/22300
data_sensitivity:
- interaction
notification_emails:
- android-probes@mozilla.com
expires: "2022-11-01"
reader_mode:
available:
@ -4106,11 +4061,12 @@ app_theme:
- https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
- https://github.com/mozilla-mobile/fenix/pull/18143
- https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789
- https://github.com/mozilla-mobile/fenix/pull/21316#issuecomment-944615938
data_sensitivity:
- interaction
notification_emails:
- android-probes@mozilla.com
expires: "2021-12-01"
expires: "2022-12-01"
pocket:
pocket_top_site_clicked:
@ -4421,11 +4377,12 @@ addons:
- https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
- https://github.com/mozilla-mobile/fenix/pull/18143
- https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789
- https://github.com/mozilla-mobile/fenix/pull/21316#issuecomment-944615938
data_sensitivity:
- interaction
notification_emails:
- android-probes@mozilla.com
expires: "2021-12-01"
expires: "2022-08-01"
open_addon_in_toolbar_menu:
type: event
description: |
@ -4442,11 +4399,12 @@ addons:
- https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
- https://github.com/mozilla-mobile/fenix/pull/18143
- https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789
- https://github.com/mozilla-mobile/fenix/pull/21316#issuecomment-944615938
data_sensitivity:
- interaction
notification_emails:
- android-probes@mozilla.com
expires: "2021-12-01"
expires: "2022-08-01"
open_addon_setting:
type: event
description: |
@ -4542,11 +4500,12 @@ addons:
- https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
- https://github.com/mozilla-mobile/fenix/pull/18143
- https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789
- https://github.com/mozilla-mobile/fenix/pull/21316#issuecomment-944615938
data_sensitivity:
- interaction
notification_emails:
- android-probes@mozilla.com
expires: "2021-12-01"
expires: "2022-08-01"
perf.startup:
cold_main_app_to_first_frame:

@ -1,6 +1,6 @@
<html>
<body>
<a id="link" href="../resources/Globe.svg" download>Page content: Globe.svg</a>
<a id="link" href="../resources/tAJwqaWjJsXS8AhzSninBMCfIZbHBGgcc001lx5DIdDwIcfEgQ6vE5Gb5VgAled17DFZ2A7ZDOHA0NpQPHXXFHPSD4wzCkRWiaOorNI574zLtv4Hjiz6O6T7onmUTGgUQ2YQoiQFyrCrPv8ZB9Kvmt.svg" download>Page content: tAJwqaWjJsXS8AhzSninBMCfIZbHBGgcc001lx5DIdDwIcfEgQ6vE5Gb5VgAled17DFZ2A7ZDOHA0NpQPHXXFHPSD4wzCkRWiaOorNI574zLtv4Hjiz6O6T7onmUTGgUQ2YQoiQFyrCrPv8ZB9Kvmt.svg</a>
<script>
(function() {
document.getElementById("link").click()

@ -17,6 +17,8 @@ object TestAssetHelper {
@Suppress("MagicNumber")
val waitingTime: Long = TimeUnit.SECONDS.toMillis(15)
val waitingTimeShort: Long = TimeUnit.SECONDS.toMillis(1)
// A long enough file name to not fit on a single line in the UI.
const val downloadFileName = "tAJwqaWjJsXS8AhzSninBMCfIZbHBGgcc001lx5DIdDwIcfEgQ6vE5Gb5VgAled17DFZ2A7ZDOHA0NpQPHXXFHPSD4wzCkRWiaOorNI574zLtv4Hjiz6O6T7onmUTGgUQ2YQoiQFyrCrPv8ZB9Kvmt.svg"
data class TestAsset(val url: Uri, val content: String, val title: String)
@ -70,7 +72,7 @@ object TestAssetHelper {
fun getDownloadAsset(server: MockWebServer): TestAsset {
val url = server.url("pages/download.html").toString().toUri()!!
val content = "Page content: Globe.svg"
val content = "Page content: $downloadFileName"
return TestAsset(url, content, "")
}

@ -15,6 +15,7 @@ import org.junit.Test
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
import org.mozilla.fenix.helpers.HomeActivityTestRule
import org.mozilla.fenix.helpers.TestAssetHelper
import org.mozilla.fenix.helpers.TestAssetHelper.downloadFileName
import org.mozilla.fenix.helpers.TestHelper
import org.mozilla.fenix.ui.robots.downloadRobot
import org.mozilla.fenix.ui.robots.navigationToolbar
@ -31,7 +32,6 @@ import org.mozilla.fenix.ui.robots.notificationShade
class DownloadTest {
private val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
private lateinit var mockWebServer: MockWebServer
/* ktlint-disable no-blank-line-before-rbrace */ // This imposes unreadable grouping.
@ -56,7 +56,7 @@ class DownloadTest {
fun tearDown() {
mockWebServer.shutdown()
TestHelper.deleteDownloadFromStorage("Globe.svg")
TestHelper.deleteDownloadFromStorage(downloadFileName)
}
@Test

@ -13,6 +13,7 @@ import mozilla.components.browser.storage.sync.PlacesHistoryStorage
import okhttp3.mockwebserver.MockWebServer
import org.junit.After
import org.junit.Before
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.mozilla.fenix.R
@ -78,6 +79,7 @@ class HistoryTest {
}
}
@Ignore("Failing, see https://github.com/mozilla-mobile/fenix/issues/22304")
@Test
// Test running on beta/release builds in CI:
// caution when making changes to it, so they don't block the builds

@ -30,6 +30,7 @@ import org.mozilla.fenix.helpers.FeatureSettingsHelper
import org.mozilla.fenix.helpers.HomeActivityIntentTestRule
import org.mozilla.fenix.helpers.RecyclerViewIdlingResource
import org.mozilla.fenix.helpers.TestAssetHelper
import org.mozilla.fenix.helpers.TestAssetHelper.downloadFileName
import org.mozilla.fenix.helpers.TestHelper
import org.mozilla.fenix.helpers.TestHelper.appName
import org.mozilla.fenix.helpers.TestHelper.assertExternalAppOpens
@ -69,7 +70,6 @@ class SmokeTest {
private var addonsListIdlingResource: RecyclerViewIdlingResource? = null
private var recentlyClosedTabsListIdlingResource: RecyclerViewIdlingResource? = null
private var readerViewNotification: ViewVisibilityIdlingResource? = null
private val downloadFileName = "Globe.svg"
private val collectionName = "First Collection"
private var bookmarksListIdlingResource: RecyclerViewIdlingResource? = null
private var localeListIdlingResource: RecyclerViewIdlingResource? = null
@ -548,6 +548,7 @@ class SmokeTest {
}
}
@Ignore("Failing intermittently https://github.com/mozilla-mobile/fenix/issues/22256")
@Test
// Verifies setting as default a customized search engine name and URL
fun editCustomSearchEngineTest() {

@ -298,10 +298,16 @@ class TabbedBrowsingTest {
fun verifyEmptyTabTray() {
navigationToolbar {
}.openTabTray {
verifyNormalBrowsingButtonIsSelected(true)
verifyPrivateBrowsingButtonIsSelected(false)
verifySyncedTabsButtonIsSelected(false)
verifyNoTabsOpened()
verifyNewTabButton()
verifyTabTrayOverflowMenu(true)
}.toggleToPrivateTabs {
verifyNormalBrowsingButtonIsSelected(false)
verifyPrivateBrowsingButtonIsSelected(true)
verifySyncedTabsButtonIsSelected(false)
verifyNoTabsOpened()
verifyNewTabButton()
verifyTabTrayOverflowMenu(true)

@ -12,6 +12,7 @@ import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.matcher.IntentMatchers
import androidx.test.espresso.matcher.RootMatchers.isDialog
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
import androidx.test.espresso.matcher.ViewMatchers.withId
@ -132,6 +133,8 @@ private fun assertDownloadNotificationPopup() {
mDevice.waitNotNull(Until.findObjects(By.text("Open")), TestAssetHelper.waitingTime)
onView(withId(R.id.download_dialog_title))
.check(matches(withText(CoreMatchers.containsString("Download completed"))))
onView(withId(R.id.download_dialog_filename))
.check(matches(ViewMatchers.isCompletelyDisplayed()))
}
private fun closePromptButton() =

@ -46,6 +46,7 @@ import org.mozilla.fenix.helpers.click
import org.mozilla.fenix.helpers.clickAtLocationInView
import org.mozilla.fenix.helpers.ext.waitNotNull
import org.mozilla.fenix.helpers.idlingresource.BottomSheetBehaviorStateIdlingResource
import org.mozilla.fenix.helpers.isSelected
import org.mozilla.fenix.helpers.matchers.BottomSheetBehaviorHalfExpandedMaxRatioMatcher
import org.mozilla.fenix.helpers.matchers.BottomSheetBehaviorStateMatcher
@ -66,6 +67,12 @@ class TabDrawerRobot {
}
fun verifyNormalBrowsingButtonIsDisplayed() = assertNormalBrowsingButton()
fun verifyNormalBrowsingButtonIsSelected(isSelected: Boolean) =
assertNormalBrowsingButtonIsSelected(isSelected)
fun verifyPrivateBrowsingButtonIsSelected(isSelected: Boolean) =
assertPrivateBrowsingButtonIsSelected(isSelected)
fun verifySyncedTabsButtonIsSelected(isSelected: Boolean) =
assertSyncedTabsButtonIsSelected(isSelected)
fun verifyExistingOpenTabs(title: String) = assertExistingOpenTabs(title)
fun verifyCloseTabsButton(title: String) = assertCloseTabsButton(title)
@ -450,6 +457,7 @@ private fun normalBrowsingButton() = onView(
)
private fun privateBrowsingButton() = onView(withContentDescription("Private tabs"))
private fun syncedTabsButton() = onView(withContentDescription("Synced tabs"))
private fun newTabButton() = mDevice.findObject(UiSelector().resourceId("$packageName:id/new_tab_button"))
private fun threeDotMenu() = onView(withId(R.id.tab_tray_overflow))
@ -542,6 +550,18 @@ private fun assertNormalBrowsingButton() {
normalBrowsingButton().check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
}
private fun assertNormalBrowsingButtonIsSelected(isSelected: Boolean) {
normalBrowsingButton().check(matches(isSelected(isSelected)))
}
private fun assertPrivateBrowsingButtonIsSelected(isSelected: Boolean) {
privateBrowsingButton().check(matches(isSelected(isSelected)))
}
private fun assertSyncedTabsButtonIsSelected(isSelected: Boolean) {
syncedTabsButton().check(matches(isSelected(isSelected)))
}
private fun assertTabThumbnail() {
assertTrue(
mDevice.findObject(

@ -80,8 +80,8 @@ import org.mozilla.fenix.components.Core
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MozillaProductDetector
import org.mozilla.fenix.components.toolbar.ToolbarPosition
import org.mozilla.fenix.ext.actualInactiveTabs
import org.mozilla.fenix.perf.MarkersActivityLifecycleCallbacks
import org.mozilla.fenix.tabstray.ext.inactiveTabs
import org.mozilla.fenix.utils.Settings
/**
@ -628,7 +628,7 @@ open class FenixApplication : LocaleAwareApplication(), Provider {
tabViewSetting.set(settings.getTabViewPingString())
closeTabSetting.set(settings.getTabTimeoutPingString())
inactiveTabsCount.set(browserStore.state.inactiveTabs.size.toLong())
inactiveTabsCount.set(browserStore.state.actualInactiveTabs(settings).size.toLong())
val installSourcePackage = if (SDK_INT >= Build.VERSION_CODES.R) {
packageManager.getInstallSourceInfo(packageName).installingPackageName

@ -213,7 +213,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
binding = ActivityHomeBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.root.profilerProvider = { components.core.engine.profiler }
ProfilerMarkers.addListenerForOnGlobalLayout(components.core.engine, this, binding.root)
// Must be after we set the content view

@ -156,8 +156,8 @@ class DefaultPagedHistoryProvider(
val historyGroupsInOffset = if (history.isNotEmpty()) {
historyGroups?.filter {
it.items.any { item ->
history.last().visitedAt <= item.visitedAt - visitedAtBuffer &&
item.visitedAt - visitedAtBuffer <= (history.first().visitedAt + visitedAtBuffer)
(history.last().visitedAt - visitedAtBuffer) <= item.visitedAt &&
item.visitedAt <= (history.first().visitedAt + visitedAtBuffer)
}
} ?: emptyList()
} else {

@ -14,7 +14,6 @@ import org.mozilla.fenix.GleanMetrics.AppTheme
import org.mozilla.fenix.GleanMetrics.Autoplay
import org.mozilla.fenix.GleanMetrics.Collections
import org.mozilla.fenix.GleanMetrics.ContextMenu
import org.mozilla.fenix.GleanMetrics.CrashReporter
import org.mozilla.fenix.GleanMetrics.ErrorPage
import org.mozilla.fenix.GleanMetrics.Events
import org.mozilla.fenix.GleanMetrics.History
@ -86,6 +85,7 @@ sealed class Event {
data class HistoryRecentSearchesTapped(val source: String) : Event() {
override val extras = mapOf(History.recentSearchesTappedKeys.pageNumber to source)
}
object HistorySearchTermGroupTapped : Event()
object ReaderModeAvailable : Event()
object ReaderModeOpened : Event()
object ReaderModeClosed : Event()
@ -619,14 +619,8 @@ sealed class Event {
}
}
object CrashReporterOpened : Event()
data class AddonInstalled(val addonId: String) : Event()
data class CrashReporterClosed(val crashSubmitted: Boolean) : Event() {
override val extras: Map<CrashReporter.closedKeys, String>?
get() = mapOf(CrashReporter.closedKeys.crashSubmitted to crashSubmitted.toString())
}
data class BrowserMenuItemTapped(val item: Item) : Event() {
enum class Item {
SETTINGS, HELP, DESKTOP_VIEW_ON, DESKTOP_VIEW_OFF, FIND_IN_PAGE, NEW_TAB,
@ -640,15 +634,6 @@ sealed class Event {
get() = mapOf(Events.browserMenuActionKeys.item to item.toString().lowercase(Locale.ROOT))
}
data class TabCounterMenuItemTapped(val item: Item) : Event() {
enum class Item {
NEW_TAB, NEW_PRIVATE_TAB, CLOSE_TAB
}
override val extras: Map<Events.tabCounterMenuActionKeys, String>?
get() = mapOf(Events.tabCounterMenuActionKeys.item to item.toString().lowercase(Locale.ROOT))
}
object AutoPlaySettingVisited : Event()
data class AutoPlaySettingChanged(val setting: AutoplaySetting) : Event() {

@ -19,7 +19,6 @@ import org.mozilla.fenix.GleanMetrics.BrowserSearch
import org.mozilla.fenix.GleanMetrics.Collections
import org.mozilla.fenix.GleanMetrics.ContextMenu
import org.mozilla.fenix.GleanMetrics.ContextualMenu
import org.mozilla.fenix.GleanMetrics.CrashReporter
import org.mozilla.fenix.GleanMetrics.CreditCards
import org.mozilla.fenix.GleanMetrics.CustomTab
import org.mozilla.fenix.GleanMetrics.CustomizeHome
@ -158,13 +157,6 @@ private val Event.wrapper: EventWrapper<*>?
{ ContextMenu.itemTapped.record(it) },
{ ContextMenu.itemTappedKeys.valueOf(it) }
)
is Event.CrashReporterOpened -> EventWrapper<NoExtraKeys>(
{ CrashReporter.opened.record(it) }
)
is Event.CrashReporterClosed -> EventWrapper(
{ CrashReporter.closed.record(it) },
{ CrashReporter.closedKeys.valueOf(it) }
)
is Event.BrowserMenuItemTapped -> EventWrapper(
{ Events.browserMenuAction.record(it) },
{ Events.browserMenuActionKeys.valueOf(it) }
@ -320,6 +312,9 @@ private val Event.wrapper: EventWrapper<*>?
{ History.recentSearchesTapped.record(it) },
{ History.recentSearchesTappedKeys.valueOf(it) }
)
is Event.HistorySearchTermGroupTapped -> EventWrapper<NoExtraKeys>(
{ History.searchTermGroupTapped.record(it) }
)
is Event.CollectionRenamed -> EventWrapper<NoExtraKeys>(
{ Collections.renamed.record(it) }
)
@ -544,10 +539,6 @@ private val Event.wrapper: EventWrapper<*>?
is Event.VoiceSearchTapped -> EventWrapper<NoExtraKeys>(
{ VoiceSearch.tapped.record(it) }
)
is Event.TabCounterMenuItemTapped -> EventWrapper(
{ Events.tabCounterMenuAction.record(it) },
{ Events.tabCounterMenuActionKeys.valueOf(it) }
)
is Event.OnboardingPrivacyNotice -> EventWrapper<NoExtraKeys>(
{ Onboarding.privacyNotice.record(it) }
)

@ -126,9 +126,6 @@ class DefaultBrowserToolbarController(
override fun handleTabCounterItemInteraction(item: TabCounterMenu.Item) {
when (item) {
is TabCounterMenu.Item.CloseTab -> {
metrics.track(
Event.TabCounterMenuItemTapped(Event.TabCounterMenuItemTapped.Item.CLOSE_TAB)
)
store.state.selectedTab?.let {
// When closing the last tab we must show the undo snackbar in the home fragment
if (store.state.getNormalOrPrivateTabs(it.content.private).count() == 1) {
@ -143,20 +140,12 @@ class DefaultBrowserToolbarController(
}
}
is TabCounterMenu.Item.NewTab -> {
metrics.track(
Event.TabCounterMenuItemTapped(Event.TabCounterMenuItemTapped.Item.NEW_TAB)
)
activity.browsingModeManager.mode = BrowsingMode.Normal
navController.navigate(
BrowserFragmentDirections.actionGlobalHome(focusOnAddressBar = true)
)
}
is TabCounterMenu.Item.NewPrivateTab -> {
metrics.track(
Event.TabCounterMenuItemTapped(
Event.TabCounterMenuItemTapped.Item.NEW_PRIVATE_TAB
)
)
activity.browsingModeManager.mode = BrowsingMode.Private
navController.navigate(
BrowserFragmentDirections.actionGlobalHome(focusOnAddressBar = true)

@ -4,6 +4,7 @@
package org.mozilla.fenix.crashes
import android.util.Log
import androidx.navigation.NavController
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
@ -13,7 +14,6 @@ import kotlinx.coroutines.launch
import mozilla.components.lib.crash.Crash
import org.mozilla.fenix.R
import org.mozilla.fenix.components.Components
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.utils.Settings
@ -25,10 +25,6 @@ class CrashReporterController(
private val settings: Settings
) {
init {
components.analytics.metrics.track(Event.CrashReporterOpened)
}
/**
* Closes the crash reporter fragment and tries to recover the session.
*
@ -82,7 +78,7 @@ class CrashReporterController(
false
}
components.analytics.metrics.track(Event.CrashReporterClosed(didSubmitReport))
Log.i("Crash Reporter", "Report submitted: $didSubmitReport")
return job
}
}

@ -11,10 +11,22 @@ import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.feature.tabs.ext.hasMediaPlayed
import org.mozilla.fenix.home.recenttabs.RecentTab
import org.mozilla.fenix.tabstray.browser.TabGroup
import org.mozilla.fenix.tabstray.browser.maxActiveTime
import org.mozilla.fenix.tabstray.ext.isNormalTabActiveWithSearchTerm
import org.mozilla.fenix.tabstray.ext.isNormalTabInactive
import org.mozilla.fenix.utils.Settings
import java.util.concurrent.TimeUnit
import kotlin.math.max
/**
* The time until which a tab is considered in-active (in days).
*/
const val DEFAULT_ACTIVE_DAYS = 14L
/**
* The maximum time from when a tab was created or accessed until it is considered "inactive".
*/
val maxActiveTime = TimeUnit.DAYS.toMillis(DEFAULT_ACTIVE_DAYS)
/**
* Get the last opened normal tab, last tab with in progress media and last search term group, if available.
*
@ -109,3 +121,23 @@ fun List<TabSessionState>.toSearchGroup(
return groups to remainderTabs
}
/**
* List of all inactive tabs based on [maxActiveTime].
* The user may have disabled the feature so for user interactions consider using the [actualInactiveTabs] method
* or an in place check of the feature status.
*/
val BrowserState.potentialInactiveTabs: List<TabSessionState>
get() = normalTabs.filter { it.isNormalTabInactive(maxActiveTime) }
/**
* List of all inactive tabs based on [maxActiveTime].
* The result will be always be empty if the user disabled the feature.
*/
fun BrowserState.actualInactiveTabs(settings: Settings): List<TabSessionState> {
return if (settings.inactiveTabsAreEnabled) {
potentialInactiveTabs
} else {
emptyList()
}
}

@ -14,6 +14,7 @@ import mozilla.components.browser.state.selector.findNormalTab
import mozilla.components.browser.state.selector.findTab
import mozilla.components.browser.state.selector.selectedNormalTab
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.SearchState
import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.feature.search.ext.parseSearchTerms
import mozilla.components.lib.state.Middleware
@ -141,40 +142,48 @@ class HistoryMetadataMiddleware(
}
}
private fun createHistoryMetadata(context: MiddlewareContext<BrowserState, BrowserAction>, tab: TabSessionState) {
@Suppress("ComplexMethod")
private fun createHistoryMetadata(
context: MiddlewareContext<BrowserState, BrowserAction>,
tab: TabSessionState
) {
val tabParent = tab.getParent(context.store)
val previousUrlIndex = tab.content.history.currentIndex - 1
val tabMetadataHasSearchTerms = !tab.historyMetadata?.searchTerm.isNullOrBlank()
// Obtain search terms and referrer url either from tab parent, from the history stack, or
// from the tab itself.
// At a high level, there are two main cases here - 1) either the tab was opened as a 'new tab'
// via the search results page, or 2) a page was opened in the same tab as the search results page.
// Details about the New Tab case:
// - we obtain search terms via tab's parent (the search results page)
// - however, it's possible that parent changed (e.g. user navigated away from the search
// results page).
// - our approach below is to capture search terms from the parent within the tab.historyMetadata
// state on the first load of the tab, and then rely on this data for subsequent page loads on that tab.
// - this way, once a tab becomes part of the search group, it won't leave this search group
// unless a direct navigation event happens.
//
// At a high level, there are two main cases here:
// 1) The tab was opened as a 'new tab' via the search engine results page (SERP). In this
// case we obtain search terms via the tab's parent (the search results page). However, it's
// possible that the parent changed (e.g. user navigated away from the search results page).
// Our approach below is to capture search terms from the parent within the
// tab.historyMetadata state on the first load of the tab, and then rely on this data for
// subsequent page loads on that tab. This way, once a tab becomes part of the search group,
// it won't leave this group unless a direct navigation event happens.
//
// 2) A page was opened in the same tab as the search results page (navigated to via content).
val (searchTerm, referrerUrl) = when {
// Loading page opened in a New Tab for the first time.
// Page was opened in a new tab. Look for search terms in the parent tab.
tabParent != null && !tabMetadataHasSearchTerms -> {
val searchTerms = tabParent.content.searchTerms.takeUnless { it.isEmpty() }
?: context.state.search.parseSearchTerms(tabParent.content.url)
val searchTerms = findSearchTerms(tabParent, context.state.search)
searchTerms to tabParent.content.url
}
// We only want to inspect the previous url in history if the user navigated via
// web content i.e., they followed a link, not if the user navigated directly via
// toolbar.
// Page was navigated to via content i.e., the user followed a link. Look for search terms in tab history.
!directLoadTriggered && previousUrlIndex >= 0 -> {
// Once a tab is within the search group, only a direct load event (via the toolbar) can change that.
val previousUrl = tab.content.history.items[previousUrlIndex].uri
val (searchTerms, referrerUrl) = if (tabMetadataHasSearchTerms) {
tab.historyMetadata?.searchTerm to tab.historyMetadata?.referrerUrl
tab.historyMetadata?.searchTerm to previousUrl
} else {
val previousUrl = tab.content.history.items[previousUrlIndex].uri
context.state.search.parseSearchTerms(previousUrl) to previousUrl
// Find search terms by checking if page is a SERP or a result opened from a SERP
val searchTerms = findSearchTerms(tab, context.state.search)
if (searchTerms != null) {
searchTerms to null
} else {
context.state.search.parseSearchTerms(previousUrl) to previousUrl
}
}
if (searchTerms != null) {
@ -190,11 +199,8 @@ class HistoryMetadataMiddleware(
tabMetadataHasSearchTerms && !(directLoadTriggered && previousUrlIndex >= 0) -> {
tab.historyMetadata?.searchTerm to tab.historyMetadata?.referrerUrl
}
// We had no search terms, no history stack, and no parent.
// This would be the case for any page loaded directly via the toolbar including
// a search results page itself. For now, the original search results page is not
// part of the search group: https://github.com/mozilla-mobile/fenix/issues/21659.
else -> null to null
// In all other cases (e.g. direct load) find search terms by checking if page is a SERP
else -> findSearchTerms(tab, context.state.search) to null
}
// Sanity check to make sure we don't record a metadata record referring to itself.
@ -218,4 +224,8 @@ class HistoryMetadataMiddleware(
store.state.findTab(it)
}
}
private fun findSearchTerms(tab: TabSessionState, searchState: SearchState): String? {
return tab.content.searchTerms.takeUnless { it.isEmpty() } ?: searchState.parseSearchTerms(tab.content.url)
}
}

@ -42,6 +42,7 @@ class DefaultHistoryController(
when (item) {
is History.Regular -> openToBrowser(item)
is History.Group -> {
metrics.track(Event.HistorySearchTermGroupTapped)
navController.navigate(
HistoryFragmentDirections.actionGlobalHistoryMetadataGroup(
title = item.title,

@ -0,0 +1,43 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.perf
import android.content.Context
import android.graphics.Canvas
import android.util.AttributeSet
import android.widget.LinearLayout
import mozilla.components.concept.base.profiler.Profiler
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.perf.ProfilerMarkers.MEASURE_LAYOUT_DRAW_MARKER_NAME
/**
* A [LinearLayout] that adds profiler markers for various methods. This is intended to be used on
* the root view of [HomeActivity]'s view hierarchy to understand global measure/layout events.
*/
class HomeActivityRootLinearLayout(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
private val profiler: Profiler? = context.components.core.engine.profiler
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val profilerStartTime = profiler?.getProfilerTime()
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
profiler?.addMarker(MEASURE_LAYOUT_DRAW_MARKER_NAME, profilerStartTime, "onMeasure (HomeActivity root)")
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
val profilerStartTime = profiler?.getProfilerTime()
super.onLayout(changed, l, t, r, b)
profiler?.addMarker(MEASURE_LAYOUT_DRAW_MARKER_NAME, profilerStartTime, "onLayout (HomeActivity root)")
}
override fun dispatchDraw(canvas: Canvas?) {
// We instrument dispatchDraw, for drawing children, because LinearLayout never draws itself,
// i.e. it never calls onDraw or draw.
val profilerStartTime = profiler?.getProfilerTime()
super.dispatchDraw(canvas)
profiler?.addMarker(MEASURE_LAYOUT_DRAW_MARKER_NAME, profilerStartTime, "dispatchDraw (HomeActivity root)")
}
}

@ -18,6 +18,8 @@ import mozilla.components.concept.engine.Engine
*/
object ProfilerMarkers {
const val MEASURE_LAYOUT_DRAW_MARKER_NAME = "Measure, Layout, Draw"
fun addListenerForOnGlobalLayout(engine: Engine, activity: Activity, rootView: View) {
// We define the listener in a non-anonymous class to avoid memory leaks with the activity.
val listener = MarkerGlobalLayoutListener(engine, activity::class.simpleName ?: "null")

@ -1,33 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.perf
import android.content.Context
import android.util.AttributeSet
import android.widget.LinearLayout
import mozilla.components.concept.base.profiler.Profiler
private const val DETAIL_TEXT = "RootLinearLayout"
/**
* A [LinearLayout] that adds profiler markers for various methods. This is intended to be used on
* the root view of the view hierarchy to understand global measure/layout events.
*/
class RootLinearLayout(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
var profilerProvider: () -> Profiler? = { null }
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val profilerStartTime = profilerProvider.invoke()?.getProfilerTime()
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
profilerProvider.invoke()?.addMarker("onMeasure", profilerStartTime, DETAIL_TEXT)
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
val profilerStartTime = profilerProvider.invoke()?.getProfilerTime()
super.onLayout(changed, l, t, r, b)
profilerProvider.invoke()?.addMarker("onLayout", profilerStartTime, DETAIL_TEXT)
}
}

@ -0,0 +1,43 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.perf
import android.content.Context
import android.graphics.Canvas
import android.util.AttributeSet
import androidx.constraintlayout.widget.ConstraintLayout
import mozilla.components.concept.base.profiler.Profiler
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.perf.ProfilerMarkers.MEASURE_LAYOUT_DRAW_MARKER_NAME
import org.mozilla.fenix.search.SearchDialogFragment
/**
* Adds markers for measure/layout/draw to the root layout of [SearchDialogFragment].
*/
class SearchDialogFragmentConstraintLayout(context: Context, attrs: AttributeSet) : ConstraintLayout(context, attrs) {
private val profiler: Profiler? = context.components.core.engine.profiler
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val profilerStartTime = profiler?.getProfilerTime()
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
profiler?.addMarker(MEASURE_LAYOUT_DRAW_MARKER_NAME, profilerStartTime, "onMeasure (SearchDialogFragment root)")
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
val profilerStartTime = profiler?.getProfilerTime()
super.onLayout(changed, left, top, right, bottom)
profiler?.addMarker(MEASURE_LAYOUT_DRAW_MARKER_NAME, profilerStartTime, "onLayout (SearchDialogFragment root)")
}
override fun draw(canvas: Canvas?) {
// We instrument draw, rather than onDraw or dispatchDraw, because ConstraintLayout's draw includes
// both of the other methods. If we want to track how long it takes to draw the children,
// we'd get more information by instrumenting them individually.
val profilerStartTime = profiler?.getProfilerTime()
super.draw(canvas)
profiler?.addMarker(MEASURE_LAYOUT_DRAW_MARKER_NAME, profilerStartTime, "draw (SearchDialogFragment root)")
}
}

@ -12,6 +12,7 @@ import mozilla.components.support.base.log.logger.Logger
import org.mozilla.fenix.utils.ManufacturerCodes
private const val FCQN_EDM_STORAGE_PROVIDER_BASE = "com.android.server.enterprise.storage.EdmStorageProviderBase"
private const val INSTRUMENTED_HOOKS_CLASS = "com.android.tools.deploy.instrument.InstrumentationHooks"
/**
* A [StrictMode.OnThreadViolationListener] that recreates
@ -45,7 +46,8 @@ class ThreadPenaltyDeathWithIgnoresListener(
}
private fun shouldViolationBeIgnored(violation: Violation): Boolean =
isSamsungLgEdmStorageProviderStartupViolation(violation)
isSamsungLgEdmStorageProviderStartupViolation(violation) ||
containsInstrumentedHooksClass(violation)
private fun isSamsungLgEdmStorageProviderStartupViolation(violation: Violation): Boolean {
// Root issue: https://github.com/mozilla-mobile/fenix/issues/17920
@ -70,4 +72,13 @@ class ThreadPenaltyDeathWithIgnoresListener(
// issue, we'd catch it on other manufacturers.
return violation.stackTrace.any { it.className == FCQN_EDM_STORAGE_PROVIDER_BASE }
}
private fun containsInstrumentedHooksClass(violation: Violation): Boolean {
// See https://github.com/mozilla-mobile/fenix/issues/21695
// When deploying debug builds from Android Studio then we may hit a DiskReadViolation
// occasionally. There's an upstream fix for this, but the stable version of Android Studio
// still seems to be affected.
// https://cs.android.com/android-studio/platform/tools/base/+/abbbe67087626460e0127d3f5377f9cf896e9941
return violation.stackTrace.any { it.className == INSTRUMENTED_HOOKS_CLASS }
}
}

@ -98,6 +98,7 @@ class AboutFragment : Fragment(), AboutPageListener {
val packageInfo =
requireContext().packageManager.getPackageInfo(requireContext().packageName, 0)
val versionCode = PackageInfoCompat.getLongVersionCode(packageInfo).toString()
val maybeFenixGitHash = if (BuildConfig.GIT_HASH.isNotBlank()) ", ${BuildConfig.GIT_HASH}" else ""
val componentsAbbreviation = getString(R.string.components_abbreviation)
val componentsVersion =
mozilla.components.Build.version + ", " + mozilla.components.Build.gitHash
@ -108,9 +109,10 @@ class AboutFragment : Fragment(), AboutPageListener {
val appServicesVersion = mozilla.components.Build.applicationServicesVersion
String.format(
"%s (Build #%s)\n%s: %s\n%s: %s\n%s: %s",
"%s (Build #%s)%s\n%s: %s\n%s: %s\n%s: %s",
packageInfo.versionName,
versionCode,
maybeFenixGitHash,
componentsAbbreviation,
componentsVersion,
maybeGecko,

@ -21,8 +21,8 @@ import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.home.HomeFragment
import org.mozilla.fenix.tabstray.browser.DEFAULT_ACTIVE_DAYS
import org.mozilla.fenix.tabstray.ext.inactiveTabs
import org.mozilla.fenix.ext.DEFAULT_ACTIVE_DAYS
import org.mozilla.fenix.ext.potentialInactiveTabs
import java.util.concurrent.TimeUnit
interface TabsTrayController {
@ -245,7 +245,7 @@ class DefaultTabsTrayController(
override fun handleDeleteAllInactiveTabs() {
metrics.track(Event.TabsTrayCloseAllInactiveTabs)
browserStore.state.inactiveTabs.map { it.id }.let {
browserStore.state.potentialInactiveTabs.map { it.id }.let {
tabsUseCases.removeTabs(it)
}
showUndoSnackbarForTab(false)

@ -26,6 +26,7 @@ import mozilla.appservices.places.BookmarkRoot
import mozilla.components.browser.state.selector.normalTabs
import mozilla.components.browser.state.selector.privateTabs
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.feature.tabs.tabstray.TabsFeature
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.NavGraphDirections
@ -48,6 +49,7 @@ import org.mozilla.fenix.tabstray.browser.DefaultBrowserTrayInteractor
import org.mozilla.fenix.tabstray.browser.SelectionBannerBinding
import org.mozilla.fenix.tabstray.browser.SelectionBannerBinding.VisibilityModifier
import org.mozilla.fenix.tabstray.browser.SelectionHandleBinding
import org.mozilla.fenix.tabstray.browser.TabSorter
import org.mozilla.fenix.tabstray.ext.anchorWithAction
import org.mozilla.fenix.tabstray.ext.bookmarkMessage
import org.mozilla.fenix.tabstray.ext.collectionMessage
@ -72,6 +74,7 @@ class TabsTrayFragment : AppCompatDialogFragment() {
private val selectionHandleBinding = ViewBoundFeatureWrapper<SelectionHandleBinding>()
private val tabsTrayCtaBinding = ViewBoundFeatureWrapper<TabsTrayInfoBannerBinding>()
private val secureTabsTrayBinding = ViewBoundFeatureWrapper<SecureTabsTrayBinding>()
private val tabsFeature = ViewBoundFeatureWrapper<TabsFeature>()
private val tabsTrayInactiveTabsOnboardingBinding = ViewBoundFeatureWrapper<TabsTrayInactiveTabsOnboardingBinding>()
@VisibleForTesting @Suppress("VariableNaming")
@ -223,6 +226,19 @@ class TabsTrayFragment : AppCompatDialogFragment() {
displayMetrics = requireContext().resources.displayMetrics
)
tabsFeature.set(
feature = TabsFeature(
tabsTray = TabSorter(
requireContext().settings(),
requireContext().components.analytics.metrics,
tabsTrayStore
),
store = requireContext().components.core.store,
),
owner = this,
view = view
)
tabsTrayCtaBinding.set(
feature = TabsTrayInfoBannerBinding(
context = view.context,

@ -29,7 +29,7 @@ import org.mozilla.fenix.R
import org.mozilla.fenix.browser.infobanner.InfoBanner
import org.mozilla.fenix.databinding.ComponentTabstray2Binding
import org.mozilla.fenix.databinding.OnboardingInactiveTabsCfrBinding
import org.mozilla.fenix.tabstray.ext.inactiveTabs
import org.mozilla.fenix.ext.potentialInactiveTabs
import org.mozilla.fenix.utils.Settings
@OptIn(ExperimentalCoroutinesApi::class)
@ -49,7 +49,7 @@ class TabsTrayInactiveTabsOnboardingBinding(
.ifChanged()
.collect {
val inactiveTabsList =
if (settings.inactiveTabsAreEnabled) { store.state.inactiveTabs } else emptyList()
if (settings.inactiveTabsAreEnabled) { store.state.potentialInactiveTabs } else emptyList()
if (inactiveTabsList.isNotEmpty() && shouldShowOnboardingForInactiveTabs()) {
createInactiveCFR()
}

@ -4,11 +4,13 @@
package org.mozilla.fenix.tabstray
import mozilla.components.browser.state.state.ContentState
import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.lib.state.Action
import mozilla.components.lib.state.Middleware
import mozilla.components.lib.state.State
import mozilla.components.lib.state.Store
import org.mozilla.fenix.tabstray.browser.TabGroup
/**
* Value type that represents the state of the tabs tray.
@ -16,13 +18,20 @@ import mozilla.components.lib.state.Store
* @property selectedPage The current page in the tray can be on.
* @property mode Whether the browser tab list is in multi-select mode or not with the set of
* currently selected tabs.
* @property syncing Whether the Synced Tabs feature should fetch the latest tabs from paired
* devices.
* @property inactiveTabs The list of tabs are considered inactive.
* @property searchTermGroups The list of tab groups.
* @property normalTabs The list of normal tabs that do not fall under [inactiveTabs] or [searchTermGroups].
* @property privateTabs The list of tabs that are [ContentState.private].
* @property syncing Whether the Synced Tabs feature should fetch the latest tabs from paired devices.
* @property focusGroupTabId The search group tab id to focus. Defaults to null.
*/
data class TabsTrayState(
val selectedPage: Page = Page.NormalTabs,
val mode: Mode = Mode.Normal,
val inactiveTabs: List<TabSessionState> = emptyList(),
val searchTermGroups: List<TabGroup> = emptyList(),
val normalTabs: List<TabSessionState> = emptyList(),
val privateTabs: List<TabSessionState> = emptyList(),
val syncing: Boolean = false,
val focusGroupTabId: String? = null
) : State {
@ -125,7 +134,27 @@ sealed class TabsTrayAction : Action {
/**
* Removes the [TabsTrayState.focusGroupTabId] of the [TabsTrayState].
*/
object ConsumeFocusGroupTabIdAction : TabsTrayAction()
object ConsumeFocusGroupTabId : TabsTrayAction()
/**
* Updates the list of tabs in [TabsTrayState.inactiveTabs].
*/
data class UpdateInactiveTabs(val tabs: List<TabSessionState>) : TabsTrayAction()
/**
* Updates the list of tab groups in [TabsTrayState.searchTermGroups].
*/
data class UpdateSearchGroupTabs(val groups: List<TabGroup>) : TabsTrayAction()
/**
* Updates the list of tabs in [TabsTrayState.normalTabs].
*/
data class UpdateNormalTabs(val tabs: List<TabSessionState>) : TabsTrayAction()
/**
* Updates the list of tabs in [TabsTrayState.privateTabs].
*/
data class UpdatePrivateTabs(val tabs: List<TabSessionState>) : TabsTrayAction()
}
/**
@ -156,8 +185,16 @@ internal object TabsTrayReducer {
state.copy(syncing = true)
is TabsTrayAction.SyncCompleted ->
state.copy(syncing = false)
is TabsTrayAction.ConsumeFocusGroupTabIdAction ->
is TabsTrayAction.ConsumeFocusGroupTabId ->
state.copy(focusGroupTabId = null)
is TabsTrayAction.UpdateInactiveTabs ->
state.copy(inactiveTabs = action.tabs)
is TabsTrayAction.UpdateSearchGroupTabs ->
state.copy(searchTermGroups = action.groups)
is TabsTrayAction.UpdateNormalTabs ->
state.copy(normalTabs = action.tabs)
is TabsTrayAction.UpdatePrivateTabs ->
state.copy(privateTabs = action.tabs)
}
}
}

@ -30,27 +30,6 @@ abstract class AbstractBrowserTrayList @JvmOverloads constructor(
lateinit var interactor: TabsTrayInteractor
lateinit var tabsTrayStore: TabsTrayStore
/**
* A [TabsFeature] is required for each browser list to ensure one always exists for displaying
* tabs.
*/
abstract val tabsFeature: TabsFeature
// NB: The use cases here are duplicated because there isn't a nicer
// way to share them without a better dependency injection solution.
protected val selectTabUseCase = SelectTabUseCaseWrapper(
context.components.analytics.metrics,
context.components.useCases.tabsUseCases.selectTab
) {
interactor.onBrowserTabSelected()
}
protected val removeTabUseCase = RemoveTabUseCaseWrapper(
context.components.analytics.metrics
) { sessionId ->
interactor.onDeleteTab(sessionId)
}
protected val swipeToDelete by lazy {
SwipeToDeleteBinding(tabsTrayStore)
}

@ -41,7 +41,7 @@ class InactiveTabsAdapter(
) : Adapter(DiffCallback), TabsTray, FeatureNameHolder {
internal lateinit var inactiveTabsInteractor: InactiveTabsInteractor
internal var inActiveTabsCount: Int = 0
private var inActiveTabsCount: Int = 0
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): InactiveTabViewHolder {
val view = LayoutInflater.from(parent.context)

@ -0,0 +1,31 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.tabstray.browser
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map
import mozilla.components.browser.tabstray.TabsTray
import mozilla.components.lib.state.helpers.AbstractBinding
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
import org.mozilla.fenix.tabstray.TabsTrayState
import org.mozilla.fenix.tabstray.TabsTrayStore
/**
* An inactive tabs observer that updates the provided [TabsTray].
*/
class InactiveTabsBinding(
store: TabsTrayStore,
private val tray: TabsTray
) : AbstractBinding<TabsTrayState>(store) {
override suspend fun onState(flow: Flow<TabsTrayState>) {
flow.map { it.inactiveTabs }
.ifChanged()
.collect {
// We pass null for the selected tab id here, because inactive tabs doesn't care.
tray.updateTabs(it, null)
}
}
}

@ -5,19 +5,17 @@
package org.mozilla.fenix.tabstray.browser
import androidx.annotation.VisibleForTesting
import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.browser.tabstray.TabsTray
import org.mozilla.fenix.components.appstate.AppAction
import org.mozilla.fenix.components.AppStore
import org.mozilla.fenix.components.appstate.AppAction.UpdateInactiveExpanded
import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.tabstray.TabsTrayStore
import org.mozilla.fenix.utils.Settings
class InactiveTabsController(
private val browserStore: BrowserStore,
private val tabsTrayStore: TabsTrayStore,
private val appStore: AppStore,
private val tabFilter: (TabSessionState) -> Boolean,
private val tray: TabsTray,
private val metrics: MetricController,
private val settings: Settings
@ -27,7 +25,11 @@ class InactiveTabsController(
* the title showing.
*/
fun updateCardExpansion(isExpanded: Boolean) {
appStore.dispatch(AppAction.UpdateInactiveExpanded(isExpanded))
appStore.dispatch(UpdateInactiveExpanded(isExpanded)).invokeOnCompletion {
// To avoid racing, we read the list of inactive tabs only after we have updated
// the expanded state.
refreshInactiveTabsSection()
}
metrics.track(
when (isExpanded) {
@ -35,8 +37,6 @@ class InactiveTabsController(
false -> Event.TabsTrayInactiveTabsCollapsed
}
)
refreshInactiveTabsSection()
}
/**
@ -70,7 +70,7 @@ class InactiveTabsController(
@VisibleForTesting
internal fun refreshInactiveTabsSection() {
val tabs = browserStore.state.tabs.filter(tabFilter)
tray.updateTabs(tabs, browserStore.state.selectedTabId)
val tabs = tabsTrayStore.state.inactiveTabs
tray.updateTabs(tabs, null)
}
}

@ -7,25 +7,13 @@ package org.mozilla.fenix.tabstray.browser
import android.content.Context
import android.util.AttributeSet
import androidx.recyclerview.widget.ConcatAdapter
import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.browser.tabstray.TabViewHolder
import mozilla.components.feature.tabs.tabstray.TabsFeature
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.tabstray.ext.browserAdapter
import org.mozilla.fenix.tabstray.ext.inactiveTabsAdapter
import org.mozilla.fenix.tabstray.ext.isNormalTabInactive
import java.util.concurrent.TimeUnit
/**
* The time until which a tab is considered in-active (in days).
*/
const val DEFAULT_ACTIVE_DAYS = 14L
/**
* The maximum time from when a tab was created or accessed until it is considered "inactive".
*/
val maxActiveTime = TimeUnit.DAYS.toMillis(DEFAULT_ACTIVE_DAYS)
import org.mozilla.fenix.tabstray.ext.tabGroupAdapter
import org.mozilla.fenix.tabstray.ext.titleHeaderAdapter
class NormalBrowserTrayList @JvmOverloads constructor(
context: Context,
@ -34,26 +22,28 @@ class NormalBrowserTrayList @JvmOverloads constructor(
) : AbstractBrowserTrayList(context, attrs, defStyleAttr) {
private val concatAdapter by lazy { adapter as ConcatAdapter }
private val tabSorter by lazy {
TabSorter(
context.settings(),
context.components.analytics.metrics,
concatAdapter
)
private val inactiveTabsBinding by lazy {
InactiveTabsBinding(tabsTrayStore, concatAdapter.inactiveTabsAdapter)
}
private val inactiveTabsFilter: (TabSessionState) -> Boolean = filter@{
if (!context.settings().inactiveTabsAreEnabled) {
return@filter false
}
it.isNormalTabInactive(maxActiveTime)
private val normalTabsBinding by lazy {
NormalTabsBinding(tabsTrayStore, context.components.core.store, concatAdapter.browserAdapter)
}
private val titleHeaderBinding by lazy {
OtherHeaderBinding(tabsTrayStore) { concatAdapter.titleHeaderAdapter.handleListChanges(it) }
}
private val tabGroupBinding by lazy {
TabGroupBinding(tabsTrayStore) { concatAdapter.tabGroupAdapter.submitList(it) }
}
private val inactiveTabsInteractor by lazy {
DefaultInactiveTabsInteractor(
InactiveTabsController(
context.components.core.store,
tabsTrayStore,
context.components.appStore,
inactiveTabsFilter,
concatAdapter.inactiveTabsAdapter,
context.components.analytics.metrics,
context.settings()
@ -61,13 +51,6 @@ class NormalBrowserTrayList @JvmOverloads constructor(
)
}
override val tabsFeature by lazy {
TabsFeature(
tabSorter,
context.components.core.store,
) { !it.content.private }
}
private val touchHelper by lazy {
TabsTouchHelper(
interactionDelegate = concatAdapter.browserAdapter.interactor,
@ -84,7 +67,10 @@ class NormalBrowserTrayList @JvmOverloads constructor(
concatAdapter.inactiveTabsAdapter.inactiveTabsInteractor = inactiveTabsInteractor
tabsFeature.start()
inactiveTabsBinding.start()
normalTabsBinding.start()
titleHeaderBinding.start()
tabGroupBinding.start()
touchHelper.attachToRecyclerView(this)
}
@ -92,7 +78,10 @@ class NormalBrowserTrayList @JvmOverloads constructor(
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
tabsFeature.stop()
inactiveTabsBinding.stop()
normalTabsBinding.stop()
titleHeaderBinding.stop()
tabGroupBinding.stop()
touchHelper.attachToRecyclerView(null)
}

@ -0,0 +1,33 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.tabstray.browser
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.browser.tabstray.TabsTray
import mozilla.components.lib.state.helpers.AbstractBinding
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
import org.mozilla.fenix.tabstray.TabsTrayState
import org.mozilla.fenix.tabstray.TabsTrayStore
/**
* A normal tabs observer that updates the provided [TabsTray].
*/
class NormalTabsBinding(
store: TabsTrayStore,
private val browserStore: BrowserStore,
private val tabsTray: TabsTray
) : AbstractBinding<TabsTrayState>(store) {
override suspend fun onState(flow: Flow<TabsTrayState>) {
flow.map { it.normalTabs }
.ifChanged()
.collect {
// Getting the selectedTabId from the BrowserStore at a different time might lead to a race.
tabsTray.updateTabs(it, browserStore.state.selectedTabId)
}
}
}

@ -0,0 +1,31 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.tabstray.browser
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import mozilla.components.lib.state.helpers.AbstractBinding
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifAnyChanged
import org.mozilla.fenix.tabstray.TabsTrayState
import org.mozilla.fenix.tabstray.TabsTrayStore
/**
* A tabs observer that informs [showHeader] if an "Other tabs" title should be displayed in the tray.
*/
class OtherHeaderBinding(
store: TabsTrayStore,
private val showHeader: (Boolean) -> Unit
) : AbstractBinding<TabsTrayState>(store) {
override suspend fun onState(flow: Flow<TabsTrayState>) {
flow.ifAnyChanged { arrayOf(it.normalTabs, it.searchTermGroups) }
.collect {
if (it.searchTermGroups.isNotEmpty() && it.normalTabs.isNotEmpty()) {
showHeader(true)
} else {
showHeader(false)
}
}
}
}

@ -8,7 +8,6 @@ import android.content.Context
import android.util.AttributeSet
import androidx.annotation.VisibleForTesting
import androidx.annotation.VisibleForTesting.PACKAGE_PRIVATE
import mozilla.components.feature.tabs.tabstray.TabsFeature
import org.mozilla.fenix.ext.components
class PrivateBrowserTrayList @JvmOverloads constructor(
@ -17,14 +16,10 @@ class PrivateBrowserTrayList @JvmOverloads constructor(
defStyleAttr: Int = 0
) : AbstractBrowserTrayList(context, attrs, defStyleAttr) {
override val tabsFeature by lazy {
// NB: The use cases here are duplicated because there isn't a nicer
// way to share them without a better dependency injection solution.
TabsFeature(
adapter as BrowserTabsAdapter,
context.components.core.store,
) { it.content.private }
private val privateTabsBinding by lazy {
PrivateTabsBinding(tabsTrayStore, context.components.core.store, adapter as BrowserTabsAdapter)
}
private val touchHelper by lazy {
TabsTouchHelper(
interactionDelegate = (adapter as BrowserTabsAdapter).delegate,
@ -37,7 +32,7 @@ class PrivateBrowserTrayList @JvmOverloads constructor(
override fun onAttachedToWindow() {
super.onAttachedToWindow()
tabsFeature.start()
privateTabsBinding.start()
swipeToDelete.start()
adapter?.onAttachedToRecyclerView(this)
@ -49,7 +44,7 @@ class PrivateBrowserTrayList @JvmOverloads constructor(
public override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
tabsFeature.stop()
privateTabsBinding.stop()
swipeToDelete.stop()
// Notify the adapter that it is released from the view preemptively.

@ -0,0 +1,33 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.tabstray.browser
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.browser.tabstray.TabsTray
import mozilla.components.lib.state.helpers.AbstractBinding
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
import org.mozilla.fenix.tabstray.TabsTrayState
import org.mozilla.fenix.tabstray.TabsTrayStore
/**
* A private tabs observer that updates the provided [TabsTray].
*/
class PrivateTabsBinding(
store: TabsTrayStore,
private val browserStore: BrowserStore,
private val tray: TabsTray
) : AbstractBinding<TabsTrayState>(store) {
override suspend fun onState(flow: Flow<TabsTrayState>) {
flow.map { it.privateTabs }
.ifChanged()
.collect {
// Getting the selectedTabId from the BrowserStore at a different time might lead to a race.
tray.updateTabs(it, browserStore.state.selectedTabId)
}
}
}

@ -0,0 +1,29 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.tabstray.browser
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map
import mozilla.components.lib.state.helpers.AbstractBinding
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
import org.mozilla.fenix.tabstray.TabsTrayState
import org.mozilla.fenix.tabstray.TabsTrayStore
/**
* A search-term tab group observer that updates the provided [tray].
*/
class TabGroupBinding(
store: TabsTrayStore,
private val tray: (List<TabGroup>) -> Unit
) : AbstractBinding<TabsTrayState>(store) {
override suspend fun onState(flow: Flow<TabsTrayState>) {
flow.map { it.searchTermGroups }
.ifChanged()
.collect {
tray.invoke(it)
}
}
}

@ -4,20 +4,18 @@
package org.mozilla.fenix.tabstray.browser
import androidx.recyclerview.widget.ConcatAdapter
import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.browser.tabstray.TabsTray
import mozilla.components.feature.tabs.tabstray.TabsFeature
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.ext.maxActiveTime
import org.mozilla.fenix.ext.toSearchGroup
import org.mozilla.fenix.tabstray.ext.browserAdapter
import org.mozilla.fenix.tabstray.TabsTrayAction
import org.mozilla.fenix.tabstray.TabsTrayStore
import org.mozilla.fenix.tabstray.ext.hasSearchTerm
import org.mozilla.fenix.tabstray.ext.inactiveTabsAdapter
import org.mozilla.fenix.tabstray.ext.isActive
import org.mozilla.fenix.tabstray.ext.isNormalTabActiveWithSearchTerm
import org.mozilla.fenix.tabstray.ext.tabGroupAdapter
import org.mozilla.fenix.tabstray.ext.titleHeaderAdapter
import org.mozilla.fenix.utils.Settings
/**
@ -26,36 +24,36 @@ import org.mozilla.fenix.utils.Settings
class TabSorter(
private val settings: Settings,
private val metrics: MetricController,
private val concatAdapter: ConcatAdapter
private val tabsTrayStore: TabsTrayStore? = null
) : TabsTray {
private var shouldReportMetrics: Boolean = true
private val groupsSet = mutableSetOf<String>()
override fun updateTabs(tabs: List<TabSessionState>, selectedTabId: String?) {
val inactiveTabs = tabs.getInactiveTabs(settings)
val searchTermTabs = tabs.getSearchGroupTabs(settings)
val normalTabs = tabs - inactiveTabs - searchTermTabs
val privateTabs = tabs.filter { it.content.private }
val allNormalTabs = tabs - privateTabs
val inactiveTabs = allNormalTabs.getInactiveTabs(settings)
val searchTermTabs = allNormalTabs.getSearchGroupTabs(settings)
val normalTabs = allNormalTabs - inactiveTabs - searchTermTabs
// Private tabs
tabsTrayStore?.dispatch(TabsTrayAction.UpdatePrivateTabs(privateTabs))
// Inactive tabs
concatAdapter.inactiveTabsAdapter.updateTabs(inactiveTabs, selectedTabId)
tabsTrayStore?.dispatch(TabsTrayAction.UpdateInactiveTabs(inactiveTabs))
// Tab groups
// We don't need to provide a selectedId, because the [TabGroupAdapter] has that built-in with support from
// NormalBrowserPageViewHolder.scrollToTab.
val (groups, remainderTabs) = searchTermTabs.toSearchGroup(groupsSet)
groupsSet.clear()
groupsSet.addAll(groups.map { it.searchTerm })
concatAdapter.tabGroupAdapter.submitList(groups)
tabsTrayStore?.dispatch(TabsTrayAction.UpdateSearchGroupTabs(groups))
// Normal tabs.
val totalNormalTabs = (normalTabs + remainderTabs)
concatAdapter.browserAdapter.updateTabs(totalNormalTabs, selectedTabId)
// Normal tab title header.
concatAdapter.titleHeaderAdapter
.handleListChanges(totalNormalTabs.isNotEmpty() && groups.isNotEmpty())
tabsTrayStore?.dispatch(TabsTrayAction.UpdateNormalTabs(totalNormalTabs))
// TODO move this to a middleware in the TabsTrayStore.
if (shouldReportMetrics) {
shouldReportMetrics = false

@ -9,7 +9,7 @@ import mozilla.components.browser.state.selector.privateTabs
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.TabSessionState
import org.mozilla.fenix.ext.toSearchGroup
import org.mozilla.fenix.tabstray.browser.maxActiveTime
import org.mozilla.fenix.ext.maxActiveTime
/**
* The currently selected tab if there's one that is private.
@ -32,12 +32,6 @@ fun BrowserState.findPrivateTab(tabId: String): TabSessionState? {
return privateTabs.firstOrNull { it.id == tabId }
}
/**
* The list of inactive tabs in the tabs tray filtered based on [maxActiveTime].
*/
val BrowserState.inactiveTabs: List<TabSessionState>
get() = normalTabs.filter { it.isNormalTabInactive(maxActiveTime) }
/**
* The list of normal tabs in the tabs tray filtered appropriately based on feature flags.
*/

@ -21,11 +21,11 @@ import org.mozilla.fenix.tabstray.TabsTrayAction
import org.mozilla.fenix.tabstray.TabsTrayInteractor
import org.mozilla.fenix.tabstray.TabsTrayStore
import org.mozilla.fenix.tabstray.browser.containsTabId
import org.mozilla.fenix.tabstray.browser.maxActiveTime
import org.mozilla.fenix.ext.maxActiveTime
import org.mozilla.fenix.tabstray.ext.browserAdapter
import org.mozilla.fenix.tabstray.ext.defaultBrowserLayoutColumns
import org.mozilla.fenix.tabstray.ext.getNormalTrayTabs
import org.mozilla.fenix.tabstray.ext.inactiveTabs
import org.mozilla.fenix.ext.potentialInactiveTabs
import org.mozilla.fenix.tabstray.ext.titleHeaderAdapter
import org.mozilla.fenix.tabstray.ext.inactiveTabsAdapter
import org.mozilla.fenix.tabstray.ext.isNormalTabActiveWithSearchTerm
@ -92,7 +92,7 @@ class NormalBrowserPageViewHolder(
// Update tabs into the inactive adapter.
if (inactiveTabsAreEnabled && selectedTab.isNormalTabInactive(maxActiveTime)) {
val inactiveTabsList = browserStore.state.inactiveTabs
val inactiveTabsList = browserStore.state.potentialInactiveTabs
// We want to expand the inactive section first before we want to fire our scroll observer.
appStore.dispatch(AppAction.UpdateInactiveExpanded(true))
@ -135,7 +135,7 @@ class NormalBrowserPageViewHolder(
layoutManager.scrollToPosition(indexToScrollTo)
if (focusGroupTabId != null) {
tabsTrayStore.dispatch(TabsTrayAction.ConsumeFocusGroupTabIdAction)
tabsTrayStore.dispatch(TabsTrayAction.ConsumeFocusGroupTabId)
}
return@observeFirstInsert
}

@ -1,7 +1,7 @@
<!-- 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/. -->
<org.mozilla.fenix.perf.RootLinearLayout
<org.mozilla.fenix.perf.HomeActivityRootLinearLayout
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"
@ -25,4 +25,4 @@
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph" />
</org.mozilla.fenix.perf.RootLinearLayout>
</org.mozilla.fenix.perf.HomeActivityRootLinearLayout>

@ -8,7 +8,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?foundation"
android:paddingBottom="4dp">
android:paddingBottom="12dp">
<ImageView
android:id="@+id/download_dialog_icon"
@ -55,17 +55,19 @@
<TextView
android:id="@+id/download_dialog_filename"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="3dp"
android:layout_marginEnd="16dp"
android:layout_marginTop="8dp"
android:paddingStart="5dp"
android:paddingTop="4dp"
android:paddingEnd="5dp"
android:textColor="?primaryText"
app:layout_constraintStart_toEndOf="@id/download_dialog_icon"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/download_dialog_title"
tools:text="Firefox_Preview_v2.1.apk" />
tools:text="l38ID7Xze57vCac8C9oD7Z2LXzBw00HDiw7XR6ZDu5G5O8uyODAVEOTS2PrZt8OoBM77CmaFyrdGxUODuEWwpfzwnTsTTRcGDsr6Cez4Q7DK0Kr0KJIRVAFbV4czwMeiI25FIml6QCMvQR8nBZHe1oUPQn23BplLC4c3iXGvuEBGEhyU81UpqqTSwU5tfxZ7mBOYcQUqYNG0A7ixekg9awVeq8PncVdCZKLA0hXgJEW4.svg" />
<com.google.android.material.button.MaterialButton
android:id="@+id/download_dialog_action_button"
@ -73,11 +75,9 @@
android:layout_width="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="8dp"
android:backgroundTint="?accent"
android:text="@string/mozac_feature_downloads_button_open"
android:textAllCaps="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/download_dialog_filename" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -59,7 +59,7 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/about_content"
app:layout_constraintWidth_percent="0.8"
tools:text="1.0.x (Build #x)\nAC: 1.0.0\nGV: 69.x-x\nAS: 1.0.0" />
tools:text="1.0.x (Build #x), 0d7383fd2\nAC: 1.0.0, 96ae23b628\nGV: 69.x-x\nAS: 1.0.0" />
<TextView
android:id="@+id/build_date"

@ -2,7 +2,7 @@
<!-- 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/. -->
<androidx.constraintlayout.widget.ConstraintLayout
<org.mozilla.fenix.perf.SearchDialogFragmentConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
@ -190,4 +190,4 @@
app:layout_constraintBottom_toBottomOf="@id/pill_wrapper"
app:layout_constraintStart_toEndOf="@id/qr_scan_button"
app:layout_constraintTop_toTopOf="@id/pill_wrapper" />
</androidx.constraintlayout.widget.ConstraintLayout>
</org.mozilla.fenix.perf.SearchDialogFragmentConstraintLayout>

@ -23,7 +23,7 @@
android:paddingTop="1dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/showAllBookmarksButton"
app:layout_constraintBottom_toBottomOf="parent" />
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/showAllBookmarksButton"

@ -1258,6 +1258,10 @@
<string name="qr_scanner_dialog_positive">PERMETTE</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">RICUSÀ</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Lindirizzi web ùn sò micca accettevule.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">Vai</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Site sicuru di vulè squassà %1$s ?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -1102,7 +1102,7 @@
<!-- Content description (not visible, for screen readers etc.): Opens the collection menu when pressed -->
<string name="collection_menu_button_content_description">Dewislen casgliadau</string>
<!-- Label to describe what collections are to a new user without any collections -->
<string name="no_collections_description2">Casglwch y pethau sydd o bwys i chi.\nCrynhowch ynghyd chwiliadau, gwefannau a thabiau tebyg i gael mynediad cyflym yn hwyrach.</string>
<string name="no_collections_description2">Casglwch y pethau sydd o bwys i chi.\nCasglwch ynghyd chwiliadau, gwefannau a thabiau tebyg i gael mynediad cyflym yn nes ymlaen.</string>
<!-- Title for the "select tabs" step of the collection creator -->
<string name="create_collection_select_tabs">Dewis Tabiau</string>
<!-- Title for the "select collection" step of the collection creator -->
@ -1431,7 +1431,7 @@ Fodd bynnag, gall fod yn llai sefydlog. Llwythwch ein porwr Beta i gael profiad
<!-- Onboarding theme -->
<!-- text for the theme picker onboarding card header -->
<string name="onboarding_theme_picker_header">Dewis eich thema</string>
<string name="onboarding_theme_picker_header">Dewiswch eich thema</string>
<!-- text for the theme picker onboarding card description -->
<string name="onboarding_theme_picker_description_2">Arbedwch ychydig o fatri ach golwg trwy alluogir modd tywyll.</string>
<!-- Automatic theme setting (will follow device setting) -->

@ -1280,6 +1280,10 @@
<string name="qr_scanner_dialog_positive">ERLAUBEN</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">VERWEIGERN</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Internetadresse ungültig.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">OK</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Soll %1$s wirklich gelöscht werden?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -1248,6 +1248,10 @@
<string name="qr_scanner_dialog_positive">DOWÓLIŚ</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">WÓTPOKAZAŚ</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Webadresa njejo płaśiwa.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">W pórěźe</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Cośo napšawdu %1$s lašowaś?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -1261,6 +1261,10 @@
<string name="qr_scanner_dialog_positive">ΑΠΟΔΟΧΗ</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">ΑΡΝΗΣΗ</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Μη έγκυρη διεύθυνση ιστού.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">OK</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Θέλετε σίγουρα να διαγράψετε το %1$s;</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -123,6 +123,10 @@
<string moz:removedIn="95" name="tab_tray_inactive_turn_on_auto_close_button" tools:ignore="UnusedResources">Turn on auto close</string>
<!-- Text for turn on auto close tabs button in the auto-close dialog of the inactive tabs. -->
<string name="tab_tray_inactive_turn_on_auto_close_button_2">Turn on auto-close</string>
<!-- Home screen icons - Long press shortcuts -->
<!-- Shortcut action to open new tab -->
<string name="home_screen_shortcut_open_new_tab_2">New tab</string>
@ -1234,6 +1238,10 @@
<string name="qr_scanner_dialog_positive">ALLOW</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">DENY</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Web address not valid.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">OK</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Are you sure you want to delete %1$s?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->
@ -2000,16 +2008,30 @@
<!-- A call to action below the description to allow the user to turn on the auto closing of inactive tabs. -->
<string name="inactive_tabs_auto_close_message_action" tools:ignore="UnusedResources">TURN ON AUTO CLOSE</string>
<!-- Text for the snackbar to confirm auto-close is enabled for inactive tabs -->
<string name="inactive_tabs_auto_close_message_snackbar">Auto-close enabled</string>
<!-- Inactive tabs survey -->
<!-- Header text for the inactive tabs survey asking for feedback to improve the inactive tabs feature. -->
<string moz:removedIn="95" name="inactive_tabs_survey_header" tools:ignore="UnusedResources">Please help us to improve</string>
<!-- Header text for the inactive tabs survey asking for feedback to improve the inactive tabs feature. -->
<string name="inactive_tabs_survey_header_1">Help improve Firefox</string>
<!-- Content text for the inactive tabs survey asking the primary survey feedback question. -->
<string name="inactive_tabs_survey_content">Why did you disable inactive tabs?</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_do_not_understand">I dont understand how it works</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_do_it_myself">I like to clear out old tabs myself</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string moz:removedIn="95" name="inactive_tabs_survey_time_too_long_option" tools:ignore="UnusedResources">Time to inactive is too long</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_time_too_long_option_1">The two-week time period is too long</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string moz:removedIn="95" name="inactive_tabs_survey_time_too_short_option" tools:ignore="UnusedResources">Time to inactive is too short</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_time_too_short_option_1">The two-week time period is too short</string>
<!-- Confirmation button text to submit the feedback for the inactive tabs survey. -->
<string name="inactive_tabs_survey_send_button">Send</string>
<!-- Content description for inactive tabs survey close button -->

@ -1243,6 +1243,10 @@
<string name="qr_scanner_dialog_positive">ALLOW</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">DENY</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Web address not valid.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">OK</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Are you sure you want to delete %1$s?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -1269,6 +1269,10 @@
<string name="qr_scanner_dialog_positive">ALLOW</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">DENY</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Dirección web inválida.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">Aceptar</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">¿Estás seguro de que querés eliminar %1$s?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -1245,6 +1245,10 @@
<string name="qr_scanner_dialog_positive">PERMITIR</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">DENEGAR</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Dirección web no válida.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">Aceptar</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">¿Estás seguro de que deseas eliminar %1$s?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -1277,6 +1277,10 @@
<string name="qr_scanner_dialog_positive">PERMITIR</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">DENEGAR</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">La dirección web no es válida.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">Aceptar</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">¿Seguro que quieres eliminar %1$s?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -141,6 +141,8 @@
<!-- Button text for showing all the tabs in the tabs tray -->
<string name="recent_tabs_show_all">Mostrar todo</string>
<!-- Content description for the button which navigates the user to show all recent tabs in the tabs tray. -->
<string name="recent_tabs_show_all_content_description">Mostrar el botón de todas las pestañas recientes</string>
<!-- Title for showing a group item in the 'Jump back in' section of the new tab
The first parameter is the search term that the user used. (for example: your search for "cat")-->
<string name="recent_tabs_search_term">Tu búsqueda de \&quot;%1$s\&quot;</string>
@ -164,6 +166,9 @@
in the Recently visited section -->
<string name="recently_visited_menu_item_remove">Eliminar</string>
<!-- Content description for the button which navigates the user to show all of their history. -->
<string name="past_explorations_show_all_content_description">Mostrar el botón de todas las exploraciones pasadas</string>
<!-- Browser Fragment -->
<!-- Content description (not visible, for screen readers etc.): Navigate to open tabs -->
<string name="browser_tabs_button">Abrir pestañas</string>
@ -295,6 +300,10 @@
<string name="onboarding_home_screen_title_2">Novedades en Firefox</string>
<!-- Onboarding home screen dialog description text. -->
<string name="onboarding_home_screen_description_2">Ahora es más fácil continuar donde lo dejaste.</string>
<!-- Onboarding home screen dialog title text for the home section. Firefox is intentionally hardcoded. -->
<string name="onboarding_home_screen_section_home_title_2">Página de inicio personalizada de Firefox</string>
<!-- Onboarding home screen dialog description text for the home section. -->
<string name="onboarding_home_screen_section_home_description_2">Salta a tus pestañas abiertas, marcadores e historial de navegación.</string>
<!-- Onboarding home screen dialog description text for the tab tray section. -->
<string name="onboarding_home_screen_section_cleaner_tab_tray_title_2">Pestañas limpias y organizadas</string>
<!-- Onboarding home screen dialog description text for the tab tray section. -->
@ -305,6 +314,9 @@
<!-- Onboarding home screen dialog description text for the history section. -->
<string name="onboarding_home_screen_section_useful_history_description_2">Revisa tus últimas búsquedas desde tú página de inicio y pestañas.</string>
<!-- Onboarding home screen popup dialog, shown on top of the Jump back in section. Firefox is intentionally hardcoded. -->
<string name="onboarding_home_screen_jump_back_contextual_hint" tools:ignore="UnusedResources">Tú página de inicio personalizada de Firefox ahora hace que sea más fácil continuar donde lo dejaste. Encuentra tus pestañas, marcadores y resultados de búsqueda recientes.</string>
<!-- Search Widget -->
<!-- Content description for searching with a widget. Firefox is intentionally hardcoded.-->
<string name="search_widget_content_description">Abrir una nueva pestaña de Firefox</string>
@ -460,6 +472,8 @@
<!-- Title for the customize home screen section with recently saved bookmarks. -->
<string moz:removedIn="94" name="customize_toggle_recently_saved_bookmarks" tools:ignore="UnusedResources">Guardados recientemente</string>
<!-- Title for the customize home screen section with recently saved bookmarks. -->
<string moz:removedIn="94" name="customize_toggle_recently_bookmarked" tools:ignore="UnusedResources">Agregados a marcadores recientemente</string>
<!-- Title for the customize home screen section with recently saved bookmarks. -->
<string name="customize_toggle_recent_bookmarks">Marcadores recientes</string>
<!-- Title for the customize home screen section with recently visited. Recently visited is
a section where users see a list of tabs that they have visited in the past few days -->
@ -684,6 +698,8 @@
<string name="tab_view_list">Lista</string>
<!-- Option for a grid tab view -->
<string name="tab_view_grid">Cuadrícula</string>
<!-- Option for search term tab groups -->
<string name="tab_view_search_term_tab_groups">Buscar en grupos</string>
<!-- Summary text for search term tab groups -->
<string name="tab_view_search_term_tab_groups_summary">Agrupar sitios relacionados</string>
<!-- Title of preference that allows a user to auto close tabs after a specified amount of time -->
@ -715,6 +731,8 @@
<string name="opening_screen_last_tab">Última pestaña</string>
<!-- Option for never starting on the home screen -->
<string moz:removedIn="94" name="start_on_home_never" tools:ignore="UnusedResources">Nunca</string>
<!-- Option for always opening the homepage when re-opening the app after four hours of inactivity -->
<string name="opening_screen_after_four_hours_of_inactivity">Página de inicio después de cuatro horas de inactividad</string>
<!-- Summary for tabs preference when auto closing tabs setting is set to manual close-->
<string name="close_tabs_manually_summary">Cerrar manualmente</string>
<!-- Summary for tabs preference when auto closing tabs setting is set to auto close tabs after one day-->
@ -1229,6 +1247,10 @@
<string name="qr_scanner_dialog_positive">PERMITIR</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">DENEGAR</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">La dirección web no es válida.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">Aceptar</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">¿Seguro que quieres eliminar %1$s?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->
@ -1564,6 +1586,9 @@
<!-- Description of redirect tracker cookies that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_description">Limpia las cookies creadas por redirecciones a sitios web de seguimiento conocidos.</string>
<!-- Description of the SmartBlock Enhanced Tracking Protection feature. The * symbol is intentionally hardcoded here,
as we use it on the UI to indicate which trackers have been partially unblocked. -->
<string name="preference_etp_smartblock_description">Algunos rastreadores marcados abajo han sido parcialmente desbloqueados en esta página porque interactuaste con ellos*.</string>
<!-- Text displayed that links to website about enhanced tracking protection SmartBlock -->
<string name="preference_etp_smartblock_learn_more">Saber más</string>
@ -1698,6 +1723,8 @@
<string name="saved_login_copy_username">Copiar nombre de usuario</string>
<!-- Content Description (for screenreaders etc) read for the button to clear a username while editing a login -->
<string name="saved_login_clear_username">Borrar nombre de usuario</string>
<!-- Content Description (for screenreaders etc) read for the button to clear the hostname field while creating a login -->
<string name="saved_login_clear_hostname">Borrar nombre de servidor</string>
<!-- Content Description (for screenreaders etc) read for the button to copy a site in logins -->
<string name="saved_login_copy_site">Copiar sitio</string>
<!-- Content Description (for screenreaders etc) read for the button to open a site in logins -->
@ -1864,6 +1891,8 @@
<!-- Label that indicates a site is using a secure connection -->
<string name="quick_settings_sheet_secure_connection_2">Conexión segura</string>
<!-- Label that indicates a site is using a insecure connection -->
<string name="quick_settings_sheet_insecure_connection_2">La conexión no es segura</string>
<!-- Label that indicates a site is using a secure connection -->
<string moz:removedIn="94" name="quick_settings_sheet_secure_connection" tools:ignore="UnusedResources">Conexión segura</string>
@ -1912,6 +1941,8 @@
<string name="add_login">Agregar nuevo inicio de sesión</string>
<!-- The error message in add/edit login view when password field is blank. -->
<string name="saved_login_password_required">Se requiere contraseña</string>
<!-- The error message in add login view when username field is blank. -->
<string name="saved_login_username_required">Se requiere nombre de usuario</string>
<!-- The error message in add login view when hostname field is blank. -->
<string name="saved_login_hostname_required" tools:ignore="UnusedResources">Se necesita nombre de servidor</string>
<!-- Voice search button content description -->
@ -1924,9 +1955,14 @@
<!-- This is the hint text that is shown inline on the hostname field of the create new login page. 'https://www.example.com' intentionally hardcoded here -->
<string name="add_login_hostname_hint_text">https://www.example.com</string>
<!-- This is an error message shown below the hostname field of the add login page when a hostname does not contain http or https. -->
<string moz:removedIn="94" name="add_login_hostname_invalid_text_1" tools:ignore="UnusedResources">La dirección web debe contener \“https://\“ o \“http://</string>
<!-- This is an error message shown below the hostname field of the add login page when a hostname does not contain http or https. -->
<string name="add_login_hostname_invalid_text_3">La dirección web debe contener &quot;https://&quot; o &quot;http://&quot;</string>
<!-- This is an error message shown below the hostname field of the add login page when a hostname is invalid. -->
<string name="add_login_hostname_invalid_text_2">Se requiere nombre de servidor válido</string>
<!-- Synced Tabs -->
<!-- Text displayed to ask user to connect another device as no devices found with account -->
<string name="synced_tabs_connect_another_device">Conectar otro dispositivo.</string>
@ -1952,6 +1988,10 @@
<!-- Confirmation dialog button text when top sites limit is reached. -->
<string name="top_sites_max_limit_confirmation_button">Vale, entendido</string>
<!-- Label for the preference to show the most visited top sites on the homepage -->
<string name="top_sites_toggle_top_recent_sites_3">Sitios favoritos más visitados</string>
<!-- Label for the show most visited top sites preference -->
<string moz:removedIn="94" name="top_sites_toggle_top_frecent_sites_2" tools:ignore="UnusedResources">Mostrar los sitios favoritos más visitados</string>
<!-- Label for the show most visited sites preference -->
<string moz:removedIn="93" name="top_sites_toggle_top_frecent_sites" tools:ignore="UnusedResources">Mostrar los sitios más visitados</string>
@ -1996,6 +2036,18 @@
<!-- Content text for the inactive tabs survey asking the primary survey feedback question. -->
<string name="inactive_tabs_survey_content">¿Por qué desactivaste las pestañas inactivas?</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_do_not_understand">No entiendo como funciona</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_do_it_myself">Me gusta limpiar las pestañas viejas yo mismo</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string moz:removedIn="95" name="inactive_tabs_survey_time_too_long_option" tools:ignore="UnusedResources">El tiempo para pasarlas a inactivas es demasiado largo</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_time_too_long_option_1">El periodo de dos semanas es muy largo</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string moz:removedIn="95" name="inactive_tabs_survey_time_too_short_option" tools:ignore="UnusedResources">El tiempo para pasarlas a inactivas es demasiado corto</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_time_too_short_option_1">El período de dos semanas es demasiado corto</string>
<!-- Confirmation button text to submit the feedback for the inactive tabs survey. -->
<string name="inactive_tabs_survey_send_button">Enviar</string>
@ -2017,6 +2069,11 @@
<!-- Content description for privacy content close button -->
<string name="privacy_content_close_button_content_description">Cerrar</string>
<!-- Pocket recommended stories -->
<!-- Header text for a section on the home screen. -->
<string moz:removedIn="94" name="pocket_stories_header" tools:ignore="UnusedResources">Historias que invitan a la reflexión</string>
<!-- Header text for a section on the home screen. -->
<string name="pocket_stories_header_1">Historias que invitan a la reflexión</string>
<!-- Header text for a section on the home screen. -->
<string name="pocket_stories_categories_header">Historias por tema</string>
<!-- Text of a button allowing users to access an external url for more Pocket recommendations. -->

@ -114,6 +114,14 @@
<!-- Text for the negative action button to dismiss the Close Tabs Banner. -->
<string name="tab_tray_close_tabs_banner_negative_button_text">Descartar</string>
<!-- Text for the banner message to tell users about our inactive tabs feature. -->
<string name="tab_tray_inactive_onboarding_message">Las pestañas que no ha visto durante dos semanas se mueven aquí.</string>
<!-- Text for the action link to go to Settings for inactive tabs. -->
<string name="tab_tray_inactive_onboarding_button_text">Desactivar en ajustes</string>
<!-- Text for title for the auto-close dialog of the inactive tabs. -->
<string name="tab_tray_inactive_auto_close_title">¿Cerrar automáticamente después de un mes?</string>
<!-- Text for the body for the auto-close dialog of the inactive tabs. -->
<string name="tab_tray_inactive_auto_close_body">Firefox puede cerrar pestañas que no has visto durante el último mes.</string>
<!-- Content description for close button in the auto-close dialog of the inactive tabs. -->
<string name="tab_tray_inactive_auto_close_button_content_description">Cerrar</string>
<!-- Text for turn on auto close tabs button in the auto-close dialog of the inactive tabs. -->
@ -163,6 +171,9 @@
in the Recently visited section -->
<string name="recently_visited_menu_item_remove">Eliminar</string>
<!-- Content description for the button which navigates the user to show all of their history. -->
<string name="past_explorations_show_all_content_description">Mostrar el botón de todas las exploraciones anteriores</string>
<!-- Browser Fragment -->
<!-- Content description (not visible, for screen readers etc.): Navigate to open tabs -->
<string name="browser_tabs_button">Pestañas abiertas</string>
@ -240,6 +251,8 @@
<!-- Browser menu button that opens the Customize menu -->
<string name="browser_menu_customize_home">Personalizar inicio</string>
<!-- Button shown on the home page that opens the Customize home settings -->
<string name="browser_menu_customize_home_1">Personalizar la página de inicio</string>
<!-- Browser Toolbar -->
<!-- Content description for the Home screen button on the browser toolbar -->
<string name="browser_toolbar_home">Pantalla de inicio</string>
@ -287,11 +300,16 @@
<!-- Search engine suggestion description text -->
<string name="search_engine_suggestions_description">Busca directamente desde la barra de direcciones</string>
<!-- Home onboarding -->
<!-- Onboarding home screen dialog title text. Firefox is intentionally hardcoded. -->
<string name="onboarding_home_screen_title_2">Novedades en Firefox</string>
<!-- Onboarding home screen dialog description text. -->
<string name="onboarding_home_screen_description_2">Ahora es más fácil retomar desde donde quedaste.</string>
<!-- Onboarding home screen dialog title text for the home section. Firefox is intentionally hardcoded. -->
<string name="onboarding_home_screen_section_home_title_2">Página de inicio de Firefox personalizada</string>
<!-- Onboarding home screen dialog description text for the home section. -->
<string name="onboarding_home_screen_section_home_description_2">Accede a tus pestañas abiertas, marcadores e historial de navegación.</string>
<!-- Onboarding home screen dialog description text for the tab tray section. -->
<string name="onboarding_home_screen_section_cleaner_tab_tray_title_2">Pestañas limpias y organizadas</string>
<!-- Onboarding home screen dialog description text for the tab tray section. -->
@ -299,6 +317,12 @@
<!-- Onboarding home screen dialog description text for the history section. -->
<string name="onboarding_home_screen_section_useful_history_title_2">Búsquedas recientes</string>
<!-- Onboarding home screen dialog description text for the history section. -->
<string name="onboarding_home_screen_section_useful_history_description_2">Revisa tus últimas búsquedas desde tu página de inicio y pestañas.</string>
<!-- Onboarding home screen popup dialog, shown on top of the Jump back in section. Firefox is intentionally hardcoded. -->
<string name="onboarding_home_screen_jump_back_contextual_hint" tools:ignore="UnusedResources">Tu página de inicio personalizada de Firefox ahora hace que sea mucho más fácil continuar desde donde quedaste. Encuentra tus pestañas, marcadores y resultados de búsqueda recientes.</string>
<!-- Search Widget -->
<!-- Content description for searching with a widget. Firefox is intentionally hardcoded.-->
<string name="search_widget_content_description">Abrir una nueva pestaña de Firefox</string>
@ -715,6 +739,8 @@
<string name="opening_screen_last_tab">Última pestaña</string>
<!-- Option for never starting on the home screen -->
<string moz:removedIn="94" name="start_on_home_never" tools:ignore="UnusedResources">Nunca</string>
<!-- Option for always opening the homepage when re-opening the app after four hours of inactivity -->
<string name="opening_screen_after_four_hours_of_inactivity">Página de inicio después de cuatro horas de inactividad</string>
<!-- Summary for tabs preference when auto closing tabs setting is set to manual close-->
<string name="close_tabs_manually_summary">Cerrar manualmente</string>
<!-- Summary for tabs preference when auto closing tabs setting is set to auto close tabs after one day-->
@ -724,6 +750,12 @@
<!-- Summary for tabs preference when auto closing tabs setting is set to auto close tabs after one month-->
<string name="close_tabs_after_one_month_summary">Cerrar después de un mes</string>
<!-- Inactive tabs -->
<!-- Category header of a preference that allows a user to enable or disable the inactive tabs feature -->
<string name="preferences_inactive_tabs">Mover pestañas antiguas a inactivas</string>
<!-- Title of inactive tabs preference -->
<string name="preferences_inactive_tabs_title">Las pestañas que no ha visto durante dos semanas se mueven a la sección de inactivas.</string>
<!-- Studies -->
<!-- Title of the remove studies button -->
<string name="studies_remove">Eliminar</string>
@ -1247,6 +1279,10 @@
<string name="qr_scanner_dialog_positive">PERMITIR</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">DENEGAR</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Dirección web no válida.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">OK</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">¿Seguro que quieres eliminar %1$s?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->
@ -2015,9 +2051,19 @@
<!-- The header text of the auto-close message when the user is asked if they want to turn on the auto-closing of inactive tabs. -->
<string name="inactive_tabs_auto_close_message_header" tools:ignore="UnusedResources">¿Cerrar automáticamente después de un mes?</string>
<!-- A description below the header to notify the user what the inactive tabs auto-close feature is. -->
<string name="inactive_tabs_auto_close_message_description" tools:ignore="UnusedResources">Firefox puede cerrar pestañas que no has visto durante el último mes.</string>
<!-- A call to action below the description to allow the user to turn on the auto closing of inactive tabs. -->
<string name="inactive_tabs_auto_close_message_action" tools:ignore="UnusedResources">ACTIVAR CIERRE AUTOMÁTICO</string>
<!-- Text for the snackbar to confirm auto-close is enabled for inactive tabs -->
<string name="inactive_tabs_auto_close_message_snackbar">Cierre automático activado</string>
<!-- Inactive tabs survey -->
<!-- Header text for the inactive tabs survey asking for feedback to improve the inactive tabs feature. -->
<string moz:removedIn="95" name="inactive_tabs_survey_header" tools:ignore="UnusedResources">Ayúdanos a mejorar</string>
<!-- Header text for the inactive tabs survey asking for feedback to improve the inactive tabs feature. -->
<string name="inactive_tabs_survey_header_1">Ayuda a mejorar Firefox</string>
<!-- Content text for the inactive tabs survey asking the primary survey feedback question. -->
<string name="inactive_tabs_survey_content">¿Por qué desactivaste pestañas inactivas?</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
@ -2044,6 +2090,11 @@
<!-- Content description for privacy content close button -->
<string name="privacy_content_close_button_content_description">Cerrar</string>
<!-- Pocket recommended stories -->
<!-- Header text for a section on the home screen. -->
<string moz:removedIn="94" name="pocket_stories_header" tools:ignore="UnusedResources">Historias que te hacen reflexionar</string>
<!-- Header text for a section on the home screen. -->
<string name="pocket_stories_header_1">Historias que te hacen reflexionar</string>
<!-- Header text for a section on the home screen. -->
<string name="pocket_stories_categories_header">Historias por tema</string>
<!-- Text of a button allowing users to access an external url for more Pocket recommendations. -->

@ -122,7 +122,11 @@
<!-- Content description for close button in the auto-close dialog of the inactive tabs. -->
<string name="tab_tray_inactive_auto_close_button_content_description">Itxi</string>
<!-- Text for turn on auto close tabs button in the auto-close dialog of the inactive tabs. -->
<string name="tab_tray_inactive_turn_on_auto_close_button">Aktibatu automatikoki ixtea</string>
<string moz:removedIn="95" name="tab_tray_inactive_turn_on_auto_close_button" tools:ignore="UnusedResources">Aktibatu automatikoki ixtea</string>
<!-- Text for turn on auto close tabs button in the auto-close dialog of the inactive tabs. -->
<string name="tab_tray_inactive_turn_on_auto_close_button_2">Aktibatu automatikoki ixtea</string>
<!-- Home screen icons - Long press shortcuts -->
<!-- Shortcut action to open new tab -->
@ -1252,6 +1256,10 @@
<string name="qr_scanner_dialog_positive">BAIMENDU</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">UKATU</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Web helbidea ez da baliozkoa.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">Ados</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Ziur zaude %1$s bilduma ezabatu nahi duzula?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->
@ -2011,11 +2019,11 @@
<!-- Content description for closing all inactive tabs -->
<string name="inactive_tabs_delete_all">Itxi fitxa inaktibo guztiak</string>
<!-- A description below the section of "inactive" tabs to notify the user when those tabs will be closed, if appropriate. See strings inactive_tabs_30_days and inactive_tabs_7_days for placeholders options. -->
<string moz:removedIn="93" name="inactive_tabs_description" tools:ignore="UnusedResources">Fitxak %s egongo dira erabilgarri. Denbora horren ondoren, automatikoki itxiko dira.</string>
<string moz:removedIn="95" name="inactive_tabs_description" tools:ignore="UnusedResources">Fitxak %s egongo dira erabilgarri. Denbora horren ondoren, automatikoki itxiko dira.</string>
<!-- The amount of time until a tab in the "inactive" section of the tabs tray will be closed. See string inactive_tabs_description as well -->
<string moz:removedIn="93" name="inactive_tabs_30_days" tools:ignore="UnusedResources">30 egunez</string>
<string moz:removedIn="95" name="inactive_tabs_30_days" tools:ignore="UnusedResources">30 egunez</string>
<!-- The amount of time until a tab in the "inactive" section of the tabs tray will be closed. See string inactive_tabs_description as well -->
<string moz:removedIn="93" name="inactive_tabs_7_days" tools:ignore="UnusedResources">astebetez</string>
<string moz:removedIn="95" name="inactive_tabs_7_days" tools:ignore="UnusedResources">astebetez</string>
<!-- Inactive tabs auto-close message in the tabs tray -->
<!-- The header text of the auto-close message when the user is asked if they want to turn on the auto-closing of inactive tabs. -->
@ -2025,21 +2033,33 @@
<!-- A call to action below the description to allow the user to turn on the auto closing of inactive tabs. -->
<string name="inactive_tabs_auto_close_message_action" tools:ignore="UnusedResources">AKTIBATU AUTOMATIKOKI IXTEA</string>
<!-- Text for the snackbar to confirm auto-close is enabled for inactive tabs -->
<string name="inactive_tabs_auto_close_message_snackbar">Automatikoki ixtea gaituta</string>
<!-- Inactive tabs survey -->
<!-- Header text for the inactive tabs survey asking for feedback to improve the inactive tabs feature. -->
<string name="inactive_tabs_survey_header" tools:ignore="UnusedResources">Lagun iezaguzu hobetzen</string>
<string moz:removedIn="95" name="inactive_tabs_survey_header" tools:ignore="UnusedResources">Lagun iezaguzu hobetzen</string>
<!-- Header text for the inactive tabs survey asking for feedback to improve the inactive tabs feature. -->
<string name="inactive_tabs_survey_header_1">Lagundu Firefox hobetzen</string>
<!-- Content text for the inactive tabs survey asking the primary survey feedback question. -->
<string name="inactive_tabs_survey_content" tools:ignore="UnusedResources">Zergatik desgaitu dituzu fitxa inaktiboak?</string>
<string name="inactive_tabs_survey_content">Zergatik desgaitu dituzu fitxa inaktiboak?</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_do_not_understand">Ez dut ulertzen nola dabilen</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_do_it_myself">Fitxa zaharrak neronek garbitzea gustatzen zait</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string moz:removedIn="95" name="inactive_tabs_survey_time_too_long_option" tools:ignore="UnusedResources">Desaktibatzeko denbora luzeegia da</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_not_interested_option" tools:ignore="UnusedResources">Ez daukat eginbide honen interesik</string>
<string name="inactive_tabs_survey_time_too_long_option_1">Bi asteko denbora-tartea luzeegia da</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_time_too_long_option" tools:ignore="UnusedResources">Desaktibatzeko denbora luzeegia da</string>
<string moz:removedIn="95" name="inactive_tabs_survey_time_too_short_option" tools:ignore="UnusedResources">Desaktibatzeko denbora laburregia da</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_time_too_short_option" tools:ignore="UnusedResources">Desaktibatzeko denbora laburregia da</string>
<string name="inactive_tabs_survey_time_too_short_option_1">Bi asteko denbora-tartea laburregia da</string>
<!-- Confirmation button text to submit the feedback for the inactive tabs survey. -->
<string name="inactive_tabs_survey_send_button" tools:ignore="UnusedResources">Bidali</string>
<string name="inactive_tabs_survey_send_button">Bidali</string>
<!-- Content description for inactive tabs survey close button -->
<string name="inactive_tabs_survey_close_button_content_description" tools:ignore="UnusedResources">Itxi</string>
<string name="inactive_tabs_survey_close_button_content_description">Itxi</string>
<!-- Default browser experiment -->
<string name="default_browser_experiment_card_text">Ireki webgune, posta elektroniko eta mezuetako loturak Firefoxen automatikoki.</string>

@ -1259,6 +1259,10 @@
<string name="qr_scanner_dialog_positive">SALLI</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">ESTÄ</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Verkko-osoite ei ole kelvollinen.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">OK</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Poistetaanko %1$s?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -1245,6 +1245,10 @@
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">WEGERJE</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Webadres net jildich.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">OK</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Binne jo wis dat jo %1$s fuortsmite wolle?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -1253,6 +1253,10 @@
<string name="qr_scanner_dialog_positive">DOWOLIĆ</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">WOTPOKAZAĆ</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Webadresa płaćiwa njeje.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">W porjadku</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Chceće woprawdźe %1$s zhašeć?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -1256,6 +1256,10 @@
<string name="qr_scanner_dialog_positive">ENGEDÉLYEZÉS</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">ELUTASÍTÁS</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">A webcím érvénytelen.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">OK</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Biztos, hogy törli ezt: %1$s?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -24,7 +24,7 @@
<!-- Message announced to the user when tab tray is selected with 1 tab -->
<string name="open_tab_tray_single">1 բաց ներդիր: Հպեք՝ փոխարկելու համար:</string>
<!-- Message announced to the user when tab tray is selected with 0 or 2+ tabs -->
<string name="open_tab_tray_plural">%1$s բաց ներդիրներ: Հպեք՝ փոխարկելու համար:</string>
<string moz:removedIn="95" name="open_tab_tray_plural" tools:ignore="UnusedResources">%1$s բաց ներդիրներ: Հպեք՝ փոխարկելու համար:</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</string>
@ -108,9 +108,22 @@
<string name="tab_tray_close_tabs_banner_negative_button_text">Բաց թողնել</string>
<!-- Text for the banner message to tell users about our inactive tabs feature. -->
<string name="tab_tray_inactive_onboarding_message" tools:ignore="UnusedResources">Երկու շաբաթ չնայած էջանիշերը տեղափոխվում են այստեղ:</string>
<string name="tab_tray_inactive_onboarding_message">Երկու շաբաթ չնայած էջանիշերը տեղափոխվում են այստեղ:</string>
<!-- Text for the action link to go to Settings for inactive tabs. -->
<string name="tab_tray_inactive_onboarding_button_text" tools:ignore="UnusedResources">Անջատել կարգավորումներում</string>
<string name="tab_tray_inactive_onboarding_button_text">Անջատել կարգավորումներում</string>
<!-- Text for title for the auto-close dialog of the inactive tabs. -->
<string name="tab_tray_inactive_auto_close_title">Ինքնափակե՞լ մեկ ամսից:</string>
<!-- Text for the body for the auto-close dialog of the inactive tabs. -->
<string name="tab_tray_inactive_auto_close_body">Firefox-ը կարող է փակել ներդիրները, որոնք չեք դիտել վերջին ամսում:</string>
<!-- Content description for close button in the auto-close dialog of the inactive tabs. -->
<string name="tab_tray_inactive_auto_close_button_content_description">Փակել</string>
<!-- Text for turn on auto close tabs button in the auto-close dialog of the inactive tabs. -->
<string moz:removedIn="95" name="tab_tray_inactive_turn_on_auto_close_button" tools:ignore="UnusedResources">Միացնել ինքնափակումը</string>
<!-- Text for turn on auto close tabs button in the auto-close dialog of the inactive tabs. -->
<string name="tab_tray_inactive_turn_on_auto_close_button_2">Միացնել ինքնափակումը</string>
<!-- Home screen icons - Long press shortcuts -->
<!-- Shortcut action to open new tab -->
@ -120,7 +133,7 @@
<string name="home_screen_shortcut_open_new_private_tab_2">Նոր գաղտնի ներդիր</string>
<!-- Heading for the Top Sites block -->
<string name="home_screen_top_sites_heading">Լավագույն կայքեր</string>
<string moz:removedIn="95" name="home_screen_top_sites_heading" tools:ignore="UnusedResources">Լավագույն կայքեր</string>
<!-- Recent Tabs -->
<!-- Header text for jumping back into the recent tab in the home screen -->
@ -167,7 +180,7 @@
<!-- Content description (not visible, for screen readers etc.): Stop loading current website -->
<string name="browser_menu_stop">Կանգնեցնել</string>
<!-- Content description (not visible, for screen readers etc.): Bookmark the current page -->
<string name="browser_menu_bookmark">Էջանիշ</string>
<string moz:removedIn="95" name="browser_menu_bookmark" tools:ignore="UnusedResources">Էջանիշ</string>
<!-- Content description (not visible, for screen readers etc.): Un-bookmark the current page -->
<string name="browser_menu_edit_bookmark">Խմբագրել Էջանիշը</string>
<!-- Browser menu button that opens the addon manager -->
@ -378,7 +391,9 @@
<!-- Preference for changing default theme to dark or light mode -->
<string name="preferences_theme">Ոճ</string>
<!-- Preference for customizing the home screen -->
<string name="preferences_home">Տուն</string>
<string moz:removedIn="94" name="preferences_home" tools:ignore="UnusedResources">Տուն</string>
<!-- Preference for customizing the home screen -->
<string name="preferences_home_2">Տնային էջ</string>
<!-- Preference for gestures based actions -->
<string name="preferences_gestures">Ժեստեր</string>
<!-- Preference for settings related to visual options -->
@ -572,7 +587,7 @@
<!-- Header of the Turn on Sync preference view -->
<string name="preferences_sync">Միացնել համաժամեցումը</string>
<!-- Preference for pairing -->
<string name="preferences_sync_pair">Firefox-ում աշխատասեղանի զուգավորման կոդ սկանավորեք</string>
<string moz:removedIn="95" name="preferences_sync_pair" tools:ignore="UnusedResources">Firefox-ում աշխատասեղանի զուգավորման կոդ սկանավորեք</string>
<!-- Preference for account login -->
<string name="preferences_sync_sign_in">Մուտք գործել</string>
<!-- Preference for reconnecting to FxA sync -->
@ -696,15 +711,23 @@
<!-- Title of preference that allows a user to specify the auto-close settings for open tabs -->
<string name="preference_auto_close_tabs" tools:ignore="UnusedResources">Ինքնափակել բաց ներդիրները</string>
<!-- Start on Home -->
<!-- Opening screen -->
<!-- Title of a preference that allows a user to indicate after a specified amount of time when the app should start on the home screen -->
<string name="preferences_start_on_home">Սկսել տնից</string>
<string moz:removedIn="94" name="preferences_start_on_home" tools:ignore="UnusedResources">Սկսել տնից</string>
<!-- Title of a preference that allows a user to choose what screen to show after opening the app -->
<string name="preferences_opening_screen">Ողջյունի պատուհան</string>
<!-- Option for starting on the home screen after after four hours or inactivity -->
<string name="start_on_home_after_four_hours">Չորս ժամ անց</string>
<string moz:removedIn="94" name="start_on_home_after_four_hours" tools:ignore="UnusedResources">Չորս ժամ անց</string>
<!-- Option for always opening the homepage when re-opening the app -->
<string name="opening_screen_homepage">Տնային էջ</string>
<!-- Option for always starting on the home screen -->
<string name="start_on_home_always">Միշտ</string>
<string moz:removedIn="94" name="start_on_home_always" tools:ignore="UnusedResources">Միշտ</string>
<!-- Option for always opening the user's last-open tab when re-opening the app -->
<string name="opening_screen_last_tab">Վերջին ներդիրը</string>
<!-- Option for never starting on the home screen -->
<string name="start_on_home_never">Երբեք</string>
<string moz:removedIn="94" name="start_on_home_never" tools:ignore="UnusedResources">Երբեք</string>
<!-- Option for always opening the homepage when re-opening the app after four hours of inactivity -->
<string name="opening_screen_after_four_hours_of_inactivity">Տնային էջը չորս ժամ անգործությունից հետո</string>
<!-- Summary for tabs preference when auto closing tabs setting is set to manual close-->
<string name="close_tabs_manually_summary">Ձեռքով փակեք</string>
<!-- Summary for tabs preference when auto closing tabs setting is set to auto close tabs after one day-->
@ -761,13 +784,13 @@
<!-- Text shown in the menu for saving tabs to a collection -->
<string name="tab_tray_menu_item_save">Պահպանել հավաքածուում</string>
<!-- Text shown in the menu for the collection selector -->
<string name="tab_tray_menu_select">Ընտրել</string>
<string moz:removedIn="95" name="tab_tray_menu_select" tools:ignore="UnusedResources">Ընտրել</string>
<!-- Text shown in the menu for sharing all tabs -->
<string name="tab_tray_menu_item_share">Համօգտագործել ներդիրները</string>
<!-- Text shown in the menu to view recently closed tabs -->
<string name="tab_tray_menu_recently_closed">Վերջերս փակված ներդիրներ</string>
<!-- Text shown in the tabs tray inactive tabs section -->
<string name="tab_tray_inactive_recently_closed">Վերջին փակվածը</string>
<string name="tab_tray_inactive_recently_closed" tools:ignore="UnusedResources">Վերջին փակվածը</string>
<!-- Text shown in the menu to view account settings -->
<string name="tab_tray_menu_account_settings">Հաշվի կարգավորումներ</string>
<!-- Text shown in the menu to view tab settings -->
@ -888,10 +911,6 @@
<string name="history_empty_message">Այստեղ պատմություն չկա</string>
<!-- Downloads -->
<!-- Text for the button to clear all downloads -->
<string name="download_delete_all">Ջնջել ներբեռնումները</string>
<!-- Text for the dialog to confirm clearing all downloads -->
<string name="download_delete_all_dialog">Համոզվա՞ծ եք, որ ցանկանում եք մաքրել ներբեռնումերը:</string>
<!-- Text for the snackbar to confirm that multiple downloads items have been removed -->
<string name="download_delete_multiple_items_snackbar_1">Ներբեռնումները հեռացված են</string>
<!-- Text for the snackbar to confirm that a single download item has been removed. The first parameter is the name of the download item. -->
@ -932,7 +951,7 @@
<!-- Content description for bookmarks library menu -->
<string name="bookmark_menu_content_description">Էջանիշերի ցանկ</string>
<!-- Screen title for editing bookmarks -->
<string name="bookmark_edit">Խմբագրել Էջանիշը</string>
<string moz:removedIn="95" name="bookmark_edit" tools:ignore="UnusedResources">Խմբագրել Էջանիշը</string>
<!-- Screen title for selecting a bookmarks folder -->
<string name="bookmark_select_folder">Ընտրել պանակ</string>
<!-- Confirmation message for a dialog confirming if the user wants to delete the selected folder -->
@ -1963,8 +1982,10 @@
<!-- Confirmation dialog button text when top sites limit is reached. -->
<string name="top_sites_max_limit_confirmation_button">Հասկանալի է</string>
<!-- Label for the preference to show the most visited top sites on the homepage -->
<string name="top_sites_toggle_top_recent_sites_3">Ամենաշատ այցելված լավագույն կայքերը</string>
<!-- Label for the show most visited top sites preference -->
<string name="top_sites_toggle_top_frecent_sites_2">Ցուցադրել ամենաշատ այցելված կայքերը</string>
<string moz:removedIn="94" name="top_sites_toggle_top_frecent_sites_2" tools:ignore="UnusedResources">Ցուցադրել ամենաշատ այցելված կայքերը</string>
<!-- Label for the show most visited sites preference -->
<string moz:removedIn="93" name="top_sites_toggle_top_frecent_sites" tools:ignore="UnusedResources">Ցուցադրել ամենաշատ այցելվող կայքերը</string>
@ -1983,11 +2004,11 @@
<!-- Content description for closing all inactive tabs -->
<string name="inactive_tabs_delete_all">Փակել բոլոր անգործուն ներդիրները</string>
<!-- A description below the section of "inactive" tabs to notify the user when those tabs will be closed, if appropriate. See strings inactive_tabs_30_days and inactive_tabs_7_days for placeholders options. -->
<string name="inactive_tabs_description">Ներդիրներն այստեղ հասանելի են %s: Այդ ժամանակից հետո ներդիրներն ինքնաբար կփակվեն:</string>
<string moz:removedIn="95" name="inactive_tabs_description" tools:ignore="UnusedResources">Ներդիրներն այստեղ հասանելի են %s: Այդ ժամանակից հետո ներդիրներն ինքնաբար կփակվեն:</string>
<!-- The amount of time until a tab in the "inactive" section of the tabs tray will be closed. See string inactive_tabs_description as well -->
<string name="inactive_tabs_30_days">30 օր</string>
<string moz:removedIn="95" name="inactive_tabs_30_days" tools:ignore="UnusedResources">30 օր</string>
<!-- The amount of time until a tab in the "inactive" section of the tabs tray will be closed. See string inactive_tabs_description as well -->
<string name="inactive_tabs_7_days">1 շաբաթ</string>
<string moz:removedIn="95" name="inactive_tabs_7_days" tools:ignore="UnusedResources">1 շաբաթ</string>
<!-- Inactive tabs auto-close message in the tabs tray -->
<!-- The header text of the auto-close message when the user is asked if they want to turn on the auto-closing of inactive tabs. -->
@ -1997,21 +2018,33 @@
<!-- A call to action below the description to allow the user to turn on the auto closing of inactive tabs. -->
<string name="inactive_tabs_auto_close_message_action" tools:ignore="UnusedResources">ՄԻԱՑՆԵԼ ԻՆՔՆԱՓԱԿՈՒՄԸ</string>
<!-- Text for the snackbar to confirm auto-close is enabled for inactive tabs -->
<string name="inactive_tabs_auto_close_message_snackbar">Ինքնափակումը միացված է</string>
<!-- Inactive tabs survey -->
<!-- Header text for the inactive tabs survey asking for feedback to improve the inactive tabs feature. -->
<string name="inactive_tabs_survey_header" tools:ignore="UnusedResources">Օգնեք մեզ բարելավել</string>
<string moz:removedIn="95" name="inactive_tabs_survey_header" tools:ignore="UnusedResources">Օգնեք մեզ բարելավել</string>
<!-- Header text for the inactive tabs survey asking for feedback to improve the inactive tabs feature. -->
<string name="inactive_tabs_survey_header_1">Օգնեք բարելավել Firefox-ը</string>
<!-- Content text for the inactive tabs survey asking the primary survey feedback question. -->
<string name="inactive_tabs_survey_content" tools:ignore="UnusedResources">Ինչու՞ եք անջատել անգործուն ներդիրները:</string>
<string name="inactive_tabs_survey_content">Ինչու՞ եք անջատել անգործուն ներդիրները:</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_do_not_understand">Ես չեմ հասկանում, թե ինչպես է դա աշխատում</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_do_it_myself">Ես ինքս սիրում եմ մաքրել հին ներդիրները</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string moz:removedIn="95" name="inactive_tabs_survey_time_too_long_option" tools:ignore="UnusedResources">Անգործունության ժամանակը երկար է</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_not_interested_option" tools:ignore="UnusedResources">Հետաքրքրված չեմ</string>
<string name="inactive_tabs_survey_time_too_long_option_1">Երկշաբաթյա ժամկետը չափազանց երկար է</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_time_too_long_option" tools:ignore="UnusedResources">Անգործունության ժամանակը երկար է</string>
<string moz:removedIn="95" name="inactive_tabs_survey_time_too_short_option" tools:ignore="UnusedResources">Անգործունության ժամանակը կարճ է</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_time_too_short_option" tools:ignore="UnusedResources">Անգործունության ժամանակը կարճ է</string>
<string name="inactive_tabs_survey_time_too_short_option_1">Երկշաբաթյա ժամկետը չափազանց կարճ է</string>
<!-- Confirmation button text to submit the feedback for the inactive tabs survey. -->
<string name="inactive_tabs_survey_send_button" tools:ignore="UnusedResources">Ուղարկել</string>
<string name="inactive_tabs_survey_send_button">Ուղարկել</string>
<!-- Content description for inactive tabs survey close button -->
<string name="inactive_tabs_survey_close_button_content_description" tools:ignore="UnusedResources">Փակել</string>
<string name="inactive_tabs_survey_close_button_content_description">Փակել</string>
<!-- Default browser experiment -->
<string name="default_browser_experiment_card_text">Կայեք հղումներ կայքերից, էլ. նամակներից և հաղորդագրություններից, որոնք ինքնաբար կերպով կբացվեն Firefox-ում:</string>

@ -1285,6 +1285,10 @@
<string name="qr_scanner_dialog_positive">PERMITTER</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">NEGAR</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Adresse web non valide.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">OK</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Desira tu vermente deler %1$s?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -1283,6 +1283,10 @@
<string name="qr_scanner_dialog_positive">CONSENTI</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">NEGA</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Indirizzo web non valido.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">OK</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Eliminare %1$s?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -192,7 +192,7 @@
<!-- Browser menu button that opens a user's library -->
<string name="browser_menu_library">ספרייה</string>
<!-- Browser menu toggle that requests a desktop site -->
<string name="browser_menu_desktop_site">אתר למחשבים שולחניים</string>
<string name="browser_menu_desktop_site">אתר למחשבים</string>
<!-- Browser menu toggle that adds a shortcut to the site on the device home screen. -->
<string name="browser_menu_add_to_homescreen">הוספה למסך הבית</string>
<!-- Browser menu toggle that installs a Progressive Web App shortcut to the site on the device home screen. -->
@ -1241,6 +1241,10 @@
<string name="qr_scanner_dialog_positive">לאפשר</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">לחסום</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">כתובת האתר לא חוקית.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">אישור</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">האם ברצונך למחוק את %1$s?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -448,7 +448,7 @@
<!-- Preference for enabling url autocomplete-->
<string name="preferences_enable_autocomplete_urls">自動補完 URL</string>
<!-- Preference for open links in third party apps -->
<string name="preferences_open_links_in_apps">アプリ内のリンクを開く</string>
<string name="preferences_open_links_in_apps">リンクをアプリで開く</string>
<!-- Preference for open download with an external download manager app -->
<string name="preferences_external_download_manager">外部のダウンロードマネージャー</string>
@ -1266,6 +1266,10 @@
<string name="qr_scanner_dialog_positive">許可</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">拒否</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">ウェブアドレスが正しくありません。</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">OK</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">本当に %1$s を削除してもよろしいですか?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->
@ -1498,7 +1502,7 @@
<!-- Link displayed in enhanced tracking protection panel to access tracking protection settings -->
<string name="etp_settings">追跡防止の設定</string>
<!-- Preference title for enhanced tracking protection settings -->
<string name="preference_enhanced_tracking_protection">強化されたトラッキング防止</string>
<string name="preference_enhanced_tracking_protection">強化トラッキング防止</string>
<!-- Title for the description of enhanced tracking protection -->
<string name="preference_enhanced_tracking_protection_explanation_title">閲覧中のプライバシーを保護します</string>
<!-- Description of enhanced tracking protection. The first parameter is the name of the application (For example: Fenix) -->
@ -1579,7 +1583,7 @@
<!-- Enhanced Tracking Protection message that protection is currently off for this site -->
<string name="etp_panel_off">このサイトでは保護が無効になっています</string>
<!-- Header for exceptions list for which sites enhanced tracking protection is always off -->
<string name="enhanced_tracking_protection_exceptions">これらのウェブサイトでは強化されたトラッキング防止が無効になります</string>
<string name="enhanced_tracking_protection_exceptions">これらのウェブサイトでは強化トラッキング防止が無効になります</string>
<!-- Content description (not visible, for screen readers etc.): Navigate
back from ETP details (Ex: Tracking content) -->
<string name="etp_back_button_content_description">前のページへ戻る</string>

@ -1263,6 +1263,10 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara
<string name="qr_scanner_dialog_positive">SIREG</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">GDEL</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Tansa n web d tarameɣtut.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">IH</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Tebɣiḍ ad tekseḍ %1$s?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -1238,6 +1238,10 @@
<string name="qr_scanner_dialog_positive">РҰҚСАТ ЕТУ</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">ТЫЙЫМ САЛУ</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Веб-адресі қате.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">ОК</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">%1$s өшіруді шынымен қалайсыз ба?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->
@ -2015,16 +2019,30 @@
<!-- A call to action below the description to allow the user to turn on the auto closing of inactive tabs. -->
<string name="inactive_tabs_auto_close_message_action" tools:ignore="UnusedResources">АВТОЖАБУДЫ ІСКЕ ҚОСУ</string>
<!-- Text for the snackbar to confirm auto-close is enabled for inactive tabs -->
<string name="inactive_tabs_auto_close_message_snackbar">Автожабу іске қосылған</string>
<!-- Inactive tabs survey -->
<!-- Header text for the inactive tabs survey asking for feedback to improve the inactive tabs feature. -->
<string moz:removedIn="95" name="inactive_tabs_survey_header" tools:ignore="UnusedResources">Бізге осыны жақсартуға көмектесіңіз</string>
<!-- Header text for the inactive tabs survey asking for feedback to improve the inactive tabs feature. -->
<string name="inactive_tabs_survey_header_1">Firefox-ты жақсартуға көмектесіңіз</string>
<!-- Content text for the inactive tabs survey asking the primary survey feedback question. -->
<string name="inactive_tabs_survey_content">Белсенді емес беттер мүмкіндігін неліктен сөндірдіңіз?</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_do_not_understand">Мен оның қалай жұмыс істейтінін түсінбеймін</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_do_it_myself">Мен ескі беттерді өзім тазалағанды қалаймын</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string moz:removedIn="95" name="inactive_tabs_survey_time_too_long_option" tools:ignore="UnusedResources">Белсенді емес күйін күту уақыты тым ұзақ</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_time_too_long_option_1">Екі апталық уақыт кезеңі тым ұзақ</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string moz:removedIn="95" name="inactive_tabs_survey_time_too_short_option" tools:ignore="UnusedResources">Белсенді емес күйін күту уақыты тым қысқа</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_time_too_short_option_1">Екі апталық уақыт кезеңі тым қысқа</string>
<!-- Confirmation button text to submit the feedback for the inactive tabs survey. -->
<string name="inactive_tabs_survey_send_button">Жіберу</string>
<!-- Content description for inactive tabs survey close button -->

@ -1289,6 +1289,10 @@
<string name="qr_scanner_dialog_positive">허용</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">거부</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">웹 주소가 잘못되었습니다.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">확인</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">%1$s 파일을 삭제하시겠습니까?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -1248,6 +1248,10 @@
<string name="qr_scanner_dialog_positive">Leisti</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">Drausti</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Neteisingas saityno adresas.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">Gerai</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Ar tikrai norite pašalinti „%1$s“?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -1257,6 +1257,10 @@
<string name="qr_scanner_dialog_positive">TILLAT</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">AVSLÅ</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Nettadressen er ikke gyldig.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">OK</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Er du sikker på at du vil slette %1$s?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -1258,6 +1258,10 @@
<string name="qr_scanner_dialog_positive">TOESTAAN</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">WEIGEREN</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Webadres niet geldig.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">OK</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Weet u zeker dat u %1$s wilt verwijderen?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -120,7 +120,11 @@
<string name="tab_tray_inactive_auto_close_button_content_description">Tampar</string>
<!-- Text for turn on auto close tabs button in the auto-close dialog of the inactive tabs. -->
<string name="tab_tray_inactive_turn_on_auto_close_button">Activar la tampadura auto</string>
<string moz:removedIn="95" name="tab_tray_inactive_turn_on_auto_close_button" tools:ignore="UnusedResources">Activar la tampadura auto</string>
<!-- Text for turn on auto close tabs button in the auto-close dialog of the inactive tabs. -->
<string name="tab_tray_inactive_turn_on_auto_close_button_2">Activar la tampadura auto</string>
<!-- Home screen icons - Long press shortcuts -->
<!-- Shortcut action to open new tab -->
@ -760,7 +764,7 @@
<string name="studies_learn_more">Ne saber mai</string>
<!-- Dialog message shown after removing a study -->
<string name="studies_restart_app">Laplicacion es a se tampar per aplicar las modifcacions</string>
<string name="studies_restart_app">Laplicacion es a se tampar per aplicar las modificacions</string>
<!-- Dialog button to confirm the removing a study. -->
<string name="studies_restart_dialog_ok">Dacòrdi</string>
<!-- Dialog button text for canceling removing a study. -->
@ -1258,6 +1262,10 @@
<string name="qr_scanner_dialog_positive">AUTORIZAR</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">REFUSAR</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Adreça web pas valida.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">Dacòrdi</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Volètz vertadièrament suprimir %1$s ?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->
@ -2034,11 +2042,11 @@
<!-- Content description for closing all inactive tabs -->
<string name="inactive_tabs_delete_all">Tampar los onglets inactius</string>
<!-- A description below the section of "inactive" tabs to notify the user when those tabs will be closed, if appropriate. See strings inactive_tabs_30_days and inactive_tabs_7_days for placeholders options. -->
<string moz:removedIn="93" name="inactive_tabs_description" tools:ignore="UnusedResources">Los onglets son disponible aicí pendent %s jorns. Aprèp, seràn automaticament tampats.</string>
<string moz:removedIn="95" name="inactive_tabs_description" tools:ignore="UnusedResources">Los onglets son disponible aicí pendent %s jorns. Aprèp, seràn automaticament tampats.</string>
<!-- The amount of time until a tab in the "inactive" section of the tabs tray will be closed. See string inactive_tabs_description as well -->
<string moz:removedIn="93" name="inactive_tabs_30_days" tools:ignore="UnusedResources">30 jorns</string>
<string moz:removedIn="95" name="inactive_tabs_30_days" tools:ignore="UnusedResources">30 jorns</string>
<!-- The amount of time until a tab in the "inactive" section of the tabs tray will be closed. See string inactive_tabs_description as well -->
<string moz:removedIn="93" name="inactive_tabs_7_days" tools:ignore="UnusedResources">1 setmana</string>
<string moz:removedIn="95" name="inactive_tabs_7_days" tools:ignore="UnusedResources">1 setmana</string>
<!-- Inactive tabs auto-close message in the tabs tray -->
<!-- The header text of the auto-close message when the user is asked if they want to turn on the auto-closing of inactive tabs. -->
@ -2049,22 +2057,34 @@
<!-- A call to action below the description to allow the user to turn on the auto closing of inactive tabs. -->
<string name="inactive_tabs_auto_close_message_action" tools:ignore="UnusedResources">ACTIVAR LA TAMPADURA AUTO</string>
<!-- Text for the snackbar to confirm auto-close is enabled for inactive tabs -->
<string name="inactive_tabs_auto_close_message_snackbar">Tampadura auto activada</string>
<!-- Inactive tabs survey -->
<!-- Header text for the inactive tabs survey asking for feedback to improve the inactive tabs feature. -->
<string name="inactive_tabs_survey_header" tools:ignore="UnusedResources">Mercés de vos ajudar a melhorar</string>
<string moz:removedIn="95" name="inactive_tabs_survey_header" tools:ignore="UnusedResources">Mercés de vos ajudar a melhorar</string>
<!-- Header text for the inactive tabs survey asking for feedback to improve the inactive tabs feature. -->
<string name="inactive_tabs_survey_header_1">Ajudatz a melhorar Firefox</string>
<!-- Content text for the inactive tabs survey asking the primary survey feedback question. -->
<string name="inactive_tabs_survey_content" tools:ignore="UnusedResources">Perqué avètz desactivat los onglets inactius?</string>
<string name="inactive_tabs_survey_content">Perqué avètz desactivat los onglets inactius?</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_do_not_understand">Compreni pas cossí fonciona</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_do_it_myself">Mestimi mai escafar los onglets ancians solet</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string moz:removedIn="95" name="inactive_tabs_survey_time_too_long_option" tools:ignore="UnusedResources">Lo temps dinactivitat es tròp long</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_not_interested_option" tools:ignore="UnusedResources">Pas interessat per aquesta foncionalitat</string>
<string name="inactive_tabs_survey_time_too_long_option_1">Lo periòde de doas setmanas es tròp long</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_time_too_long_option" tools:ignore="UnusedResources">Lo temps dinactivitat es tròp long</string>
<string moz:removedIn="95" name="inactive_tabs_survey_time_too_short_option" tools:ignore="UnusedResources">Lo temps dinactivitat es tròp cort</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_time_too_short_option" tools:ignore="UnusedResources">Lo temps dinactivitat es tròp cort</string>
<string name="inactive_tabs_survey_time_too_short_option_1">Lo periòde de doas setmanas es tròp cort</string>
<!-- Confirmation button text to submit the feedback for the inactive tabs survey. -->
<string name="inactive_tabs_survey_send_button" tools:ignore="UnusedResources">Mandar</string>
<string name="inactive_tabs_survey_send_button">Mandar</string>
<!-- Content description for inactive tabs survey close button -->
<string name="inactive_tabs_survey_close_button_content_description" tools:ignore="UnusedResources">Tampar</string>
<string name="inactive_tabs_survey_close_button_content_description">Tampar</string>
<!-- Default browser experiment -->
<string name="default_browser_experiment_card_text">Causir de dobrir los sites web, los corrièls e messatges automaticament dins Firefox.</string>

@ -2021,11 +2021,11 @@
<!-- Content description for closing all inactive tabs -->
<string name="inactive_tabs_delete_all">ਸਾਰੀਆਂ ਨਾ-ਸਰਗਰਮ ਟੈਬਾਂ ਬੰਦ ਕਰੋ</string>
<!-- A description below the section of "inactive" tabs to notify the user when those tabs will be closed, if appropriate. See strings inactive_tabs_30_days and inactive_tabs_7_days for placeholders options. -->
<string moz:removedIn="93" name="inactive_tabs_description" tools:ignore="UnusedResources">ਟੈਬਾਂ %s ਦਿਨਾਂ ਲਈ ਇੱਥੇ ਮੌਜੂਦ ਹੁੰਦੀਆਂ ਹਨ। ਉਸ ਸਮੇਂ ਬਾਅਦ, ਟੈਬਾਂ ਨੂੰ ਆਪਣੇ-ਆਪ ਬੰਦ ਕੀਤਾ ਜਾਵੇਗਾ।</string>
<string moz:removedIn="95" name="inactive_tabs_description" tools:ignore="UnusedResources">ਟੈਬਾਂ %s ਦਿਨਾਂ ਲਈ ਇੱਥੇ ਮੌਜੂਦ ਹੁੰਦੀਆਂ ਹਨ। ਉਸ ਸਮੇਂ ਬਾਅਦ, ਟੈਬਾਂ ਨੂੰ ਆਪਣੇ-ਆਪ ਬੰਦ ਕੀਤਾ ਜਾਵੇਗਾ।</string>
<!-- The amount of time until a tab in the "inactive" section of the tabs tray will be closed. See string inactive_tabs_description as well -->
<string moz:removedIn="93" name="inactive_tabs_30_days" tools:ignore="UnusedResources">30 ਦਿਨ</string>
<string moz:removedIn="95" name="inactive_tabs_30_days" tools:ignore="UnusedResources">30 ਦਿਨ</string>
<!-- The amount of time until a tab in the "inactive" section of the tabs tray will be closed. See string inactive_tabs_description as well -->
<string moz:removedIn="93" name="inactive_tabs_7_days" tools:ignore="UnusedResources">1 ਹਫ਼ਤਾ</string>
<string moz:removedIn="95" name="inactive_tabs_7_days" tools:ignore="UnusedResources">1 ਹਫ਼ਤਾ</string>
<!-- Inactive tabs auto-close message in the tabs tray -->
<!-- The header text of the auto-close message when the user is asked if they want to turn on the auto-closing of inactive tabs. -->
@ -2040,19 +2040,28 @@
<!-- Inactive tabs survey -->
<!-- Header text for the inactive tabs survey asking for feedback to improve the inactive tabs feature. -->
<string name="inactive_tabs_survey_header" tools:ignore="UnusedResources">ਸੁਧਾਰ ਕਰਨ ਲਈ ਸਾਡੀ ਮਦਦ ਕਰੋ</string>
<string moz:removedIn="95" name="inactive_tabs_survey_header" tools:ignore="UnusedResources">ਸੁਧਾਰ ਕਰਨ ਲਈ ਸਾਡੀ ਮਦਦ ਕਰੋ</string>
<!-- Header text for the inactive tabs survey asking for feedback to improve the inactive tabs feature. -->
<string name="inactive_tabs_survey_header_1">Firefox ਸੁਧਾਰਨ ਲਈ ਮਦਦ</string>
<!-- Content text for the inactive tabs survey asking the primary survey feedback question. -->
<string name="inactive_tabs_survey_content" tools:ignore="UnusedResources">ਤੁਸੀਂ ਨਾ-ਸਰਗਰਮ ਟੈਬਾਂ ਨੂੰ ਅਸਮਰੱਥ ਕਿਉਂ ਕੀਤਾ ਸੀ?</string>
<string name="inactive_tabs_survey_content">ਤੁਸੀਂ ਨਾ-ਸਰਗਰਮ ਟੈਬਾਂ ਨੂੰ ਅਸਮਰੱਥ ਕਿਉਂ ਕੀਤਾ ਸੀ?</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_do_not_understand">ਮੈਨੂੰ ਸਮਝ ਨਹੀਂ ਆਈ ਕਿ ਇਹ ਕਿਵੇਂ ਕਰਦਾ ਹੈ</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_do_it_myself">ਮੈਂ ਆਪਣੀਆਂ ਪੁਰਾਣੀਆਂ ਟੈਬਾਂ ਖੁਦ ਸਾਫ਼ ਕਰਨੀਆਂ ਹਨ</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string moz:removedIn="95" name="inactive_tabs_survey_time_too_long_option" tools:ignore="UnusedResources">ਨਾ-ਸਰਗਰਮੀ ਲਈ ਸਮਾਂ ਬਹੁਤ ਜ਼ਿਆਦਾ ਹੈ</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_not_interested_option" tools:ignore="UnusedResources">ਫ਼ੀਚਰ ਵਿੱਚ ਦਿਲਚਸਪੀ ਨਹੀਂ ਹੈ</string>
<string name="inactive_tabs_survey_time_too_long_option_1">ਦੋ ਹਫ਼ਤੇ ਦਾ ਸਮਾਂ ਬਹੁਤ ਜ਼ਿਆਦਾ ਹੈ</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_time_too_long_option" tools:ignore="UnusedResources">ਨਾ-ਸਰਗਰਮੀ ਲਈ ਸਮਾਂ ਬਹੁਤ ਜ਼ਿਆਦਾ ਹੈ</string>
<string moz:removedIn="95" name="inactive_tabs_survey_time_too_short_option" tools:ignore="UnusedResources">ਨਾ-ਸਰਗਰਮੀ ਲਈ ਸਮਾਂ ਬਹੁਤ ਘੱਟ ਹੈ</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_time_too_short_option" tools:ignore="UnusedResources">ਨਾ-ਸਰਗਰਮੀ ਲਈ ਸਮਾਂ ਬਹੁਤ ਘੱਟ ਹੈ</string>
<string name="inactive_tabs_survey_time_too_short_option_1">ਦੋ ਹਫ਼ਤੇ ਦਾ ਸਮਾਂ ਬਹੁਤ ਘੱਟ ਹੈ</string>
<!-- Confirmation button text to submit the feedback for the inactive tabs survey. -->
<string name="inactive_tabs_survey_send_button" tools:ignore="UnusedResources">ਭੇਜੋ</string>
<string name="inactive_tabs_survey_send_button">ਭੇਜੋ</string>
<!-- Content description for inactive tabs survey close button -->
<string name="inactive_tabs_survey_close_button_content_description" tools:ignore="UnusedResources">ਬੰਦ ਕਰੋ</string>
<string name="inactive_tabs_survey_close_button_content_description">ਬੰਦ ਕਰੋ</string>
<!-- Default browser experiment -->
<string name="default_browser_experiment_card_text">ਵੈੱਬਸਾਈਟਾਂ, ਈਮੇਲਾਂ ਅਤੇ ਸੁਨੇਹਿਆਂ ਨੂੰ Firefox ਵਿੱਚ ਆਪਣੇ ਖੋਲ੍ਹਣ ਲਈ ਲਿੰਕ ਸੈੱਟ ਕਰੋ।</string>

@ -1252,6 +1252,10 @@
<string name="qr_scanner_dialog_positive">Zezwól</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">Odmów</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Nieprawidłowy adres internetowy.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">OK</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Czy na pewno usunąć „%1$s”?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -1250,6 +1250,10 @@
<string name="qr_scanner_dialog_positive">PERMITIR</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">NEGAR</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Endereço web não válido.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">OK</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Tem certeza que quer excluir %1$s?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -1257,6 +1257,10 @@
<string name="qr_scanner_dialog_positive">POVOLIŤ</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">ZAMIETNUŤ</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Webová adresa je neplatná.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">OK</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Naozaj chcete odstrániť kolekciu %1$s?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -1242,6 +1242,10 @@
<string name="qr_scanner_dialog_positive">DOVOLI</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">ZAVRNI</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Spletni naslov ni veljaven.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">V redu</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Ali ste prepričani, da želite izbrisati %1$s?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -1252,6 +1252,10 @@
<string name="qr_scanner_dialog_positive">IDINAN</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">HULAG</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Alamat raramatna teu bener.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">HEUG</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Anjeun yakin rék mupus %1$s?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -1262,6 +1262,10 @@
<string name="qr_scanner_dialog_positive">TILLÅT</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">NEKA</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Webbadressen är inte giltig.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">OK</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Är du säker att du vill ta bort %1$s?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -1249,6 +1249,10 @@
<string name="qr_scanner_dialog_positive">ИҶОЗАТ ДОДАН</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">РАД КАРДАН</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Нишонии сомона нодуруст аст.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">ХУБ</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Шумо мутмаин ҳастед, ки мехоҳед %1$s-ро нест намоед?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -107,12 +107,25 @@
<!-- Text for the negative action button to dismiss the Close Tabs Banner. -->
<string name="tab_tray_close_tabs_banner_negative_button_text">ยกเลิก</string>
<!-- Text for the banner message to tell users about our inactive tabs feature. -->
<string name="tab_tray_inactive_onboarding_message">แท็บที่คุณไม่ได้ดูเป็นเวลา 2 สัปดาห์จะถูกย้ายมาที่นี่</string>
<!-- Text for the action link to go to Settings for inactive tabs. -->
<string name="tab_tray_inactive_onboarding_button_text">ปิดในการตั้งค่า</string>
<!-- Text for title for the auto-close dialog of the inactive tabs. -->
<string name="tab_tray_inactive_auto_close_title">ปิดอัตโนมัติหลังจากหนึ่งเดือน?</string>
<!-- Text for the body for the auto-close dialog of the inactive tabs. -->
<string name="tab_tray_inactive_auto_close_body">Firefox สามารถปิดแท็บที่คุณไม่ได้ดูในช่วงเดือนที่ผ่านมาได้</string>
<!-- Content description for close button in the auto-close dialog of the inactive tabs. -->
<string name="tab_tray_inactive_auto_close_button_content_description">ปิด</string>
<!-- Text for turn on auto close tabs button in the auto-close dialog of the inactive tabs. -->
<string moz:removedIn="95" name="tab_tray_inactive_turn_on_auto_close_button" tools:ignore="UnusedResources">เปิดการปิดอัตโนมัติ</string>
<!-- Text for turn on auto close tabs button in the auto-close dialog of the inactive tabs. -->
<string name="tab_tray_inactive_turn_on_auto_close_button_2">เปิดการปิดอัตโนมัติ</string>
<!-- Home screen icons - Long press shortcuts -->
<!-- Shortcut action to open new tab -->
<string name="home_screen_shortcut_open_new_tab_2">แท็บใหม่</string>
@ -128,6 +141,8 @@
<!-- Button text for showing all the tabs in the tabs tray -->
<string name="recent_tabs_show_all">แสดงหมด</string>
<!-- Content description for the button which navigates the user to show all recent tabs in the tabs tray. -->
<string name="recent_tabs_show_all_content_description">แสดงปุ่มแท็บล่าสุดทั้งหมด</string>
<!-- Title for showing a group item in the 'Jump back in' section of the new tab
The first parameter is the search term that the user used. (for example: your search for "cat")-->
<string name="recent_tabs_search_term">การค้นหา \&quot;%1$s\&quot; ของคุณ</string>
@ -150,6 +165,9 @@
in the Recently visited section -->
<string name="recently_visited_menu_item_remove">ลบ</string>
<!-- Content description for the button which navigates the user to show all of their history. -->
<string name="past_explorations_show_all_content_description">แสดงปุ่มการสำรวจที่ผ่านมาทั้งหมด</string>
<!-- Browser Fragment -->
<!-- Content description (not visible, for screen readers etc.): Navigate to open tabs -->
<string name="browser_tabs_button">แท็บที่เปิดอยู่</string>
@ -279,9 +297,25 @@
<!-- Onboarding home screen dialog title text. Firefox is intentionally hardcoded. -->
<string name="onboarding_home_screen_title_2">มีอะไรใหม่ใน Firefox</string>
<!-- Onboarding home screen dialog description text. -->
<string name="onboarding_home_screen_description_2">คุณสามารถกลับมาดูหน้าเว็บที่คุณดูค้างไว้ได้ง่ายขึ้นแล้ว</string>
<!-- Onboarding home screen dialog title text for the home section. Firefox is intentionally hardcoded. -->
<string name="onboarding_home_screen_section_home_title_2">หน้าแรกของ Firefox ที่ปรับแต่งแบบส่วนตัว</string>
<!-- Onboarding home screen dialog description text for the home section. -->
<string name="onboarding_home_screen_section_home_description_2">ข้ามไปยังแท็บที่เปิดอยู่ ที่คั่นหน้า และประวัติการเรียกดู</string>
<!-- Onboarding home screen dialog description text for the tab tray section. -->
<string name="onboarding_home_screen_section_cleaner_tab_tray_title_2">แท็บที่สะอาดและเป็นระเบียบ</string>
<!-- Onboarding home screen dialog description text for the tab tray section. -->
<string name="onboarding_home_screen_section_cleaner_tab_tray_description_2">ขจัดความยุ่งเหยิงของแท็บด้วยเลย์เอาต์ที่ได้รับการปรับปรุงและแท็บที่ปิดอัตโนมัติ</string>
<!-- Onboarding home screen dialog description text for the history section. -->
<string name="onboarding_home_screen_section_useful_history_title_2">ผลค้นหาล่าสุด</string>
<!-- Onboarding home screen dialog description text for the history section. -->
<string name="onboarding_home_screen_section_useful_history_description_2">เยี่ยมชมการค้นหาล่าสุดของคุณใหม่จากหน้าแรกและแท็บของคุณ</string>
<!-- Onboarding home screen popup dialog, shown on top of the Jump back in section. Firefox is intentionally hardcoded. -->
<string name="onboarding_home_screen_jump_back_contextual_hint" tools:ignore="UnusedResources">ขณะนี้หน้าแรกของ Firefox ที่ปรับแต่งแบบคุณได้รับการปรับปรุงให้คุณสามารถกลับมาดูหน้าเว็บที่คุณดูค้างไว้ได้ง่ายขึ้นแล้ว ค้นหาแท็บ ที่คั่นหน้า และผลการค้นหาล่าสุดของคุณ</string>
<!-- Search Widget -->
<!-- Content description for searching with a widget. Firefox is intentionally hardcoded.-->
<string name="search_widget_content_description">เปิดแท็บ Firefox ใหม่</string>
@ -662,6 +696,8 @@
<string name="tab_view_list">รายการ</string>
<!-- Option for a grid tab view -->
<string name="tab_view_grid">เส้นตาราง</string>
<!-- Option for search term tab groups -->
<string name="tab_view_search_term_tab_groups">ค้นหากลุ่ม</string>
<!-- Summary text for search term tab groups -->
<string name="tab_view_search_term_tab_groups_summary">จัดกลุ่มเว็บไซต์ที่เกี่ยวข้องกัน</string>
<!-- Title of preference that allows a user to auto close tabs after a specified amount of time -->
@ -675,9 +711,14 @@
<!-- Option for auto closing tabs that will auto close tabs after one month -->
<string name="close_tabs_after_one_month">หลังจากผ่านไปหนึ่งเดือน</string>
<!-- Title of preference that allows a user to specify the auto-close settings for open tabs -->
<string name="preference_auto_close_tabs" tools:ignore="UnusedResources">ปิดแท็บที่เปิดอยู่อัตโนมัติ</string>
<!-- Opening screen -->
<!-- Title of a preference that allows a user to indicate after a specified amount of time when the app should start on the home screen -->
<string moz:removedIn="94" name="preferences_start_on_home" tools:ignore="UnusedResources">เริ่มที่หน้าแรก</string>
<!-- Title of a preference that allows a user to choose what screen to show after opening the app -->
<string name="preferences_opening_screen">หน้าจอเมื่อเปิด</string>
<!-- Option for starting on the home screen after after four hours or inactivity -->
<string moz:removedIn="94" name="start_on_home_after_four_hours" tools:ignore="UnusedResources">หลังจาก 4 ชั่วโมง</string>
<!-- Option for always opening the homepage when re-opening the app -->
@ -688,6 +729,8 @@
<string name="opening_screen_last_tab">แท็บสุดท้าย</string>
<!-- Option for never starting on the home screen -->
<string moz:removedIn="94" name="start_on_home_never" tools:ignore="UnusedResources">ไม่เลย</string>
<!-- Option for always opening the homepage when re-opening the app after four hours of inactivity -->
<string name="opening_screen_after_four_hours_of_inactivity">หน้าแรกหลังจากไม่มีการใช้งานเป็นเวลา 4 ชั่วโมง</string>
<!-- Summary for tabs preference when auto closing tabs setting is set to manual close-->
<string name="close_tabs_manually_summary">ปิดด้วยตนเอง</string>
<!-- Summary for tabs preference when auto closing tabs setting is set to auto close tabs after one day-->
@ -697,6 +740,13 @@
<!-- Summary for tabs preference when auto closing tabs setting is set to auto close tabs after one month-->
<string name="close_tabs_after_one_month_summary">ปิดหลังจากหนึ่งเดือน</string>
<!-- Inactive tabs -->
<!-- Category header of a preference that allows a user to enable or disable the inactive tabs feature -->
<string name="preferences_inactive_tabs">ย้ายแท็บเก่าไปยังส่วนที่ไม่ได้ใช้งาน</string>
<!-- Title of inactive tabs preference -->
<string name="preferences_inactive_tabs_title">แท็บที่คุณไม่ได้ดูเป็นเวลา 2 สัปดาห์จะถูกย้ายไปยังส่วนที่ไม่ได้ใช้งาน</string>
<!-- Studies -->
<!-- Title of the remove studies button -->
<string name="studies_remove">เอาออก</string>
@ -1197,6 +1247,10 @@
<string name="qr_scanner_dialog_positive">อนุญาต</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">ปฏิเสธ</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">ที่อยู่เว็บไม่ถูกต้อง</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">ตกลง</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">คุณแน่ใจหรือไม่ว่าต้องการลบ %1$s?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->
@ -1965,12 +2019,33 @@
<!-- The header text of the auto-close message when the user is asked if they want to turn on the auto-closing of inactive tabs. -->
<string name="inactive_tabs_auto_close_message_header" tools:ignore="UnusedResources">ปิดอัตโนมัติหลังจากหนึ่งเดือน?</string>
<!-- A description below the header to notify the user what the inactive tabs auto-close feature is. -->
<string name="inactive_tabs_auto_close_message_description" tools:ignore="UnusedResources">Firefox สามารถปิดแท็บที่คุณไม่ได้ดูในช่วงเดือนที่ผ่านมาได้</string>
<!-- A call to action below the description to allow the user to turn on the auto closing of inactive tabs. -->
<string name="inactive_tabs_auto_close_message_action" tools:ignore="UnusedResources">เปิดการปิดอัตโนมัติ</string>
<!-- Text for the snackbar to confirm auto-close is enabled for inactive tabs -->
<string name="inactive_tabs_auto_close_message_snackbar">เปิดใช้งานการปิดอัตโนมัติแล้ว</string>
<!-- Inactive tabs survey -->
<!-- Header text for the inactive tabs survey asking for feedback to improve the inactive tabs feature. -->
<string moz:removedIn="95" name="inactive_tabs_survey_header" tools:ignore="UnusedResources">ช่วยปรับปรุง Firefox</string>
<!-- Header text for the inactive tabs survey asking for feedback to improve the inactive tabs feature. -->
<string name="inactive_tabs_survey_header_1">ช่วยปรับปรุง Firefox</string>
<!-- Content text for the inactive tabs survey asking the primary survey feedback question. -->
<string name="inactive_tabs_survey_content">เหตุใดคุณจึงปิดใช้งานแท็บที่ไม่ได้ใช้งาน</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_do_not_understand">ฉันไม่เข้าใจว่ามันทำงานอย่างไร</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_do_it_myself">ฉันชอบล้างแท็บเก่าด้วยตัวเอง</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string moz:removedIn="95" name="inactive_tabs_survey_time_too_long_option" tools:ignore="UnusedResources">ระยะเวลา 2 สัปดาห์ยาวเกินไป</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_time_too_long_option_1">ระยะเวลา 2 สัปดาห์ยาวเกินไป</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string moz:removedIn="95" name="inactive_tabs_survey_time_too_short_option" tools:ignore="UnusedResources">ระยะเวลา 2 สัปดาห์สั้นเกินไป</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_time_too_short_option_1">ระยะเวลา 2 สัปดาห์สั้นเกินไป</string>
<!-- Confirmation button text to submit the feedback for the inactive tabs survey. -->
<string name="inactive_tabs_survey_send_button">ส่ง</string>
<!-- Content description for inactive tabs survey close button -->
@ -1991,12 +2066,19 @@
<!-- Content description for privacy content close button -->
<string name="privacy_content_close_button_content_description">ปิด</string>
<!-- Pocket recommended stories -->
<!-- Header text for a section on the home screen. -->
<string moz:removedIn="94" name="pocket_stories_header" tools:ignore="UnusedResources">เรื่องราวที่จุดประกายความคิด</string>
<!-- Header text for a section on the home screen. -->
<string name="pocket_stories_header_1">เรื่องราวที่จุดประกายความคิด</string>
<!-- Header text for a section on the home screen. -->
<string name="pocket_stories_categories_header">เรื่องตามหัวข้อ</string>
<!-- Text of a button allowing users to access an external url for more Pocket recommendations. -->
<string name="pocket_stories_placeholder_text">ค้นพบสิ่งอื่น</string>
<!-- Title of an app feature. Smaller than a heading.-->
<string name="pocket_stories_feature_title">ขับเคลื่อนโดย Pocket</string>
<!-- Caption for describing a certain feature. The placeholder is for a clickable text (eg: Learn more) which will load an url in a new tab when clicked. -->
<string name="pocket_stories_feature_caption">ส่วนหนึ่งของตระกูล Firefox %s</string>
<!-- Clickable text for opening an external link for more information about Pocket. -->
<string name="pocket_stories_feature_learn_more">เรียนรู้เพิ่มเติม</string>
</resources>

@ -1249,6 +1249,10 @@
<string name="qr_scanner_dialog_positive">İZİN VER</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">İZİN VERME</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Web adresi geçersiz.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">Tamam</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">%1$s koleksiyonunu silmek istediğinize emin misiniz?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -1257,6 +1257,10 @@
<string name="qr_scanner_dialog_positive">ДОЗВОЛИТИ</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">ЗАБОРОНИТИ</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Вебадреса недійсна.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">Гаразд</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Ви дійсно хочете видалити %1$s?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -1242,6 +1242,10 @@
<string name="qr_scanner_dialog_positive">CHO PHÉP</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">TỪ CHỐI</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">Địa chỉ web không hợp lệ.</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">OK</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">Bạn có chắc chắn muốn xóa %1$s không?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -1291,6 +1291,10 @@
<string name="qr_scanner_dialog_positive">允许</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">拒绝</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">无效网址。</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">确定</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">您确定要删除“%1$s”吗</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -1276,6 +1276,10 @@
<string name="qr_scanner_dialog_positive">允許</string>
<!-- QR code scanner prompt dialog positive option to deny navigation to scanned link -->
<string name="qr_scanner_dialog_negative">拒絕</string>
<!-- QR code scanner prompt dialog error message shown when a hostname does not contain http or https. -->
<string name="qr_scanner_dialog_invalid">網址不正確。</string>
<!-- QR code scanner prompt dialog positive option when there is an error -->
<string name="qr_scanner_dialog_invalid_ok">確定</string>
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
<string name="tab_collection_dialog_message">您確定要刪除 %1$s 嗎?</string>
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->

@ -32,8 +32,8 @@ import org.mozilla.fenix.GleanMetrics.Preferences
import org.mozilla.fenix.GleanMetrics.SearchDefaultEngine
import org.mozilla.fenix.components.metrics.MozillaProductDetector
import org.mozilla.fenix.components.toolbar.ToolbarPosition
import org.mozilla.fenix.ext.actualInactiveTabs
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.tabstray.ext.inactiveTabs
import org.mozilla.fenix.utils.BrowsersCache
import org.mozilla.fenix.utils.Settings
import org.robolectric.annotation.Config
@ -143,8 +143,8 @@ class FenixApplicationTest {
every { settings.searchTermTabGroupsAreEnabled } returns true
every { application.reportHomeScreenMetrics(settings) } just Runs
every { settings.inactiveTabsAreEnabled } returns true
mockkStatic("org.mozilla.fenix.tabstray.ext.TabSelectorsKt") {
every { browserStore.state.inactiveTabs } returns listOf(mockk(), mockk())
mockkStatic("org.mozilla.fenix.ext.BrowserStateKt") {
every { browserStore.state.actualInactiveTabs(any()) } returns listOf(mockk(), mockk())
application.setStartupMetrics(browserStore, settings, browsersCache, mozillaProductDetector)

@ -146,4 +146,150 @@ class PagedHistoryProviderTest {
)
assertEquals(results, actualResults)
}
@Test
fun `history metadata matching lower bound`() {
val provider = DefaultPagedHistoryProvider(
historyStorage = storage,
showHistorySearchGroups = true
)
// Oldest history visit on the page is 15 seconds (buffer time) newer than matching
// metadata record.
val visitInfo1 = VisitInfo(
url = "http://www.mozilla.com",
title = "mozilla",
visitTime = 25000,
visitType = VisitType.LINK,
previewImageUrl = null
)
val historyMetadataKey1 = HistoryMetadataKey("http://www.mozilla.com", "mozilla", null)
val historyEntry1 = HistoryMetadata(
key = historyMetadataKey1,
title = "mozilla",
createdAt = 10000,
updatedAt = 10,
totalViewTime = 10,
documentType = DocumentType.Regular,
previewImageUrl = null
)
coEvery { storage.getVisitsPaginated(any(), any(), any()) } returns listOf(visitInfo1)
coEvery { storage.getHistoryMetadataSince(any()) } returns listOf(historyEntry1)
var actualResults: List<History>? = null
provider.getHistory(0L, 5) {
actualResults = it
}
coVerify {
storage.getVisitsPaginated(
offset = 0L,
count = 5,
excludeTypes = listOf(
VisitType.NOT_A_VISIT,
VisitType.DOWNLOAD,
VisitType.REDIRECT_TEMPORARY,
VisitType.RELOAD,
VisitType.EMBED,
VisitType.FRAMED_LINK,
VisitType.REDIRECT_PERMANENT
)
)
}
val results = listOf(
History.Group(
id = historyEntry1.createdAt.toInt(),
title = historyEntry1.key.searchTerm!!,
visitedAt = historyEntry1.createdAt,
// Results are de-duped by URL and sorted descending by createdAt/visitedAt
items = listOf(
History.Metadata(
id = historyEntry1.createdAt.toInt(),
title = historyEntry1.title!!,
url = historyEntry1.key.url,
visitedAt = historyEntry1.createdAt,
totalViewTime = historyEntry1.totalViewTime,
historyMetadataKey = historyMetadataKey1
)
)
)
)
assertEquals(results, actualResults)
}
@Test
fun `history metadata matching upper bound`() {
val provider = DefaultPagedHistoryProvider(
historyStorage = storage,
showHistorySearchGroups = true
)
// Newest history visit on the page is 15 seconds (buffer time) older than matching
// metadata record.
val visitInfo1 = VisitInfo(
url = "http://www.mozilla.com",
title = "mozilla",
visitTime = 10000,
visitType = VisitType.LINK,
previewImageUrl = null
)
val historyMetadataKey1 = HistoryMetadataKey("http://www.mozilla.com", "mozilla", null)
val historyEntry1 = HistoryMetadata(
key = historyMetadataKey1,
title = "mozilla",
createdAt = 25000,
updatedAt = 10,
totalViewTime = 10,
documentType = DocumentType.Regular,
previewImageUrl = null
)
coEvery { storage.getVisitsPaginated(any(), any(), any()) } returns listOf(visitInfo1)
coEvery { storage.getHistoryMetadataSince(any()) } returns listOf(historyEntry1)
var actualResults: List<History>? = null
provider.getHistory(0L, 5) {
actualResults = it
}
coVerify {
storage.getVisitsPaginated(
offset = 0L,
count = 5,
excludeTypes = listOf(
VisitType.NOT_A_VISIT,
VisitType.DOWNLOAD,
VisitType.REDIRECT_TEMPORARY,
VisitType.RELOAD,
VisitType.EMBED,
VisitType.FRAMED_LINK,
VisitType.REDIRECT_PERMANENT
)
)
}
val results = listOf(
History.Group(
id = historyEntry1.createdAt.toInt(),
title = historyEntry1.key.searchTerm!!,
visitedAt = historyEntry1.createdAt,
// Results are de-duped by URL and sorted descending by createdAt/visitedAt
items = listOf(
History.Metadata(
id = historyEntry1.createdAt.toInt(),
title = historyEntry1.title!!,
url = historyEntry1.key.url,
visitedAt = historyEntry1.createdAt,
totalViewTime = historyEntry1.totalViewTime,
historyMetadataKey = historyMetadataKey1
)
)
)
)
assertEquals(results, actualResults)
}
}

@ -176,6 +176,10 @@ class GleanMetricsServiceTest {
val events = History.recentSearchesTapped.testGetValue()
assertEquals(1, events[0].extra!!.size)
assertEquals("5", events[0].extra!!["page_number"])
assertFalse(History.searchTermGroupTapped.testHasValue())
gleanService.track(Event.HistorySearchTermGroupTapped)
assertTrue(History.searchTermGroupTapped.testHasValue())
}
@Test

@ -333,6 +333,10 @@ class MetricControllerTest {
every { marketingService1.shouldTrack(Event.HistoryOpenedInNewTabs) } returns true
every { marketingService1.shouldTrack(Event.HistoryOpenedInPrivateTab) } returns true
every { marketingService1.shouldTrack(Event.HistoryOpenedInPrivateTabs) } returns true
every { marketingService1.shouldTrack(Event.HistoryItemRemoved) } returns true
every { marketingService1.shouldTrack(Event.HistoryAllItemsRemoved) } returns true
every { marketingService1.shouldTrack(Event.HistoryRecentSearchesTapped("2")) } returns true
every { marketingService1.shouldTrack(Event.HistorySearchTermGroupTapped) } returns true
controller.start(MetricServiceType.Marketing)
@ -340,11 +344,19 @@ class MetricControllerTest {
controller.track(Event.HistoryOpenedInNewTabs)
controller.track(Event.HistoryOpenedInPrivateTab)
controller.track(Event.HistoryOpenedInPrivateTabs)
controller.track(Event.HistoryItemRemoved)
controller.track(Event.HistoryAllItemsRemoved)
controller.track(Event.HistoryRecentSearchesTapped("2"))
controller.track(Event.HistorySearchTermGroupTapped)
verify { marketingService1.track(Event.HistoryOpenedInNewTab) }
verify { marketingService1.track(Event.HistoryOpenedInNewTabs) }
verify { marketingService1.track(Event.HistoryOpenedInPrivateTab) }
verify { marketingService1.track(Event.HistoryOpenedInPrivateTabs) }
verify { marketingService1.track(Event.HistoryItemRemoved) }
verify { marketingService1.track(Event.HistoryAllItemsRemoved) }
verify { marketingService1.track(Event.HistoryRecentSearchesTapped("2")) }
verify { marketingService1.track(Event.HistorySearchTermGroupTapped) }
}
@Test

@ -15,7 +15,6 @@ import org.junit.Before
import org.junit.Test
import org.mozilla.fenix.R
import org.mozilla.fenix.components.Components
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.utils.Settings
@ -40,19 +39,11 @@ class CrashReporterControllerTest {
every { currentDest.id } returns R.id.crashReporterFragment
}
@Test
fun `reports crash reporter opened`() {
CrashReporterController(crash, sessionId, navContoller, components, settings)
verify { components.analytics.metrics.track(Event.CrashReporterOpened) }
}
@Test
fun `handle close and restore tab`() {
val controller = CrashReporterController(crash, sessionId, navContoller, components, settings)
controller.handleCloseAndRestore(sendCrash = false)?.joinBlocking()
verify { components.analytics.metrics.track(Event.CrashReporterClosed(false)) }
verify { components.useCases.sessionUseCases.crashRecovery.invoke() }
verify { navContoller.popBackStack() }
}
@ -62,7 +53,6 @@ class CrashReporterControllerTest {
val controller = CrashReporterController(crash, sessionId, navContoller, components, settings)
controller.handleCloseAndRemove(sendCrash = false)?.joinBlocking()
verify { components.analytics.metrics.track(Event.CrashReporterClosed(false)) }
verify { components.useCases.tabsUseCases.removeTab(sessionId) }
verify { components.useCases.sessionUseCases.crashRecovery.invoke() }
verify {
@ -77,7 +67,9 @@ class CrashReporterControllerTest {
val controller = CrashReporterController(crash, sessionId, navContoller, components, settings)
controller.handleCloseAndRestore(sendCrash = true)?.joinBlocking()
verify { components.analytics.metrics.track(Event.CrashReporterClosed(false)) }
verify(exactly = 0) {
components.analytics.crashReporter.submitReport(crash)
}
}
@Test
@ -87,7 +79,8 @@ class CrashReporterControllerTest {
val controller = CrashReporterController(crash, sessionId, navContoller, components, settings)
controller.handleCloseAndRestore(sendCrash = true)?.joinBlocking()
verify { components.analytics.crashReporter.submitReport(crash) }
verify { components.analytics.metrics.track(Event.CrashReporterClosed(true)) }
verify {
components.analytics.crashReporter.submitReport(crash)
}
}
}

@ -4,6 +4,7 @@
package org.mozilla.fenix.ext
import io.mockk.every
import io.mockk.mockk
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.LastMediaAccessState
@ -11,8 +12,10 @@ import mozilla.components.browser.state.state.createTab
import mozilla.components.concept.storage.HistoryMetadataKey
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test
import org.mozilla.fenix.home.recenttabs.RecentTab
import org.mozilla.fenix.utils.Settings
class BrowserStateTest {
@ -318,4 +321,96 @@ class BrowserStateTest {
)
assertEquals(normalTab3.id, browserState.secondToLastOpenedNormalTab!!.id)
}
@Test
fun `GIVEN a list of tabs WHEN potentialInactiveTabs is called THEN return the normal tabs which haven't been active lately`() {
// An inactive tab is one created or accessed more than [maxActiveTime] ago
// checked against [System.currentTimeMillis]
//
// createTab() sets the [createdTime] property to [System.currentTimeMillis] this meaning
// that the default tab is considered active.
val normalTab1 = createTab(url = "url1", id = "1", createdAt = 1)
val normalTab2 = createTab(url = "url2", id = "2")
val normalTab3 = createTab(url = "url3", id = "3", createdAt = 1)
val normalTab4 = createTab(url = "url4", id = "4")
val privateTab1 = createTab(url = "url1", id = "6", private = true)
val privateTab2 = createTab(url = "url2", id = "7", private = true, createdAt = 1)
val privateTab3 = createTab(url = "url3", id = "8", private = true)
val privateTab4 = createTab(url = "url4", id = "9", private = true, createdAt = 1)
val browserState = BrowserState(
tabs = listOf(
normalTab1, normalTab2, normalTab3, normalTab4,
privateTab1, privateTab2, privateTab3, privateTab4
)
)
val result = browserState.potentialInactiveTabs
assertEquals(2, result.size)
assertTrue(result.containsAll(listOf(normalTab1, normalTab3)))
}
@Test
fun `GIVEN inactiveTabs feature is disabled WHEN actualInactiveTabs is called THEN return an empty result`() {
// An inactive tab is one created or accessed more than [maxActiveTime] ago
// checked against [System.currentTimeMillis]
//
// createTab() sets the [createdTime] property to [System.currentTimeMillis] this meaning
// that the default tab is considered active.
val normalTab1 = createTab(url = "url1", id = "1", createdAt = 1)
val normalTab2 = createTab(url = "url2", id = "2")
val normalTab3 = createTab(url = "url3", id = "3", createdAt = 1)
val normalTab4 = createTab(url = "url4", id = "4")
val privateTab1 = createTab(url = "url1", id = "6", private = true)
val privateTab2 = createTab(url = "url2", id = "7", private = true, createdAt = 1)
val privateTab3 = createTab(url = "url3", id = "8", private = true)
val privateTab4 = createTab(url = "url4", id = "9", private = true, createdAt = 1)
val browserState = BrowserState(
tabs = listOf(
normalTab1, normalTab2, normalTab3, normalTab4,
privateTab1, privateTab2, privateTab3, privateTab4
)
)
val settings: Settings = mockk() {
every { inactiveTabsAreEnabled } returns false
}
val result = browserState.actualInactiveTabs(settings)
assertTrue(result.isEmpty())
}
@Test
fun `GIVEN inactiveTabs feature is enabled WHEN actualInactiveTabs is called THEN return the normal tabs which haven't been active lately`() {
// An inactive tab is one created or accessed more than [maxActiveTime] ago
// checked against [System.currentTimeMillis]
//
// createTab() sets the [createdTime] property to [System.currentTimeMillis] this meaning
// that the default tab is considered active.
val normalTab1 = createTab(url = "url1", id = "1", createdAt = 1)
val normalTab2 = createTab(url = "url2", id = "2")
val normalTab3 = createTab(url = "url3", id = "3", createdAt = 1)
val normalTab4 = createTab(url = "url4", id = "4")
val privateTab1 = createTab(url = "url1", id = "6", private = true)
val privateTab2 = createTab(url = "url2", id = "7", private = true, createdAt = 1)
val privateTab3 = createTab(url = "url3", id = "8", private = true)
val privateTab4 = createTab(url = "url4", id = "9", private = true, createdAt = 1)
val browserState = BrowserState(
tabs = listOf(
normalTab1, normalTab2, normalTab3, normalTab4,
privateTab1, privateTab2, privateTab3, privateTab4
)
)
val settings: Settings = mockk() {
every { inactiveTabsAreEnabled } returns true
}
val result = browserState.actualInactiveTabs(settings)
assertEquals(2, result.size)
assertTrue(result.containsAll(listOf(normalTab1, normalTab3)))
}
}

@ -114,6 +114,55 @@ class HistoryMetadataMiddlewareTest {
}
}
@Test
fun `GIVEN normal tab is a search engine result page WHEN history metadata is recorded THEN search terms are provided`() {
service = TestingMetadataService()
middleware = HistoryMetadataMiddleware(service)
val tab = createTab("about:blank")
store = BrowserStore(
middleware = listOf(middleware) + EngineMiddleware.create(engine = mockk()),
initialState = BrowserState(
tabs = listOf(tab)
)
)
setupGoogleSearchEngine()
val serpUrl = "https://google.com?q=mozilla+website"
store.dispatch(EngineAction.LoadUrlAction(tab.id, serpUrl)).joinBlocking()
store.dispatch(ContentAction.UpdateUrlAction(tab.id, serpUrl)).joinBlocking()
store.dispatch(ContentAction.UpdateHistoryStateAction(tab.id, listOf(HistoryItem("Google Search", serpUrl)), currentIndex = 0)).joinBlocking()
with((service as TestingMetadataService).createdMetadata) {
assertEquals(1, this.count())
assertEquals("https://google.com?q=mozilla+website", this[0].url)
assertEquals("mozilla website", this[0].searchTerm)
assertNull(this[0].referrerUrl)
}
}
@Test
fun `GIVEN normal tab navigates to search engine result page WHEN history metadata is recorded THEN search terms are provided`() {
service = TestingMetadataService()
middleware = HistoryMetadataMiddleware(service)
val tab = createTab("https://google.com")
store = BrowserStore(
middleware = listOf(middleware) + EngineMiddleware.create(engine = mockk()),
initialState = BrowserState(
tabs = listOf(tab)
)
)
setupGoogleSearchEngine()
val serpUrl = "https://google.com?q=mozilla+website"
store.dispatch(ContentAction.UpdateUrlAction(tab.id, serpUrl)).joinBlocking()
store.dispatch(ContentAction.UpdateHistoryStateAction(tab.id, listOf(HistoryItem("Google Search", "https://google.com"), HistoryItem("Google Search", serpUrl)), currentIndex = 1)).joinBlocking()
with((service as TestingMetadataService).createdMetadata) {
assertEquals(1, this.count())
assertEquals("https://google.com?q=mozilla+website", this[0].url)
assertEquals("mozilla website", this[0].searchTerm)
assertNull(this[0].referrerUrl)
}
}
@Test
fun `GIVEN tab opened as new tab from a search page WHEN search page navigates away THEN redirecting or navigating in tab retains original search terms`() {
service = TestingMetadataService()
@ -132,7 +181,7 @@ class HistoryMetadataMiddlewareTest {
with((service as TestingMetadataService).createdMetadata) {
assertEquals(2, this.count())
assertEquals("https://google.com?q=mozilla+website", this[0].url)
assertNull(this[0].searchTerm)
assertEquals("mozilla website", this[0].searchTerm)
assertNull(this[0].referrerUrl)
assertEquals("https://google.com?url=https://mozilla.org", this[1].url)
@ -180,7 +229,7 @@ class HistoryMetadataMiddlewareTest {
assertEquals(5, this.count())
assertEquals("https://mozilla.org/manifesto", this[4].url)
assertEquals("mozilla website", this[4].searchTerm)
assertEquals("https://google.com?q=mozilla+website", this[4].referrerUrl)
assertEquals("https://mozilla.org", this[4].referrerUrl)
}
}
@ -202,7 +251,7 @@ class HistoryMetadataMiddlewareTest {
with((service as TestingMetadataService).createdMetadata) {
assertEquals(2, this.count())
assertEquals("https://google.com?q=mozilla+website", this[0].url)
assertNull(this[0].searchTerm)
assertEquals("mozilla website", this[0].searchTerm)
assertNull(this[0].referrerUrl)
assertEquals("https://google.com?url=https://mozilla.org", this[1].url)
@ -230,6 +279,7 @@ class HistoryMetadataMiddlewareTest {
// Direct load occurs on parent tab. Search terms should be cleared.
store.dispatch(EngineAction.LoadUrlAction(parentTab.id, "https://firefox.com")).joinBlocking()
store.dispatch(ContentAction.UpdateSearchTermsAction(parentTab.id, "")).joinBlocking()
store.dispatch(ContentAction.UpdateUrlAction(parentTab.id, "https://firefox.com")).joinBlocking()
store.dispatch(ContentAction.UpdateHistoryStateAction(parentTab.id, listOf(HistoryItem("Google - mozilla website", "https://google.com?q=mozilla+website"), HistoryItem("Firefox", "https://firefox.com")), 1)).joinBlocking()
with((service as TestingMetadataService).createdMetadata) {
@ -258,7 +308,7 @@ class HistoryMetadataMiddlewareTest {
with((service as TestingMetadataService).createdMetadata) {
assertEquals(2, this.count())
assertEquals("https://google.com?q=mozilla+website", this[0].url)
assertNull(this[0].searchTerm)
assertEquals("mozilla website", this[0].searchTerm)
assertNull(this[0].referrerUrl)
assertEquals("https://google.com?url=https://mozilla.org", this[1].url)
@ -286,6 +336,7 @@ class HistoryMetadataMiddlewareTest {
// Direct load occurs on parent tab. Search terms should be cleared.
store.dispatch(EngineAction.OptimizedLoadUrlTriggeredAction(parentTab.id, "https://firefox.com")).joinBlocking()
store.dispatch(ContentAction.UpdateSearchTermsAction(parentTab.id, "")).joinBlocking()
store.dispatch(ContentAction.UpdateUrlAction(parentTab.id, "https://firefox.com")).joinBlocking()
store.dispatch(ContentAction.UpdateHistoryStateAction(parentTab.id, listOf(HistoryItem("Google - mozilla website", "https://google.com?q=mozilla+website"), HistoryItem("Firefox", "https://firefox.com")), 1)).joinBlocking()
with((service as TestingMetadataService).createdMetadata) {
@ -298,7 +349,7 @@ class HistoryMetadataMiddlewareTest {
@Test
fun `GIVEN normal tab has parent WHEN url is the same THEN nothing happens`() {
val parentTab = createTab("https://mozilla.org", searchTerms = "mozilla website")
val parentTab = createTab("https://mozilla.org")
val tab = createTab("https://mozilla.org", parent = parentTab)
store.dispatch(TabListAction.AddTabAction(parentTab)).joinBlocking()
store.dispatch(TabListAction.AddTabAction(tab)).joinBlocking()

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save