Merge branch 'mozilla_main' into fork

pull/184/head
Abhijit Valluri 4 years ago
commit e04fbe2419

@ -10,7 +10,7 @@ jobs:
treeherder-symbol: Nd
target-tasks-method: nightly
when:
- {hour: 18, minute: 0}
- {hour: 5, minute: 0}
# This is a temporary hook in order to not overload Google Play.
# See bug 1628413 for more context.
- name: nightly-on-google-play
@ -19,7 +19,7 @@ jobs:
treeherder-symbol: Nd-gp
target-tasks-method: nightly-on-google-play
when:
- {hour: 6, minute: 0}
- {hour: 17, minute: 0}
- name: fennec-production
job:
type: decision-task
@ -31,7 +31,7 @@ jobs:
type: decision-task
treeherder-symbol: bump-ac
target-tasks-method: bump_android_components
when: [{hour: 14, minute: 0}]
when: [{hour: 15, minute: 30}]
- name: screenshots
job:
type: decision-task

@ -262,11 +262,11 @@ class TabbedBrowsingTest {
}.openTabTray {
verifyNoTabsOpened()
verifyNewTabButton()
verifyTabTrayOverflowMenu(false)
verifyTabTrayOverflowMenu(true)
}.toggleToPrivateTabs {
verifyNoTabsOpened()
verifyNewTabButton()
verifyTabTrayOverflowMenu(false)
verifyTabTrayOverflowMenu(true)
}
}

@ -9,12 +9,11 @@ import androidx.preference.PreferenceManager
import leakcanary.AppWatcher
import leakcanary.LeakCanary
import org.mozilla.fenix.ext.getPreferenceKey
import org.mozilla.fenix.ext.resetPoliciesAfter
class DebugFenixApplication : FenixApplication() {
override fun setupLeakCanary() {
val isEnabled = StrictMode.allowThreadDiskReads().resetPoliciesAfter {
val isEnabled = components.strictMode.resetAfter(StrictMode.allowThreadDiskReads()) {
PreferenceManager.getDefaultSharedPreferences(this)
.getBoolean(getPreferenceKey(R.string.pref_key_leakcanary), true)
}

@ -40,11 +40,9 @@ import mozilla.components.support.rusthttp.RustHttpConfig
import mozilla.components.support.rustlog.RustLog
import mozilla.components.support.utils.logElapsedTime
import mozilla.components.support.webextensions.WebExtensionSupport
import org.mozilla.fenix.StrictModeManager.enableStrictMode
import org.mozilla.fenix.components.Components
import org.mozilla.fenix.components.metrics.MetricServiceType
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.resetPoliciesAfter
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.perf.StorageStatsMetrics
import org.mozilla.fenix.perf.StartupTimeline
@ -126,11 +124,11 @@ open class FenixApplication : LocaleAwareApplication(), Provider {
val megazordSetup = setupMegazord()
setDayNightTheme()
enableStrictMode(true)
components.strictMode.enableStrictMode(true)
warmBrowsersCache()
// Make sure the engine is initialized and ready to use.
StrictMode.allowThreadDiskReads().resetPoliciesAfter {
components.strictMode.resetAfter(StrictMode.allowThreadDiskReads()) {
components.core.engine.warmUp()
}
initializeWebExtensionSupport()
@ -451,7 +449,7 @@ open class FenixApplication : LocaleAwareApplication(), Provider {
applicationContext.resources.configuration.uiMode = config.uiMode
// random StrictMode onDiskRead violation even when Fenix is not running in the background.
StrictMode.allowThreadDiskReads().resetPoliciesAfter {
components.strictMode.resetAfter(StrictMode.allowThreadDiskReads()) {
super.onConfigurationChanged(config)
}
}

@ -73,7 +73,6 @@ import org.mozilla.fenix.ext.breadcrumb
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.metrics
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.resetPoliciesAfter
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.home.HomeFragmentDirections
import org.mozilla.fenix.home.intent.CrashReporterIntentProcessor
@ -150,9 +149,9 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
private lateinit var navigationToolbar: Toolbar
final override fun onCreate(savedInstanceState: Bundle?) {
StrictModeManager.changeStrictModePolicies(supportFragmentManager)
components.strictMode.attachListenerToDisablePenaltyDeath(supportFragmentManager)
// There is disk read violations on some devices such as samsung and pixel for android 9/10
StrictMode.allowThreadDiskReads().resetPoliciesAfter {
components.strictMode.resetAfter(StrictMode.allowThreadDiskReads()) {
super.onCreate(savedInstanceState)
}
@ -757,7 +756,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
}
override fun attachBaseContext(base: Context) {
StrictMode.allowThreadDiskReads().resetPoliciesAfter {
base.components.strictMode.resetAfter(StrictMode.allowThreadDiskReads()) {
super.attachBaseContext(base)
}
}

@ -15,7 +15,6 @@ import org.mozilla.fenix.components.IntentProcessorType
import org.mozilla.fenix.components.getType
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.resetPoliciesAfter
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.perf.StartupTimeline
import org.mozilla.fenix.shortcut.NewTabShortcutIntentProcessor
@ -28,7 +27,7 @@ class IntentReceiverActivity : Activity() {
@VisibleForTesting
override fun onCreate(savedInstanceState: Bundle?) {
// StrictMode violation on certain devices such as Samsung
StrictMode.allowThreadDiskReads().resetPoliciesAfter {
components.strictMode.resetAfter(StrictMode.allowThreadDiskReads()) {
super.onCreate(savedInstanceState)
}
@ -68,7 +67,7 @@ class IntentReceiverActivity : Activity() {
)
}
// StrictMode violation on certain devices such as Samsung
StrictMode.allowThreadDiskReads().resetPoliciesAfter {
components.strictMode.resetAfter(StrictMode.allowThreadDiskReads()) {
startActivity(intent)
}
finish() // must finish() after starting the other activity

@ -8,32 +8,31 @@ import android.os.Build
import android.os.StrictMode
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import mozilla.components.support.ktx.android.os.resetAfter
private const val MANUFACTURE_HUAWEI: String = "HUAWEI"
private const val MANUFACTURE_ONE_PLUS: String = "OnePlus"
/**
* Manages strict mode settings for the application.
*/
object StrictModeManager {
class StrictModeManager(config: Config) {
// This is public so it can be used by inline functions.
val isEnabledByBuildConfig = config.channel.isDebug
/***
* Enables strict mode for debug purposes. meant to be run only in the main process.
* @param setPenaltyDeath boolean value to decide setting the penaltyDeath as a penalty.
* @param setPenaltyDialog boolean value to decide setting the dialog box as a penalty.
* Note: dialog penalty cannot be set with penaltyDeath
*/
fun enableStrictMode(setPenaltyDeath: Boolean, setPenaltyDialog: Boolean = false) {
if (Config.channel.isDebug) {
fun enableStrictMode(setPenaltyDeath: Boolean) {
if (isEnabledByBuildConfig) {
val threadPolicy = StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
if (setPenaltyDeath && Build.MANUFACTURER !in strictModeExceptionList) {
threadPolicy.penaltyDeath()
}
// dialog penalty cannot be set with penaltyDeath
if (!setPenaltyDeath && setPenaltyDialog) {
threadPolicy.penaltyDialog()
}
StrictMode.setThreadPolicy(threadPolicy.build())
val builder = StrictMode.VmPolicy.Builder()
@ -47,33 +46,48 @@ object StrictModeManager {
builder.detectContentUriWithoutPermission()
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
if (setPenaltyDeath || setPenaltyDialog) {
builder.permitNonSdkApiUsage()
} else {
builder.detectNonSdkApiUsage()
}
builder.detectNonSdkApiUsage()
}
StrictMode.setVmPolicy(builder.build())
}
}
/**
* Revert strict mode to disable penalty. Tied to fragment lifecycle since strict mode
* Revert strict mode to disable penalty based on fragment lifecycle since strict mode
* needs to switch to penalty logs. Using the fragment life cycle allows decoupling from any
* specific fragment.
*/
fun changeStrictModePolicies(fragmentManager: FragmentManager) {
fun attachListenerToDisablePenaltyDeath(fragmentManager: FragmentManager) {
fragmentManager.registerFragmentLifecycleCallbacks(object :
FragmentManager.FragmentLifecycleCallbacks() {
override fun onFragmentResumed(fm: FragmentManager, f: Fragment) {
enableStrictMode(setPenaltyDeath = false, setPenaltyDialog = false)
enableStrictMode(setPenaltyDeath = false)
fm.unregisterFragmentLifecycleCallbacks(this)
}
}, false)
}
private const val MANUFACTURE_HUAWEI: String = "HUAWEI"
private const val MANUFACTURE_ONE_PLUS: String = "OnePlus"
/**
* Runs the given [functionBlock] and sets the given [StrictMode.ThreadPolicy] after its
* completion when in a build configuration that has StrictMode enabled. If StrictMode is
* not enabled, simply runs the [functionBlock].
*
* This function is written in the style of [AutoCloseable.use].
*
* This is significantly less convenient to run than when it was written as an extension function
* on [StrictMode.ThreadPolicy] but I think this is okay: it shouldn't be easy to ignore StrictMode.
*
* @return the value returned by [functionBlock].
*/
inline fun <R> resetAfter(policy: StrictMode.ThreadPolicy, functionBlock: () -> R): R {
// Calling resetAfter takes 1-2ms (unknown device) so we only execute it if StrictMode can
// actually be enabled. https://github.com/mozilla-mobile/fenix/issues/11617
return if (isEnabledByBuildConfig) {
policy.resetAfter(functionBlock)
} else {
functionBlock()
}
}
/**
* There are certain manufacturers that have custom font classes for the OS systems.

@ -261,9 +261,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
)
},
onCloseTab = { closedSession ->
val tab = store.state.findTab(closedSession.id)
?: return@DefaultBrowserToolbarController
val isSelected = tab.id == context.components.core.store.state.selectedTabId
val tab = store.state.findTab(closedSession.id) ?: return@DefaultBrowserToolbarController
val snackbarMessage = if (tab.content.private) {
requireContext().getString(R.string.snackbar_private_tab_closed)
@ -276,12 +274,9 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
snackbarMessage,
requireContext().getString(R.string.snackbar_deleted_undo),
{
sessionManager.add(
closedSession,
isSelected,
engineSessionState = tab.engineState.engineSessionState
)
requireComponents.useCases.tabsUseCases.undo.invoke()
},
paddedForBottomToolbar = true,
operation = { }
)
}

@ -39,7 +39,6 @@ import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.navigateSafe
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.resetPoliciesAfter
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.shortcut.PwaOnboardingObserver
import org.mozilla.fenix.trackingprotection.TrackingProtectionOverlay
@ -110,7 +109,7 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
)
readerViewFeature.set(
feature = StrictMode.allowThreadDiskReads().resetPoliciesAfter {
feature = components.strictMode.resetAfter(StrictMode.allowThreadDiskReads()) {
ReaderViewFeature(
context,
components.core.engine,

@ -90,27 +90,27 @@ class ToolbarGestureHandler(
when (getDestination()) {
is Destination.Tab -> {
// Restrict the range of motion for the views so you can't start a swipe in one direction
// then move your finger far enough in the other direction and make the content visually
// start sliding off screen the other way.
// then move your finger far enough or in the other direction and make the content visually
// start sliding off screen.
tabPreview.translationX = when (gestureDirection) {
GestureDirection.RIGHT_TO_LEFT -> min(
windowWidth.toFloat() + previewOffset,
tabPreview.translationX - distanceX
)
).coerceAtLeast(0f)
GestureDirection.LEFT_TO_RIGHT -> max(
-windowWidth.toFloat() - previewOffset,
tabPreview.translationX - distanceX
)
).coerceAtMost(0f)
}
contentLayout.translationX = when (gestureDirection) {
GestureDirection.RIGHT_TO_LEFT -> min(
0f,
contentLayout.translationX - distanceX
)
).coerceAtLeast(-windowWidth.toFloat() - previewOffset)
GestureDirection.LEFT_TO_RIGHT -> max(
0f,
contentLayout.translationX - distanceX
)
).coerceAtMost(windowWidth.toFloat() + previewOffset)
}
}
is Destination.None -> {

@ -9,23 +9,15 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.VisibleForTesting
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.navArgs
import kotlinx.android.synthetic.main.fragment_create_collection.view.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
import mozilla.components.browser.state.selector.findTab
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.lib.publicsuffixlist.PublicSuffixList
import mozilla.components.lib.state.ext.consumeFrom
import org.mozilla.fenix.R
import org.mozilla.fenix.components.StoreProvider
import org.mozilla.fenix.ext.getMediaStateForSession
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.toShortUrl
import org.mozilla.fenix.home.Tab
@ExperimentalCoroutinesApi
class CollectionCreationFragment : DialogFragment() {
@ -47,27 +39,16 @@ class CollectionCreationFragment : DialogFragment() {
val view = inflater.inflate(R.layout.fragment_create_collection, container, false)
val args: CollectionCreationFragmentArgs by navArgs()
val store = requireComponents.core.store
val publicSuffixList = requireComponents.publicSuffixList
val tabs = store.state.getTabs(args.tabIds, publicSuffixList)
val selectedTabs = if (args.selectedTabIds != null) {
store.state.getTabs(args.selectedTabIds, publicSuffixList).toSet()
} else {
if (tabs.size == 1) setOf(tabs.first()) else emptySet()
}
val tabCollections = requireComponents.core.tabCollectionStorage.cachedTabCollections
val selectedTabCollection = args.selectedTabCollectionId
.let { id -> tabCollections.firstOrNull { it.id == id } }
collectionCreationStore = StoreProvider.get(this) {
CollectionCreationStore(
CollectionCreationState(
tabs = tabs,
selectedTabs = selectedTabs,
createInitialCollectionCreationState(
browserState = requireComponents.core.store.state,
tabCollectionStorage = requireComponents.core.tabCollectionStorage,
publicSuffixList = requireComponents.publicSuffixList,
saveCollectionStep = args.saveCollectionStep,
tabCollections = tabCollections,
selectedTabCollection = selectedTabCollection
tabIds = args.tabIds,
selectedTabIds = args.selectedTabIds,
selectedTabCollectionId = args.selectedTabCollectionId
)
)
}
@ -110,31 +91,3 @@ class CollectionCreationFragment : DialogFragment() {
return dialog
}
}
@VisibleForTesting
internal fun BrowserState.getTabs(
tabIds: Array<String>?,
publicSuffixList: PublicSuffixList
): List<Tab> {
return tabIds
?.mapNotNull { id -> findTab(id) }
?.map { it.toTab(this, publicSuffixList) }
.orEmpty()
}
private fun TabSessionState.toTab(
state: BrowserState,
publicSuffixList: PublicSuffixList,
selected: Boolean? = null
): Tab {
val url = readerState.activeUrl ?: content.url
return Tab(
sessionId = this.id,
url = url,
hostname = url.toShortUrl(publicSuffixList),
title = content.title,
selected = selected,
icon = content.icon,
mediaState = state.getMediaStateForSession(this.id)
)
}

@ -4,11 +4,19 @@
package org.mozilla.fenix.collections
import androidx.annotation.VisibleForTesting
import mozilla.components.browser.state.selector.findTab
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.feature.tab.collections.TabCollection
import mozilla.components.lib.publicsuffixlist.PublicSuffixList
import mozilla.components.lib.state.Action
import mozilla.components.lib.state.State
import mozilla.components.lib.state.Store
import org.mozilla.fenix.collections.CollectionCreationAction.StepChanged
import org.mozilla.fenix.components.TabCollectionStorage
import org.mozilla.fenix.ext.getMediaStateForSession
import org.mozilla.fenix.ext.toShortUrl
import org.mozilla.fenix.home.Tab
class CollectionCreationStore(
@ -43,6 +51,62 @@ data class CollectionCreationState(
val defaultCollectionNumber: Int = 1
) : State
@Suppress("LongParameterList")
fun createInitialCollectionCreationState(
browserState: BrowserState,
tabCollectionStorage: TabCollectionStorage,
publicSuffixList: PublicSuffixList,
saveCollectionStep: SaveCollectionStep,
tabIds: Array<String>?,
selectedTabIds: Array<String>?,
selectedTabCollectionId: Long
): CollectionCreationState {
val tabs = browserState.getTabs(tabIds, publicSuffixList)
val selectedTabs = if (selectedTabIds != null) {
browserState.getTabs(selectedTabIds, publicSuffixList).toSet()
} else {
if (tabs.size == 1) setOf(tabs.first()) else emptySet()
}
val tabCollections = tabCollectionStorage.cachedTabCollections
val selectedTabCollection = tabCollections.firstOrNull { it.id == selectedTabCollectionId }
return CollectionCreationState(
tabs = tabs,
selectedTabs = selectedTabs,
saveCollectionStep = saveCollectionStep,
tabCollections = tabCollections,
selectedTabCollection = selectedTabCollection
)
}
@VisibleForTesting
internal fun BrowserState.getTabs(
tabIds: Array<String>?,
publicSuffixList: PublicSuffixList
): List<Tab> {
return tabIds
?.mapNotNull { id -> findTab(id) }
?.map { it.toTab(this, publicSuffixList) }
.orEmpty()
}
private fun TabSessionState.toTab(
state: BrowserState,
publicSuffixList: PublicSuffixList
): Tab {
val url = readerState.activeUrl ?: content.url
return Tab(
sessionId = this.id,
url = url,
hostname = url.toShortUrl(publicSuffixList),
title = content.title,
selected = null,
icon = content.icon,
mediaState = state.getMediaStateForSession(this.id)
)
}
sealed class CollectionCreationAction : Action {
object AddAllTabs : CollectionCreationAction()
object RemoveAllTabs : CollectionCreationAction()

@ -16,7 +16,7 @@ import mozilla.components.concept.sync.OAuthAccount
import mozilla.components.lib.crash.CrashReporter
import mozilla.components.service.fxa.manager.FxaAccountManager
import mozilla.components.support.base.log.logger.Logger
import org.mozilla.fenix.ext.resetPoliciesAfter
import org.mozilla.fenix.StrictModeManager
import kotlin.coroutines.CoroutineContext
/**
@ -57,6 +57,7 @@ internal abstract class AbnormalFxaEvent : Exception() {
class AccountAbnormalities(
context: Context,
private val crashReporter: CrashReporter,
strictMode: StrictModeManager,
private val coroutineContext: CoroutineContext = Dispatchers.IO
) : AccountObserver {
companion object {
@ -79,14 +80,14 @@ class AccountAbnormalities(
private val hadAccountPrior: Boolean
init {
val prefPair = StrictMode.allowThreadDiskReads().resetPoliciesAfter {
val prefPair = strictMode.resetAfter(StrictMode.allowThreadDiskReads()) {
val p = context.getSharedPreferences(PREF_FXA_ABNORMALITIES, Context.MODE_PRIVATE)
val a = p.getBoolean(KEY_HAS_ACCOUNT, false)
Pair(p, a)
}
prefs = prefPair.first
hadAccountPrior = prefPair.second
}
}
/**
* Once [accountManager] is initialized, queries it to detect abnormal account states.

@ -18,6 +18,7 @@ import org.mozilla.fenix.Config
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.ReleaseChannel
import org.mozilla.fenix.StrictModeManager
import org.mozilla.fenix.components.metrics.AdjustMetricsService
import org.mozilla.fenix.components.metrics.GleanMetricsService
import org.mozilla.fenix.components.metrics.LeanplumMetricsService
@ -34,7 +35,8 @@ import org.mozilla.geckoview.BuildConfig.MOZ_UPDATE_CHANNEL
*/
@Mockable
class Analytics(
private val context: Context
private val context: Context,
strictMode: StrictModeManager
) {
val crashReporter: CrashReporter by lazy {
val services = mutableListOf<CrashReporterService>()
@ -84,7 +86,10 @@ class Analytics(
)
}
val leanplumMetricsService by lazy { LeanplumMetricsService(context as Application) }
val leanplumMetricsService by lazy { LeanplumMetricsService(
context as Application,
strictMode
) }
val metrics: MetricController by lazy {
MetricController.create(

@ -36,6 +36,7 @@ import mozilla.components.service.sync.logins.SyncableLoginsStorage
import mozilla.components.support.utils.RunWhenReadyQueue
import org.mozilla.fenix.Config
import org.mozilla.fenix.R
import org.mozilla.fenix.StrictModeManager
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.ext.components
@ -57,7 +58,8 @@ class BackgroundServices(
historyStorage: Lazy<PlacesHistoryStorage>,
bookmarkStorage: Lazy<PlacesBookmarksStorage>,
passwordsStorage: Lazy<SyncableLoginsStorage>,
remoteTabsStorage: Lazy<RemoteTabsStorage>
remoteTabsStorage: Lazy<RemoteTabsStorage>,
strictMode: StrictModeManager
) {
// Allows executing tasks which depend on the account manager, but do not need to eagerly initialize it.
val accountManagerAvailableQueue = RunWhenReadyQueue()
@ -105,7 +107,7 @@ class BackgroundServices(
context.components.analytics.metrics
)
val accountAbnormalities = AccountAbnormalities(context, crashReporter)
val accountAbnormalities = AccountAbnormalities(context, crashReporter, strictMode)
val accountManager by lazy { makeAccountManager(context, serverConfig, deviceConfig, syncConfig) }

@ -16,7 +16,9 @@ import mozilla.components.feature.addons.update.DefaultAddonUpdater
import mozilla.components.lib.publicsuffixlist.PublicSuffixList
import mozilla.components.support.migration.state.MigrationStore
import io.github.forkmaintainers.iceraven.components.PagedAddonCollectionProvider
import org.mozilla.fenix.Config
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.StrictModeManager
import org.mozilla.fenix.components.metrics.AppStartupTelemetry
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.utils.ClipboardHandler
@ -40,11 +42,12 @@ class Components(private val context: Context) {
core.lazyHistoryStorage,
core.lazyBookmarksStorage,
core.lazyPasswordsStorage,
core.lazyRemoteTabsStorage
core.lazyRemoteTabsStorage,
strictMode
)
}
val services by lazy { Services(context, backgroundServices.accountManager) }
val core by lazy { Core(context, analytics.crashReporter) }
val core by lazy { Core(context, analytics.crashReporter, strictMode) }
val search by lazy { Search(context) }
val useCases by lazy {
UseCases(
@ -113,13 +116,14 @@ class Components(private val context: Context) {
addonCollectionProvider.setCollectionName(addonsCollection)
}
val analytics by lazy { Analytics(context) }
val analytics by lazy { Analytics(context, strictMode) }
val publicSuffixList by lazy { PublicSuffixList(context) }
val clipboardHandler by lazy { ClipboardHandler(context) }
val migrationStore by lazy { MigrationStore() }
val performance by lazy { PerformanceComponent() }
val push by lazy { Push(context, analytics.crashReporter) }
val wifiConnectionMonitor by lazy { WifiConnectionMonitor(context as Application) }
val strictMode by lazy { StrictModeManager(Config) }
val settings by lazy { Settings(context) }

@ -20,6 +20,7 @@ import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager
import mozilla.components.browser.session.engine.EngineMiddleware
import mozilla.components.browser.session.storage.SessionStorage
import mozilla.components.browser.session.undo.UndoMiddleware
import mozilla.components.browser.state.action.RecentlyClosedAction
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.store.BrowserStore
@ -59,9 +60,9 @@ import org.mozilla.fenix.AppRequestInterceptor
import org.mozilla.fenix.Config
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.StrictModeManager
import org.mozilla.fenix.downloads.DownloadService
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.resetPoliciesAfter
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.media.MediaService
import org.mozilla.fenix.search.telemetry.ads.AdsTelemetry
@ -69,13 +70,18 @@ import org.mozilla.fenix.search.telemetry.incontent.InContentTelemetry
import org.mozilla.fenix.settings.SupportUtils
import org.mozilla.fenix.settings.advanced.getSelectedLocale
import org.mozilla.fenix.utils.Mockable
import org.mozilla.fenix.utils.getUndoDelay
import java.util.concurrent.TimeUnit
/**
* Component group for all core browser functionality.
*/
@Mockable
class Core(private val context: Context, private val crashReporter: CrashReporting) {
class Core(
private val context: Context,
private val crashReporter: CrashReporting,
strictMode: StrictModeManager
) {
/**
* The browser engine component initialized based on the build
* configuration (see build variants).
@ -146,13 +152,18 @@ class Core(private val context: Context, private val crashReporter: CrashReporti
MediaMiddleware(context, MediaService::class.java),
DownloadMiddleware(context, DownloadService::class.java),
ReaderViewMiddleware(),
ThumbnailsMiddleware(thumbnailStorage)
ThumbnailsMiddleware(thumbnailStorage),
UndoMiddleware(::lookupSessionManager, context.getUndoDelay())
) + EngineMiddleware.create(engine, ::findSessionById)
).also {
it.dispatch(RecentlyClosedAction.InitializeRecentlyClosedState)
}
}
private fun lookupSessionManager(): SessionManager {
return sessionManager
}
private fun findSessionById(tabId: String): Session? {
return sessionManager.findSessionById(tabId)
}
@ -261,7 +272,11 @@ class Core(private val context: Context, private val crashReporter: CrashReporti
val bookmarksStorage by lazy { lazyBookmarksStorage.value }
val passwordsStorage by lazy { lazyPasswordsStorage.value }
val tabCollectionStorage by lazy { TabCollectionStorage(context, sessionManager) }
val tabCollectionStorage by lazy { TabCollectionStorage(
context,
sessionManager,
strictMode
) }
/**
* A storage component for persisting thumbnail images of tabs.
@ -273,7 +288,7 @@ class Core(private val context: Context, private val crashReporter: CrashReporti
val topSitesStorage by lazy {
val defaultTopSites = mutableListOf<Pair<String, String>>()
StrictMode.allowThreadDiskReads().resetPoliciesAfter {
strictMode.resetAfter(StrictMode.allowThreadDiskReads()) {
if (!context.settings().defaultTopSitesAdded) {
defaultTopSites.add(
Pair(
@ -362,6 +377,6 @@ class Core(private val context: Context, private val crashReporter: CrashReporti
private const val KEY_STRENGTH = 256
private const val KEY_STORAGE_NAME = "core_prefs"
private const val PASSWORDS_KEY = "passwords"
private const val RECENTLY_CLOSED_MAX = 5
private const val RECENTLY_CLOSED_MAX = 10
}
}

@ -19,8 +19,8 @@ import mozilla.components.feature.tab.collections.TabCollection
import mozilla.components.feature.tab.collections.TabCollectionStorage
import mozilla.components.support.base.observer.Observable
import mozilla.components.support.base.observer.ObserverRegistry
import org.mozilla.fenix.StrictModeManager
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.resetPoliciesAfter
import org.mozilla.fenix.ext.toShortUrl
import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionViewHolder
import org.mozilla.fenix.utils.Mockable
@ -29,6 +29,7 @@ import org.mozilla.fenix.utils.Mockable
class TabCollectionStorage(
private val context: Context,
private val sessionManager: SessionManager,
strictMode: StrictModeManager,
private val delegate: Observable<Observer> = ObserverRegistry()
) : Observable<org.mozilla.fenix.components.TabCollectionStorage.Observer> by delegate {
@ -56,7 +57,7 @@ class TabCollectionStorage(
var cachedTabCollections = listOf<TabCollection>()
private val collectionStorage by lazy {
StrictMode.allowThreadDiskReads().resetPoliciesAfter {
strictMode.resetAfter(StrictMode.allowThreadDiskReads()) {
TabCollectionStorage(context, sessionManager)
}
}

@ -22,8 +22,8 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import mozilla.components.support.locale.LocaleManager
import org.mozilla.fenix.BuildConfig
import org.mozilla.fenix.StrictModeManager
import org.mozilla.fenix.components.metrics.MozillaProductDetector.MozillaProducts
import org.mozilla.fenix.ext.resetPoliciesAfter
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.home.intent.DeepLinkIntentProcessor
import java.util.Locale
@ -58,6 +58,7 @@ private val Event.name: String?
class LeanplumMetricsService(
private val application: Application,
strictMode: StrictModeManager,
private val deviceIdGenerator: () -> String = { randomUUID().toString() }
) : MetricsService, DeepLinkIntentProcessor.DeepLinkVerifier {
val scope = CoroutineScope(Dispatchers.IO)
@ -83,7 +84,7 @@ class LeanplumMetricsService(
override val type = MetricServiceType.Marketing
private val token = Token(LeanplumId, LeanplumToken)
private val preferences = StrictMode.allowThreadDiskReads().resetPoliciesAfter {
private val preferences = strictMode.resetAfter(StrictMode.allowThreadDiskReads()) {
application.getSharedPreferences(PREFERENCE_NAME, MODE_PRIVATE)
}

@ -1,26 +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.ext
import android.os.StrictMode
import org.mozilla.fenix.Config
/**
* Runs the given [functionBlock] and sets the ThreadPolicy after its completion in Debug mode.
* Otherwise simply runs the [functionBlock]
* This function is written in the style of [AutoCloseable.use].
* @return the value returned by [functionBlock].
*/
inline fun <R> StrictMode.ThreadPolicy.resetPoliciesAfter(functionBlock: () -> R): R {
return if (Config.channel.isDebug) {
try {
functionBlock()
} finally {
StrictMode.setThreadPolicy(this)
}
} else {
functionBlock()
}
}

@ -91,8 +91,6 @@ import org.mozilla.fenix.ext.hideToolbar
import org.mozilla.fenix.ext.metrics
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.resetPoliciesAfter
import org.mozilla.fenix.ext.sessionsOfType
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.home.sessioncontrol.DefaultSessionControlController
import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor
@ -149,7 +147,7 @@ class HomeFragment : Fragment() {
get() = requireComponents.core.store
private val onboarding by lazy {
StrictMode.allowThreadDiskReads().resetPoliciesAfter {
requireComponents.strictMode.resetAfter(StrictMode.allowThreadDiskReads()) {
FenixOnboarding(requireContext())
}
}
@ -199,7 +197,7 @@ class HomeFragment : Fragment() {
expandedCollections = emptySet(),
mode = currentMode.getCurrentMode(),
topSites = components.core.topSitesStorage.cachedTopSites,
tip = StrictMode.allowThreadDiskReads().resetPoliciesAfter {
tip = components.strictMode.resetAfter(StrictMode.allowThreadDiskReads()) {
FenixTipManager(
listOf(
MasterPasswordTipProvider(
@ -469,17 +467,10 @@ class HomeFragment : Fragment() {
}
private fun removeAllTabsAndShowSnackbar(sessionCode: String) {
val tabs = sessionManager.sessionsOfType(private = sessionCode == ALL_PRIVATE_TABS).toList()
val selectedIndex = sessionManager
.selectedSession?.let { sessionManager.sessions.indexOf(it) }
?: SessionManager.NO_SELECTION
val snapshot = tabs
.map(sessionManager::createSessionSnapshot)
.let { SessionManager.Snapshot(it, selectedIndex) }
tabs.forEach {
requireComponents.useCases.tabsUseCases.removeTab(it)
if (sessionCode == ALL_PRIVATE_TABS) {
sessionManager.removePrivateSessions()
} else {
sessionManager.removeNormalSessions()
}
val snackbarMessage = if (sessionCode == ALL_PRIVATE_TABS) {
@ -493,7 +484,7 @@ class HomeFragment : Fragment() {
snackbarMessage,
requireContext().getString(R.string.snackbar_deleted_undo),
{
sessionManager.restore(snapshot)
requireComponents.useCases.tabsUseCases.undo.invoke()
},
operation = { },
anchorView = snackbarAnchorView
@ -501,38 +492,29 @@ class HomeFragment : Fragment() {
}
private fun removeTabAndShowSnackbar(sessionId: String) {
sessionManager.findSessionById(sessionId)?.let { session ->
val snapshot = sessionManager.createSessionSnapshot(session)
val state = store.state.findTab(sessionId)?.engineState?.engineSessionState
val isSelected =
session.id == requireComponents.core.store.state.selectedTabId ?: false
val tab = store.state.findTab(sessionId) ?: return
requireComponents.useCases.tabsUseCases.removeTab(sessionId)
val snackbarMessage = if (snapshot.session.private) {
requireContext().getString(R.string.snackbar_private_tab_closed)
} else {
requireContext().getString(R.string.snackbar_tab_closed)
}
requireComponents.useCases.tabsUseCases.removeTab(sessionId)
viewLifecycleOwner.lifecycleScope.allowUndo(
requireView(),
snackbarMessage,
requireContext().getString(R.string.snackbar_deleted_undo),
{
sessionManager.add(
snapshot.session,
isSelected,
engineSessionState = state
)
findNavController().navigate(
HomeFragmentDirections.actionGlobalBrowser(null)
)
},
operation = { },
anchorView = snackbarAnchorView
)
val snackbarMessage = if (tab.content.private) {
requireContext().getString(R.string.snackbar_private_tab_closed)
} else {
requireContext().getString(R.string.snackbar_tab_closed)
}
viewLifecycleOwner.lifecycleScope.allowUndo(
requireView(),
snackbarMessage,
requireContext().getString(R.string.snackbar_deleted_undo),
{
requireComponents.useCases.tabsUseCases.undo.invoke()
findNavController().navigate(
HomeFragmentDirections.actionGlobalBrowser(null)
)
},
operation = { },
anchorView = snackbarAnchorView
)
}
override fun onDestroyView() {
@ -555,7 +537,7 @@ class HomeFragment : Fragment() {
collections = components.core.tabCollectionStorage.cachedTabCollections,
mode = currentMode.getCurrentMode(),
topSites = components.core.topSitesStorage.cachedTopSites,
tip = StrictMode.allowThreadDiskReads().resetPoliciesAfter {
tip = components.strictMode.resetAfter(StrictMode.allowThreadDiskReads()) {
FenixTipManager(
listOf(
MasterPasswordTipProvider(

@ -6,13 +6,16 @@ package org.mozilla.fenix.settings
import android.annotation.SuppressLint
import android.content.ActivityNotFoundException
import android.content.DialogInterface
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.provider.Settings
import android.view.LayoutInflater
import android.widget.Toast
import androidx.annotation.VisibleForTesting
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavDirections
import androidx.navigation.findNavController
@ -20,6 +23,8 @@ import androidx.navigation.fragment.navArgs
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.amo_collection_override_dialog.view.*
import kotlinx.android.synthetic.main.fragment_installed_add_on_details.view.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@ -28,6 +33,7 @@ import mozilla.components.concept.sync.AuthType
import mozilla.components.concept.sync.OAuthAccount
import mozilla.components.concept.sync.Profile
import mozilla.components.support.ktx.android.content.getColorFromAttr
import mozilla.components.support.ktx.android.view.showKeyboard
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.Config
import org.mozilla.fenix.FeatureFlags
@ -43,6 +49,7 @@ import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.ext.showToolbar
import org.mozilla.fenix.settings.account.AccountUiView
import org.mozilla.fenix.utils.Settings
import kotlin.system.exitProcess
@Suppress("LargeClass", "TooManyFunctions")
@ -304,6 +311,41 @@ class SettingsFragment : PreferenceFragmentCompat() {
resources.getString(R.string.pref_key_debug_settings) -> {
SettingsFragmentDirections.actionSettingsFragmentToSecretSettingsFragment()
}
resources.getString(R.string.pref_key_override_amo_collection) -> {
val context = requireContext()
val dialogView = LayoutInflater.from(context).inflate(R.layout.amo_collection_override_dialog, null)
AlertDialog.Builder(context).apply {
setTitle(context.getString(R.string.preferences_customize_amo_collection))
setView(dialogView)
setNegativeButton(R.string.customize_addon_collection_cancel) { dialog: DialogInterface, _ ->
dialog.cancel()
}
setPositiveButton(R.string.customize_addon_collection_ok) { _, _ ->
context.settings().overrideAmoUser = dialogView.custom_amo_user.text.toString()
context.settings().overrideAmoCollection = dialogView.custom_amo_collection.text.toString()
Toast.makeText(
context,
getString(R.string.toast_customize_addon_collection_done),
Toast.LENGTH_LONG
).show()
Handler().postDelayed({
exitProcess(0)
}, AMO_COLLECTION_OVERRIDE_EXIT_DELAY)
}
dialogView.custom_amo_collection.setText(context.settings().overrideAmoCollection)
dialogView.custom_amo_user.setText(context.settings().overrideAmoUser)
dialogView.custom_amo_user.requestFocus()
dialogView.custom_amo_user.showKeyboard()
create()
}.show()
null
}
else -> null
}
directions?.let { navigateFromSettings(directions) }
@ -374,12 +416,14 @@ class SettingsFragment : PreferenceFragmentCompat() {
findPreference<Preference>(
getPreferenceKey(R.string.pref_key_debug_settings)
)?.isVisible = requireContext().settings().showSecretDebugMenuThisSession
setupAmoCollectionOverridePreference(requireContext().settings())
}
private fun getClickListenerForMakeDefaultBrowser(): Preference.OnPreferenceClickListener {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Preference.OnPreferenceClickListener {
val intent = Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS)
val intent = Intent(android.provider.Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS)
startActivity(intent)
true
}
@ -446,8 +490,23 @@ class SettingsFragment : PreferenceFragmentCompat() {
}
}
@VisibleForTesting
internal fun setupAmoCollectionOverridePreference(settings: Settings) {
val preferenceAmoCollectionOverride =
findPreference<Preference>(getPreferenceKey(R.string.pref_key_override_amo_collection))
val show = (Config.channel.isNightlyOrDebug && (
settings.amoCollectionOverrideConfigured() || settings.showSecretDebugMenuThisSession)
)
preferenceAmoCollectionOverride?.apply {
isVisible = show
summary = settings.overrideAmoCollection.ifEmpty { null }
}
}
companion object {
private const val SCROLL_INDICATOR_DELAY = 10L
private const val FXA_SYNC_OVERRIDE_EXIT_DELAY = 2000L
private const val AMO_COLLECTION_OVERRIDE_EXIT_DELAY = 3000L
}
}

@ -17,7 +17,6 @@ import kotlinx.android.synthetic.main.fragment_locale_settings.view.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
import mozilla.components.lib.state.ext.consumeFrom
import mozilla.components.support.ktx.android.view.hideKeyboard
import mozilla.components.support.locale.LocaleManager
import org.mozilla.fenix.R
import org.mozilla.fenix.components.StoreProvider
import org.mozilla.fenix.ext.showToolbar
@ -40,7 +39,11 @@ class LocaleSettingsFragment : Fragment() {
): View? {
val view = inflater.inflate(R.layout.fragment_locale_settings, container, false)
store = getStore()
store = StoreProvider.get(this) {
LocaleSettingsStore(
createInitialLocaleSettingsState(requireContext())
)
}
interactor = LocaleSettingsInteractor(
controller = DefaultLocaleSettingsController(
activity = requireActivity(),
@ -88,19 +91,4 @@ class LocaleSettingsFragment : Fragment() {
localeView.update(it)
}
}
private fun getStore(): LocaleSettingsStore {
val supportedLocales = LocaleManager.getSupportedLocales()
val selectedLocale = LocaleManager.getSelectedLocale(requireContext())
return StoreProvider.get(this) {
LocaleSettingsStore(
LocaleSettingsState(
supportedLocales,
supportedLocales,
selectedLocale
)
)
}
}
}

@ -4,9 +4,11 @@
package org.mozilla.fenix.settings.advanced
import android.content.Context
import mozilla.components.lib.state.Action
import mozilla.components.lib.state.State
import mozilla.components.lib.state.Store
import mozilla.components.support.locale.LocaleManager
import java.util.Locale
class LocaleSettingsStore(
@ -27,6 +29,16 @@ data class LocaleSettingsState(
val selectedLocale: Locale
) : State
fun createInitialLocaleSettingsState(context: Context): LocaleSettingsState {
val supportedLocales = LocaleManager.getSupportedLocales()
return LocaleSettingsState(
supportedLocales,
supportedLocales,
selectedLocale = LocaleManager.getSelectedLocale(context)
)
}
/**
* Actions to dispatch through the `LocaleSettingsStore` to modify `LocaleSettingsState` through the reducer.
*/

@ -10,6 +10,7 @@ import mozilla.components.concept.storage.Login
import mozilla.components.lib.state.Action
import mozilla.components.lib.state.State
import mozilla.components.lib.state.Store
import org.mozilla.fenix.utils.Settings
/**
* Class representing a parcelable saved logins item
@ -80,6 +81,16 @@ data class LoginsListState(
val duplicateLogins: List<SavedLogin>
) : State
fun createInitialLoginsListState(settings: Settings) = LoginsListState(
isLoading = true,
loginList = emptyList(),
filteredItems = emptyList(),
searchedForText = null,
sortingStrategy = settings.savedLoginsSortingStrategy,
highlightedItem = settings.savedLoginsMenuHighlightedItem,
duplicateLogins = emptyList() // assume on load there are no dupes
)
/**
* Handles changes in the saved logins list, including updates and filtering.
*/

@ -33,11 +33,11 @@ import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.settings.logins.LoginsAction
import org.mozilla.fenix.settings.logins.LoginsFragmentStore
import org.mozilla.fenix.settings.logins.LoginsListState
import org.mozilla.fenix.settings.logins.SavedLogin
import org.mozilla.fenix.settings.logins.togglePasswordReveal
import org.mozilla.fenix.settings.logins.controller.SavedLoginsStorageController
import org.mozilla.fenix.settings.logins.createInitialLoginsListState
import org.mozilla.fenix.settings.logins.interactor.EditLoginInteractor
import org.mozilla.fenix.settings.logins.togglePasswordReveal
/**
* Displays the editable saved login information for a single website
@ -69,15 +69,7 @@ class EditLoginFragment : Fragment(R.layout.fragment_edit_login) {
loginsFragmentStore = StoreProvider.get(this) {
LoginsFragmentStore(
LoginsListState(
isLoading = true,
loginList = listOf(),
filteredItems = listOf(),
searchedForText = null,
sortingStrategy = requireContext().settings().savedLoginsSortingStrategy,
highlightedItem = requireContext().settings().savedLoginsMenuHighlightedItem,
duplicateLogins = listOf()
)
createInitialLoginsListState(requireContext().settings())
)
}

@ -38,9 +38,9 @@ import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.ext.showToolbar
import org.mozilla.fenix.ext.simplifiedUrl
import org.mozilla.fenix.settings.logins.LoginsFragmentStore
import org.mozilla.fenix.settings.logins.LoginsListState
import org.mozilla.fenix.settings.logins.SavedLogin
import org.mozilla.fenix.settings.logins.controller.SavedLoginsStorageController
import org.mozilla.fenix.settings.logins.createInitialLoginsListState
import org.mozilla.fenix.settings.logins.interactor.LoginDetailInteractor
import org.mozilla.fenix.settings.logins.togglePasswordReveal
import org.mozilla.fenix.settings.logins.view.LoginDetailView
@ -68,15 +68,7 @@ class LoginDetailFragment : Fragment(R.layout.fragment_login_detail) {
val view = inflater.inflate(R.layout.fragment_login_detail, container, false)
savedLoginsStore = StoreProvider.get(this) {
LoginsFragmentStore(
LoginsListState(
isLoading = true,
loginList = listOf(),
filteredItems = listOf(),
searchedForText = null,
sortingStrategy = requireContext().settings().savedLoginsSortingStrategy,
highlightedItem = requireContext().settings().savedLoginsMenuHighlightedItem,
duplicateLogins = listOf() // assume on load there are no dupes
)
createInitialLoginsListState(requireContext().settings())
)
}
loginDetailView = LoginDetailView(

@ -35,11 +35,11 @@ import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.ext.showToolbar
import org.mozilla.fenix.settings.logins.LoginsAction
import org.mozilla.fenix.settings.logins.LoginsFragmentStore
import org.mozilla.fenix.settings.logins.LoginsListState
import org.mozilla.fenix.settings.logins.SavedLoginsSortingStrategyMenu
import org.mozilla.fenix.settings.logins.SortingStrategy
import org.mozilla.fenix.settings.logins.controller.LoginsListController
import org.mozilla.fenix.settings.logins.controller.SavedLoginsStorageController
import org.mozilla.fenix.settings.logins.createInitialLoginsListState
import org.mozilla.fenix.settings.logins.interactor.SavedLoginsInteractor
import org.mozilla.fenix.settings.logins.view.SavedLoginsListView
@ -77,15 +77,7 @@ class SavedLoginsFragment : Fragment() {
val view = inflater.inflate(R.layout.fragment_saved_logins, container, false)
savedLoginsStore = StoreProvider.get(this) {
LoginsFragmentStore(
LoginsListState(
isLoading = true,
loginList = listOf(),
filteredItems = listOf(),
searchedForText = null,
sortingStrategy = requireContext().settings().savedLoginsSortingStrategy,
highlightedItem = requireContext().settings().savedLoginsMenuHighlightedItem,
duplicateLogins = listOf() // assume on load there are no dupes
)
createInitialLoginsListState(requireContext().settings())
)
}

@ -4,6 +4,7 @@
package org.mozilla.fenix.share
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.content.Intent.ACTION_SEND
@ -98,13 +99,19 @@ class DefaultShareController(
setClassName(app.packageName, app.activityName)
}
@Suppress("TooGenericExceptionCaught")
val result = try {
context.startActivity(intent)
ShareController.Result.SUCCESS
} catch (e: SecurityException) {
snackbar.setText(context.getString(R.string.share_error_snackbar))
snackbar.show()
ShareController.Result.SHARE_ERROR
} catch (e: Exception) {
when (e) {
is SecurityException, is ActivityNotFoundException -> {
snackbar.setText(context.getString(R.string.share_error_snackbar))
snackbar.show()
ShareController.Result.SHARE_ERROR
}
else -> throw e
}
}
dismiss(result)
}

@ -266,10 +266,7 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler
private fun showUndoSnackbarForTab(sessionId: String) {
val store = requireComponents.core.store
val sessionManager = requireComponents.core.sessionManager
val tab = requireComponents.core.store.state.findTab(sessionId) ?: return
val session = sessionManager.findSessionById(sessionId) ?: return
// Check if this is the last tab of this session type
val isLastOpenTab =
@ -279,8 +276,6 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler
return
}
val isSelected = sessionId == requireComponents.core.store.state.selectedTabId ?: false
val snackbarMessage = if (tab.content.private) {
getString(R.string.snackbar_private_tab_closed)
} else {
@ -292,12 +287,8 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler
snackbarMessage,
getString(R.string.snackbar_deleted_undo),
{
sessionManager.add(
session,
isSelected,
engineSessionState = tab.engineState.engineSessionState
)
_tabTrayView?.scrollToTab(session.id)
requireComponents.useCases.tabsUseCases.undo.invoke()
_tabTrayView?.scrollToTab(tab.id)
},
operation = { },
elevation = ELEVATION,

@ -131,7 +131,8 @@ class TabTrayView(
private var tabsTouchHelper: TabsTouchHelper
private val collectionsButtonAdapter = SaveToCollectionsButtonAdapter(interactor, isPrivate)
private val syncedTabsController = SyncedTabsController(lifecycleOwner, view, store, concatAdapter)
private val syncedTabsController =
SyncedTabsController(lifecycleOwner, view, store, concatAdapter)
private val syncedTabsFeature = ViewBoundFeatureWrapper<SyncedTabsFeature>()
private var hasLoaded = false
@ -288,7 +289,10 @@ class TabTrayView(
}
tabTrayItemMenu =
TabTrayItemMenu(view.context, { view.tab_layout.selectedTabPosition == 0 }) {
TabTrayItemMenu(
view.context,
{ tabs.isNotEmpty() && view.tab_layout.selectedTabPosition == 0 },
{ tabs.isNotEmpty() }) {
when (it) {
is TabTrayItemMenu.Item.ShareAllTabs -> interactor.onShareTabsClicked(
isPrivateModeSelected
@ -571,7 +575,6 @@ class TabTrayView(
} else {
View.VISIBLE
}
view.tab_tray_overflow.isVisible = !hasNoTabs
counter_text.text = updateTabCounter(browserState.normalTabs.size)
updateTabCounterContentDescription(browserState.normalTabs.size)
@ -775,6 +778,7 @@ class TabTrayView(
class TabTrayItemMenu(
private val context: Context,
private val shouldShowSaveToCollection: () -> Boolean,
private val hasOpenTabs: () -> Boolean,
private val onItemTapped: (Item) -> Unit = {}
) {
@ -804,7 +808,7 @@ class TabTrayItemMenu(
) {
context.components.analytics.metrics.track(Event.TabsTrayShareAllTabsPressed)
onItemTapped.invoke(Item.ShareAllTabs)
},
}.apply { visible = hasOpenTabs },
SimpleBrowserMenuItem(
context.getString(R.string.tab_tray_menu_tab_settings),
@ -826,7 +830,7 @@ class TabTrayItemMenu(
) {
context.components.analytics.metrics.track(Event.TabsTrayCloseAllTabsPressed)
onItemTapped.invoke(Item.CloseAllTabs)
}
}.apply { visible = hasOpenTabs }
)
}
}

@ -896,6 +896,20 @@ class Settings(private val appContext: Context) : PreferencesHolder {
default = ""
)
var overrideAmoUser by stringPreference(
appContext.getPreferenceKey(R.string.pref_key_override_amo_user),
default = ""
)
var overrideAmoCollection by stringPreference(
appContext.getPreferenceKey(R.string.pref_key_override_amo_collection),
default = ""
)
fun amoCollectionOverrideConfigured(): Boolean {
return overrideAmoUser.isNotEmpty() || overrideAmoCollection.isNotEmpty()
}
val topSitesSize by intPreference(
appContext.getPreferenceKey(R.string.pref_key_top_sites_size),
default = 0

@ -4,6 +4,7 @@
package org.mozilla.fenix.utils
import android.content.Context
import android.view.View
import androidx.appcompat.widget.ContentFrameLayout
import androidx.core.view.updatePadding
@ -19,6 +20,18 @@ import java.util.concurrent.atomic.AtomicBoolean
internal const val UNDO_DELAY = 3000L
internal const val ACCESSIBLE_UNDO_DELAY = 15000L
/**
* Get the recommended time an "undo" action should be available until it can automatically be
* dismissed. The delay may be different based on the accessibility settings of the device.
*/
fun Context.getUndoDelay(): Long {
return if (settings().accessibilityServicesEnabled) {
ACCESSIBLE_UNDO_DELAY
} else {
UNDO_DELAY
}
}
/**
* Runs [operation] after giving user time (see [UNDO_DELAY]) to cancel it.
* In case of cancellation, [onCancel] is executed.
@ -47,6 +60,7 @@ fun CoroutineScope.allowUndo(
// writing a volatile variable.
val requestedUndo = AtomicBoolean(false)
@Suppress("ComplexCondition")
fun showUndoSnackbar() {
val snackbar = FenixSnackbar
.make(
@ -69,6 +83,7 @@ fun CoroutineScope.allowUndo(
val shouldUseBottomToolbar = view.context.settings().shouldUseBottomToolbar
val toolbarHeight = view.resources.getDimensionPixelSize(R.dimen.browser_toolbar_height)
val dynamicToolbarEnabled = view.context.settings().isDynamicToolbarEnabled
snackbar.view.updatePadding(
bottom = if (
@ -79,7 +94,7 @@ fun CoroutineScope.allowUndo(
// can't intelligently position the snackbar on the upper most view.
// Ideally we should not pass ContentFrameLayout in, but it's the only
// way to display snackbars through a fragment transition.
view is ContentFrameLayout
(view is ContentFrameLayout || !dynamicToolbarEnabled)
) {
toolbarHeight
} else {
@ -92,13 +107,7 @@ fun CoroutineScope.allowUndo(
// Wait a bit, and if user didn't request cancellation, proceed with
// requested operation and hide the snackbar.
launch {
val lengthToDelay = if (view.context.settings().accessibilityServicesEnabled) {
ACCESSIBLE_UNDO_DELAY
} else {
UNDO_DELAY
}
delay(lengthToDelay)
delay(view.context.getUndoDelay())
if (!requestedUndo.get()) {
snackbar.dismiss()

@ -6,7 +6,7 @@ package org.mozilla.fenix.whatsnew
import android.content.Context
import android.os.StrictMode
import org.mozilla.fenix.ext.resetPoliciesAfter
import org.mozilla.fenix.ext.components
// This file is a modified port from Focus Android
@ -70,7 +70,7 @@ class WhatsNew private constructor(private val storage: WhatsNewStorage) {
fun shouldHighlightWhatsNew(context: Context): Boolean {
return shouldHighlightWhatsNew(
ContextWhatsNewVersion(context),
StrictMode.allowThreadDiskReads().resetPoliciesAfter {
context.components.strictMode.resetAfter(StrictMode.allowThreadDiskReads()) {
SharedPreferenceWhatsNewStorage(context)
}
)

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/linear_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<EditText
android:id="@+id/custom_amo_user"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/customize_addon_collection_user_hint"
android:singleLine="true"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginTop="8dp"
android:textColor="?primaryText"/>
<EditText
android:id="@+id/custom_amo_collection"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/customize_addon_collection_hint"
android:singleLine="true"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:textColor="?primaryText"/>
</LinearLayout>

@ -158,7 +158,7 @@
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/open_tabs_menu"
android:visibility="visible"
app:tint="@color/accent_normal_theme"
app:tint="@color/tab_tray_heading_icon_menu_normal_theme"
app:layout_constraintBottom_toBottomOf="@id/tab_layout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/tab_layout"

@ -142,6 +142,8 @@
<string name="browser_menu_install_on_homescreen">Усталяваць</string>
<!-- Menu option on the toolbar that takes you to synced tabs page-->
<string name="synced_tabs">Сінхранізаваныя карткі</string>
<!-- Content description (not visible, for screen readers etc.) for the Resync tabs button -->
<string name="resync_button_content_description">Сінхранізаваць ізноў</string>
<!-- Browser menu button that opens the find in page menu -->
<string name="browser_menu_find_in_page">Знайсці на старонцы</string>
<!-- Browser menu button that creates a private tab -->
@ -264,6 +266,8 @@
<string name="preferences_open_links_in_a_private_tab">Адкрываць спасылкі ў прыватнай картцы</string>
<!-- Preference for allowing screenshots to be taken while in a private tab-->
<string name="preferences_allow_screenshots_in_private_mode">Дазволіць здымкі экрана ў прыватным рэжыме</string>
<!-- Will inform the user of the risk of activating Allow screenshots in private browsing option -->
<string name="preferences_screenshots_in_private_mode_disclaimer">Калі гэта дазволена, прыватныя карткі таксама будуць бачныя, калі адкрыта некалькі праграм</string>
<!-- Preference for adding private browsing shortcut -->
<string name="preferences_add_private_browsing_shortcut">Дадаць ярлык прыватнага аглядання</string>
<!-- Preference for accessibility -->
@ -460,6 +464,11 @@
<!-- Preferences for using pull to refresh in a webpage -->
<string name="preference_gestures_website_pull_to_refresh">Пацягніце, каб абнавіць</string>
<!-- Preference for using the dynamic toolbar -->
<string name="preference_gestures_dynamic_toolbar">Пракруціце, каб схаваць панэль інструментаў</string>
<!-- Preference for switching tabs by swiping horizontally on the toolbar -->
<string name="preference_gestures_swipe_toolbar_switch_tabs">Пасуньце ўбок панэль інструментаў, каб пераключыць карткі</string>
<!-- Library -->
<!-- Option in Library to open Sessions page -->
<string name="library_sessions">Сеансы</string>
@ -1278,6 +1287,9 @@
<!-- Placeholder text for the TextView in the Add to Homescreen dialog -->
<string name="add_to_homescreen_text_placeholder">Назва ярлыка</string>
<!-- Describes the add to homescreen functionality -->
<string name="add_to_homescreen_description_2">Вы можаце лёгка дадаць гэты вэб-сайт на хатні экран вашай прылады, каб мець да яго імгненны доступ і аглядаць хутчэй, нібыта гэта асобная праграма.</string>
<!-- Preference for managing the settings for logins and passwords in Fenix -->
<string name="preferences_passwords_logins_and_passwords">Лагіны і паролі</string>
<!-- Preference for managing the saving of logins and passwords in Fenix -->
@ -1348,8 +1360,12 @@
<string name="logins_site_copied">Сайт скапіяваны ў буфер абмену</string>
<!-- Content Description (for screenreaders etc) read for the button to copy a password in logins-->
<string name="saved_logins_copy_password">Капіяваць пароль</string>
<!-- Content Description (for screenreaders etc) read for the button to clear a password while editing a login-->
<string name="saved_logins_clear_password">Ачысціць пароль</string>
<!-- Content Description (for screenreaders etc) read for the button to copy a username in logins -->
<string name="saved_login_copy_username">Капіяваць імя карыстальніка</string>
<!-- Content Description (for screenreaders etc) read for the button to clear a username while editing a login -->
<string name="saved_login_clear_username">Ачысціць імя карыстальніка</string>
<!-- Content Description (for screenreaders etc) read for the button to copy a site in logins -->
<string name="saved_login_copy_site">Капіяваць сайт</string>
<!-- Content Description (for screenreaders etc) read for the button to open a site in logins -->

@ -140,6 +140,8 @@
<string name="browser_menu_install_on_homescreen">Staliañ</string>
<!-- Menu option on the toolbar that takes you to synced tabs page-->
<string name="synced_tabs">Ivinelloù goubredet</string>
<!-- Content description (not visible, for screen readers etc.) for the Resync tabs button -->
<string name="resync_button_content_description">Adgoubredañ</string>
<!-- Browser menu button that opens the find in page menu -->
<string name="browser_menu_find_in_page">Kavout er bajennad</string>
<!-- Browser menu button that creates a private tab -->
@ -260,6 +262,8 @@
<string name="preferences_open_links_in_a_private_tab">Digeriñ an ereoù en un ivinell brevez</string>
<!-- Preference for allowing screenshots to be taken while in a private tab-->
<string name="preferences_allow_screenshots_in_private_mode">Aotren an tapadennoù-skramm er merdeiñ prevez</string>
<!-- Will inform the user of the risk of activating Allow screenshots in private browsing option -->
<string name="preferences_screenshots_in_private_mode_disclaimer">Mard eo aotreet, an ivinelloù prevez a vo hewel pa vo meur a arload digor</string>
<!-- Preference for adding private browsing shortcut -->
<string name="preferences_add_private_browsing_shortcut">Ouzhpennañ ur verradenn merdeiñ prevez</string>
<!-- Preference for accessibility -->
@ -280,6 +284,8 @@
<string name="preferences_theme">Neuz</string>
<!-- Preference for customizing the home screen -->
<string name="preferences_home">Degemer</string>
<!-- Preference for gestures based actions -->
<string name="preferences_gestures">Jestroù</string>
<!-- Preference for settings related to visual options -->
<string name="preferences_customize">Personelaat</string>
<!-- Preference description for banner about signing in -->
@ -314,8 +320,12 @@
<string name="preferences_search_browsing_history">Klask er roll istor</string>
<!-- Preference title for switch preference to suggest bookmarks when searching -->
<string name="preferences_search_bookmarks">Klask er sinedoù</string>
<!-- Preference title for switch preference to suggest synced tabs when searching -->
<string name="preferences_search_synced_tabs">Klask en ivinelloù goubredet</string>
<!-- Preference for account settings -->
<string name="preferences_account_settings">Arventennoù ar gont</string>
<!-- Preference for enabling url autocomplete-->
<string name="preferences_enable_autocomplete_urls">Leuniañ an ereoù ent emgefreek</string>
<!-- Preference for open links in third party apps -->
<string name="preferences_open_links_in_apps">Digeriñ ereoù en arloadoù</string>
<!-- Preference for open download with an external download manager app -->
@ -452,6 +462,16 @@
<!-- Preference for using following device theme -->
<string name="preference_follow_device_theme">Mont gant neuz ar benveg</string>
<!-- Gestures Preferences-->
<!-- Preferences for using pull to refresh in a webpage -->
<string name="preference_gestures_website_pull_to_refresh">Sachit da azgrenaat</string>
<!-- Preference for using the dynamic toolbar -->
<string name="preference_gestures_dynamic_toolbar">Dibunit da guzhat ar varrenn-ostilhoù</string>
<!-- Preference for switching tabs by swiping horizontally on the toolbar -->
<string name="preference_gestures_swipe_toolbar_switch_tabs">Riklit ar varrenn-ostilhoù war ar chostez evit cheñch ivinell</string>
<!-- Preference for showing the opened tabs by swiping up on the toolbar-->
<string name="preference_gestures_swipe_toolbar_show_tabs">Rikli ar ar varrenn-ostilhoù war-zu an nech evit digeriñ ivinelloù</string>
<!-- Library -->
<!-- Option in Library to open Sessions page -->
<string name="library_sessions">Estezioù</string>
@ -1039,7 +1059,7 @@
<!-- text for the firefox account onboarding card header when we detect you're already signed in to
another Firefox browser. (The word `Firefox` should not be translated)
The first parameter is the email of the detected user's account -->
<string name="onboarding_firefox_account_auto_signin_header_2">Kennasket hoch evel %s war ur merdeer Firefox all war ar pellgomzer-mañ. Fellout a ra deoch kennaskañ gant ar gont-mañ?</string>
<string name="onboarding_firefox_account_auto_signin_header_3">Kennasket och evel %s war ur merdeer Firefox all war an trevnad-mañ. Fellout a ra deoch kennaskañ gant ar gont-mañ?</string>
<!-- text for the button to confirm automatic sign-in -->
<string name="onboarding_firefox_account_auto_signin_confirm">Ya, kennaskit achanon</string>
<!-- text for the automatic sign-in button while signing in is in process -->
@ -1129,6 +1149,8 @@
<string name="sign_in_with_camera">Kennaskit gant ho kamera</string>
<!-- Text shown for settings option for sign with email -->
<string name="sign_in_with_email">Ober gant ur chomlech postel kentoch</string>
<!-- Text shown for settings option for create new account text.'Firefox' intentionally hardcoded here.-->
<string name="sign_in_create_account_text"><![CDATA[Kont ebet? <u>Krouit unan</u> evit goubredañ Firefox etre an trevnadoù.]]></string>
<!-- Text shown in confirmation dialog to sign out of account -->
<string name="sign_out_confirmation_message">Firefox a baouezo da choubredañ gant ho kont, met ne vo ket dilamet ho roadennoù merdeiñ war an trevnad-mañ.</string>
<!-- Text shown in confirmation dialog to sign out of account. The first parameter is the name of the app (e.g. Firefox Preview) -->
@ -1237,6 +1259,11 @@
The first parameter is the app name -->
<string name="open_source_licenses_title">%s | Levraouegoù digor o zarzh</string>
<!-- Category of trackers (redirect trackers) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_title">Heulierien adheñchañ</string>
<!-- Description of redirect tracker cookies that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_description">Skarzhañ an toupinoù lakaet gant adheñchadurioù etrezek lechiennoù heuliañ anavezet</string>
<!-- About page link text to open support link -->
<string name="about_support">Skor</string>
<!-- About page link text to list of past crashes (like about:crashes on desktop) -->
@ -1279,8 +1306,9 @@
<string name="add_to_homescreen_continue">Kenderchel etrezek al lechienn</string>
<!-- Placeholder text for the TextView in the Add to Homescreen dialog -->
<string name="add_to_homescreen_text_placeholder">Anv ar verradenn</string>
<!-- Describes the add to homescreen functionality -->
<string name="add_to_homescreen_description">Gallout a rit ouzhpennañ al lechienn dho pennbajenn en un doare aes evit gallout he haeziñ ha merdeiñ buanoch, evel un arload.</string>
<string name="add_to_homescreen_description_2">Gallout a rit ouzhpennañ al lechienn-mañ da bennbajenn ho trevnad evit mont war-eeun ha merdeiñ primoch evel ma vefe un arload.</string>
<!-- Preference for managing the settings for logins and passwords in Fenix -->
<string name="preferences_passwords_logins_and_passwords">Titouroù kennaskañ</string>
@ -1351,8 +1379,12 @@
<string name="logins_site_copied">Eilet eo bet al lechienn er golver</string>
<!-- Content Description (for screenreaders etc) read for the button to copy a password in logins-->
<string name="saved_logins_copy_password">Eilañ ar ger-tremen</string>
<!-- Content Description (for screenreaders etc) read for the button to clear a password while editing a login-->
<string name="saved_logins_clear_password">Skarzhañ ar ger-tremen</string>
<!-- Content Description (for screenreaders etc) read for the button to copy a username in logins -->
<string name="saved_login_copy_username">Eilañ an anv arveriad</string>
<!-- Content Description (for screenreaders etc) read for the button to clear a username while editing a login -->
<string name="saved_login_clear_username">Skarzhañ an anv arveriad</string>
<!-- Content Description (for screenreaders etc) read for the button to copy a site in logins -->
<string name="saved_login_copy_site">Eilañ al lechienn</string>
<!-- Content Description (for screenreaders etc) read for the button to open a site in logins -->
@ -1505,7 +1537,7 @@
<string name="saved_login_duplicate">Un titour kennaskañ gant an anv arveriad-mañ a zo dioutañ endeo.</string>
<!-- Synced Tabs -->
<!-- Text displayed to ask user to connect another device as no devices found with account -->
<!-- Text displayed to ask user to connect another device as no devices found with account -->
<string name="synced_tabs_connect_another_device">Kennaskañ un trevnad all.</string>
<!-- Text displayed asking user to re-authenticate -->
<string name="synced_tabs_reauth">Adkennaskit mar plij ganeoch.</string>
@ -1526,7 +1558,7 @@
<!-- Title text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_title">Tizhet eo bet ar vevenn lechiennoù</string>
<!-- Content description text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_content">Evit ouzhpennañ ul lechienn gwellañ nevez e rankit dilemel unan all. Stokit pad pell amzer war ul lechienn ha dibabit Dilemel.</string>
<string name="top_sites_max_limit_content_2">Evit ouzhpennañ ul lechienn gwellañ eo ret deoch dilemel unan. Stokit ha dalchit al lechienn ha dibabit dilemel.</string>
<!-- Confirmation dialog button text when top sites limit is reached. -->
<string name="top_sites_max_limit_confirmation_button">Mat, komprenet am eus</string>
@ -1536,7 +1568,7 @@
<!-- Content description for close button in collection placeholder. -->
<string name="remove_home_collection_placeholder_content_description">Dilemel</string>
<!-- depcrecated: text for the firefox account onboarding card header
<!-- Deprecated: text for the firefox account onboarding card header
The first parameter is the name of the app (e.g. Firefox Preview) -->
<string name="onboarding_firefox_account_header">Tennit gounid eus %s.</string>
@ -1544,4 +1576,9 @@
<string name="no_collections_header1">Dastumit ar pezh a zo pouezus evidoch</string>
<!-- Deprecated: Label to describe what collections are to a new user without any collections -->
<string name="no_collections_description1">Strollit ar cʼhlaskoù, al lecʼhiennoù hag an ivinelloù heñvel evit mont daveto buanocʼh.</string>
<!-- Deprecated: text for the firefox account onboarding card header when we detect you're already signed in to -->
<string name="onboarding_firefox_account_auto_signin_header_2">Kennasket hoch evel %s war ur merdeer Firefox all war ar pellgomzer-mañ. Fellout a ra deoch kennaskañ gant ar gont-mañ?</string>
<!-- Deprecated: Describes the add to homescreen functionality -->
<string name="add_to_homescreen_description">Gallout a rit ouzhpennañ al lechienn dho pennbajenn en un doare aes evit gallout he haeziñ ha merdeiñ buanoch, evel un arload.</string>
</resources>

@ -151,6 +151,8 @@
<string name="browser_menu_install_on_homescreen">Tiyak</string>
<!-- Menu option on the toolbar that takes you to synced tabs page-->
<string name="synced_tabs">Ximon taq ruwi\'</string>
<!-- Content description (not visible, for screen readers etc.) for the Resync tabs button -->
<string name="resync_button_content_description">Tixim chik</string>
<!-- Browser menu button that opens the find in page menu -->
<string name="browser_menu_find_in_page">Tikanöx pa ruxaq</string>
<!-- Browser menu button that creates a private tab -->
@ -277,6 +279,8 @@
<!-- Preference for allowing screenshots to be taken while in a private tab-->
<string name="preferences_allow_screenshots_in_private_mode">Tiya\' q\'ij richin nichap ruwa pa ichinan okem pa k\'amaya\'l</string>
<!-- Will inform the user of the risk of activating Allow screenshots in private browsing option -->
<string name="preferences_screenshots_in_private_mode_disclaimer">We ya\'on q\'ij, xketz\'et chuqa\' ri ichinan taq ruwi\' toq e jaqäl k\'ïy chokoy</string>
<!-- Preference for adding private browsing shortcut -->
<string name="preferences_add_private_browsing_shortcut">Titz\'aqatisäx choj okem pa ri ichinan okem pa k\'amaya\'l</string>
<!-- Preference for accessibility -->
@ -297,6 +301,8 @@
<string name="preferences_theme">Wachinel</string>
<!-- Preference for customizing the home screen -->
<string name="preferences_home">Tikirib\'äl</string>
<!-- Preference for gestures based actions -->
<string name="preferences_gestures">B\'anoj paläj</string>
<!-- Preference for settings related to visual options -->
<string name="preferences_customize">Tichinäx</string>
<!-- Preference description for banner about signing in -->
@ -334,8 +340,12 @@
<string name="preferences_search_browsing_history">Tikanöx runatab\'al okem pa k\'amaya\'l</string>
<!-- Preference title for switch preference to suggest bookmarks when searching -->
<string name="preferences_search_bookmarks">Kekanöx taq yaketal</string>
<!-- Preference title for switch preference to suggest synced tabs when searching -->
<string name="preferences_search_synced_tabs">Kekanöx ri taq ruwi\' eximon</string>
<!-- Preference for account settings -->
<string name="preferences_account_settings">Kinuk\'ulem Rub\'i\' Taqoya\'l</string>
<!-- Preference for enabling url autocomplete-->
<string name="preferences_enable_autocomplete_urls">Titz\'aqatisäx ruyon URLs</string>
<!-- Preference for open links in third party apps -->
<string name="preferences_open_links_in_apps">Kejaq taq ximonel pa taq chokoy</string>
<!-- Preference for open download with an external download manager app -->
@ -476,6 +486,17 @@
<!-- Preference for using following device theme -->
<string name="preference_follow_device_theme">Tojqäx ri ruwachinel oyonib\'äl</string>
<!-- Gestures Preferences-->
<!-- Preferences for using pull to refresh in a webpage -->
<string name="preference_gestures_website_pull_to_refresh">Tiqirirëx richin nik\'ex</string>
<!-- Preference for using the dynamic toolbar -->
<string name="preference_gestures_dynamic_toolbar">Taq\'axaj richin nawewaj ri ajkajtz\'ik</string>
<!-- Preference for switching tabs by swiping horizontally on the toolbar -->
<string name="preference_gestures_swipe_toolbar_switch_tabs">Tiq\'axäx ri ajkajtz\'ik chi taq ruchi\' richin nijal ruwi\'</string>
<!-- Preference for showing the opened tabs by swiping up on the toolbar-->
<string name="preference_gestures_swipe_toolbar_show_tabs">Tiq\'axäx ri ajkatz\'ik ajsik richin yejaq taq ruwi\'</string>
<!-- Library -->
<!-- Option in Library to open Sessions page -->
<string name="library_sessions">Taq Moloj</string>
@ -1174,6 +1195,8 @@
<string name="sign_in_with_camera">Tatikirisaj molojri\'ïl rik\'in ri elesäy awachib\'al</string>
<!-- Text shown for settings option for sign with email -->
<string name="sign_in_with_email">Tawokisaj ri taqoya\'l</string>
<!-- Text shown for settings option for create new account text.'Firefox' intentionally hardcoded here.-->
<string name="sign_in_create_account_text"><![CDATA[¿La man k\'o ta rub\'i\' ataqoya\'l? <u>Tatz\'uku\' jun</u> richin naxïm Firefox pa taq okisab\'äl.]]></string>
<!-- Text shown in confirmation dialog to sign out of account -->
<string name="sign_out_confirmation_message">Firefox man xtuxïm ta chik ri rub\'i\' ataqoya\'l, man xkeyuj ta ri taq rutzij awokem pa re oyonib\'äl re\'.</string>
<!-- Text shown in confirmation dialog to sign out of account. The first parameter is the name of the app (e.g. Firefox Preview) -->
@ -1286,6 +1309,11 @@
The first parameter is the app name -->
<string name="open_source_licenses_title">%s | OSS taq wujb\'äl</string>
<!-- Category of trackers (redirect trackers) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_title">Kechojmïx chik Ojqanela\'</string>
<!-- Description of redirect tracker cookies that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_description">Yeruyüj ri taq cookies echojmirisan pan ajk\'amaya\'l taq ruxaq ojqanem etaman kiwa.</string>
<!-- About page link text to open support link -->
<string name="about_support">Tob\'äl</string>
<!-- About page link text to list of past crashes (like about:crashes on desktop) -->
@ -1589,6 +1617,8 @@ Achi\'el: \nhttps://www.google.com/search?q=%s</string>
<!-- Top Sites -->
<!-- Title text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_title">Xaq\'i\' ruchi\' ri jutaqil taq ruxaq</string>
<!-- Content description text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_content_2">Richin natz\'aqatisaj jun k\'ak\'a\' nimaläj ruxaq, tayuju\' jun. Tapitz\'a\' ri ruxaq chuqa\' tacha\' tiyuj.</string>
<!-- Confirmation dialog button text when top sites limit is reached. -->
<string name="top_sites_max_limit_confirmation_button">ÜTZ, Wetaman Chik</string>
@ -1598,7 +1628,7 @@ Achi\'el: \nhttps://www.google.com/search?q=%s</string>
<!-- Content description for close button in collection placeholder. -->
<string name="remove_home_collection_placeholder_content_description">Tiyuj</string>
<!-- depcrecated: text for the firefox account onboarding card header
<!-- Deprecated: text for the firefox account onboarding card header
The first parameter is the name of the app (e.g. Firefox Preview) -->
<string name="onboarding_firefox_account_header">Ütz tawokisaj ri %s.</string>

@ -1174,6 +1174,8 @@
<string name="sign_in_with_camera">Cunnettatevi cù u vostru appareghju-fotò</string>
<!-- Text shown for settings option for sign with email -->
<string name="sign_in_with_email">Impiegà piuttostu un messaghju elettronicu</string>
<!-- Text shown for settings option for create new account text.'Firefox' intentionally hardcoded here.-->
<string name="sign_in_create_account_text"><![CDATA[Contu, ùn ne avete ? <u>Createne unu</u> per sincrunizà i vostri apparechji.]]></string>
<!-- Text shown in confirmation dialog to sign out of account -->
<string name="sign_out_confirmation_message">Firefox ùn si sincruniserà più cù u vostru contu, ma ùn squasserà alcunu datu di navigazione nantà stapparechju.</string>
<!-- Text shown in confirmation dialog to sign out of account. The first parameter is the name of the app (e.g. Firefox Preview) -->
@ -1281,6 +1283,11 @@
The first parameter is the app name -->
<string name="open_source_licenses_title">%s | Bibliuteche di fonte aperta</string>
<!-- Category of trackers (redirect trackers) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_title">Frattighjatori da ridirezzione</string>
<!-- Description of redirect tracker cookies that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_description">Squasseghja i canistrelli definiti da ridirezzione ver di siti web cunnisciuti per u spiunagiu.</string>
<!-- About page link text to open support link -->
<string name="about_support">Assistenza</string>
<!-- About page link text to list of past crashes (like about:crashes on desktop) -->

@ -85,11 +85,15 @@
<!-- Text for the negative action button -->
<string name="open_in_app_cfr_negative_button_text">Απόρριψη</string>
<!-- Text for the info dialog when camera permissions have been denied but user tries to access a camera feature. -->
<string name="camera_permissions_needed_message">Απαιτείται πρόσβαση στην κάμερα. Μεταβείτε στις Ρυθμίσεις Android, πατήστε &quot;Δικαιώματα&quot; και πατήστε &quot;Να επιτρέπεται&quot;.</string>
<!-- Text for the positive action button to go to Android Settings to grant permissions. -->
<string name="camera_permissions_needed_positive_button_text">Μετάβαση στις ρυθμίσεις</string>
<!-- Text for the negative action button to dismiss the dialog. -->
<string name="camera_permissions_needed_negative_button_text">Απόρριψη</string>
<!-- Text for the banner message to tell users about our auto close feature. -->
<string name="tab_tray_close_tabs_banner_message">Ρυθμίστε τις ανοιχτές καρτέλες ώστε να κλείνουν αυτόματα αυτές που δεν έχουν προβληθεί την προηγούμενη ημέρα, εβδομάδα ή μήνα.</string>
<!-- Text for the positive action button to go to Settings for auto close tabs. -->
<string name="tab_tray_close_tabs_banner_positive_button_text">Εμφάνιση επιλογών</string>
<!-- Text for the negative action button to dismiss the Close Tabs Banner. -->
@ -265,6 +269,8 @@
<string name="preferences_open_links_in_a_private_tab">Άνοιγμα συνδέσμων σε ιδιωτική καρτέλα</string>
<!-- Preference for allowing screenshots to be taken while in a private tab-->
<string name="preferences_allow_screenshots_in_private_mode">Να επιτρέπονται στιγμιότυπα στην ιδιωτική περιήγηση</string>
<!-- Will inform the user of the risk of activating Allow screenshots in private browsing option -->
<string name="preferences_screenshots_in_private_mode_disclaimer">Αν επιτρέπεται, οι ιδιωτικές καρτέλες θα είναι επίσης ορατές όταν είναι ανοιχτές πολλές εφαρμογές</string>
<!-- Preference for adding private browsing shortcut -->
<string name="preferences_add_private_browsing_shortcut">Προσθήκη συντόμευσης ιδιωτικής περιήγησης</string>
<!-- Preference for accessibility -->
@ -398,16 +404,25 @@
<string name="preferences_tracking_protection_exceptions_description">Η προστασία από καταγραφή είναι ανενεργή για αυτές τις ιστοσελίδες</string>
<!-- Button in Exceptions Preference to turn on tracking protection for all sites (remove all exceptions) -->
<string name="preferences_tracking_protection_exceptions_turn_on_for_all">Ενεργοποίηση για όλες τις σελίδες</string>
<!-- Text displayed when there are no exceptions -->
<string name="exceptions_empty_message_description">Οι εξαιρέσεις σας επιτρέπουν να απενεργοποιήσετε την προστασία από καταγραφή σε συγκεκριμένες σελίδες.</string>
<!-- Text displayed when there are no exceptions, with learn more link that brings users to a tracking protection SUMO page -->
<string name="exceptions_empty_message_learn_more_link">Μάθετε περισσότερα</string>
<!-- Description in Quick Settings that tells user tracking protection is off globally for all sites, and links to Settings to turn it on -->
<string name="preferences_tracking_protection_turned_off_globally">Γενικά ανενεργή, μεταβείτε στις Ρυθμίσεις για ενεργοποίηση.</string>
<!-- Preference switch for Telemetry -->
<string name="preferences_telemetry">Τηλεμετρία</string>
<!-- Preference switch for usage and technical data collection -->
<string name="preference_usage_data">Δεδομένα χρήσης και τεχνικά δεδομένα</string>
<!-- Preference description for usage and technical data collection -->
<string name="preferences_usage_data_description">Αποστέλλει πληροφορίες επιδόσεων, χρήσης, υλικού συσκευής και εξατομίκευσης του προγράμματος περιήγησης στη Mozilla για βελτίωση του %1$s</string>
<!-- Preference switch for marketing data collection -->
<string name="preferences_marketing_data">Δεδομένα μάρκετινγκ</string>
<!-- Preference description for marketing data collection, parameter is the app name (e.g. Firefox) -->
<string name="preferences_marketing_data_description">Αποστέλλει δεδομένα σχετικά με τις λειτουργίες που χρησιμοποιείτε στο %1$s με το Leanplum, την εταιρεία μάρκετινγκ για κινητές συσκευές.</string>
<!-- Title for experiments preferences -->
<string name="preference_experiments">Πειράματα</string>
@ -457,6 +472,16 @@
<!-- Preference for using following device theme -->
<string name="preference_follow_device_theme">Χρήση θέματος συσκευής</string>
<!-- Gestures Preferences-->
<!-- Preferences for using pull to refresh in a webpage -->
<string name="preference_gestures_website_pull_to_refresh">Τράβηγμα για ανανέωση</string>
<!-- Preference for using the dynamic toolbar -->
<string name="preference_gestures_dynamic_toolbar">Κύλιση για απόκρυψη γραμμής εργαλείων</string>
<!-- Preference for switching tabs by swiping horizontally on the toolbar -->
<string name="preference_gestures_swipe_toolbar_switch_tabs">Ολίσθηση γραμμής εργαλείων προς τα πλάγια για εναλλαγή καρτελών</string>
<!-- Preference for showing the opened tabs by swiping up on the toolbar-->
<string name="preference_gestures_swipe_toolbar_show_tabs">Ολίσθηση γραμμής εργαλείων προς τα πάνω για άνοιγμα καρτελών</string>
<!-- Library -->
<!-- Option in Library to open Sessions page -->
<string name="library_sessions">Συνεδρίες</string>
@ -1076,10 +1101,14 @@
<string name="onboarding_tracking_protection_description_2">Οι ρυθμίσεις απορρήτου και ασφάλειας αποκλείουν ιχνηλάτες, κακόβουλο λογισμικό και εταιρείες που σας ακολουθούν.</string>
<!-- text for tracking protection radio button option for standard level of blocking -->
<string name="onboarding_tracking_protection_standard_button_2">Τυπική (προεπιλογή)</string>
<!-- text for standard blocking option button description -->
<string name="onboarding_tracking_protection_standard_button_description_2">Φραγή λιγότερων ιχνηλατών. Οι σελίδες θα φορτώνονται κανονικά.</string>
<!-- text for tracking protection radio button option for strict level of blocking -->
<string name="onboarding_tracking_protection_strict_button">Αυστηρή (προτείνεται)</string>
<!-- text for tracking protection radio button option for strict level of blocking -->
<string name="onboarding_tracking_protection_strict_option">Αυστηρή</string>
<!-- text for strict blocking option button description -->
<string name="onboarding_tracking_protection_strict_button_description_2">Φραγή περισσότερων ιχνηλατών, διαφημίσεων και αναδυόμενων παραθύρων. Οι σελίδες φορτώνονται ταχύτερα, αλλά ορισμένα χαρακτηριστικά ενδέχεται να μην λειτουργούν.</string>
<!-- text for the toolbar position card header
In English this is an idiom for "choose a side as in an argument or fight"
but it is ok to make this more literally about "choosing a position in a physical space -->
@ -1113,6 +1142,8 @@
<!-- Onboarding theme -->
<!-- text for the theme picker onboarding card header -->
<string name="onboarding_theme_picker_header">Επιλογή θέματος</string>
<!-- text for the theme picker onboarding card description -->
<string name="onboarding_theme_picker_description1">Εξοικονόμηση μπαταρίας και προστασία ματιών με τη σκοτεινή λειτουργία.</string>
<!-- Automatic theme setting (will follow device setting) -->
<string name="onboarding_theme_automatic_title">Αυτόματο</string>
<!-- Summary of automatic theme setting (will follow device setting) -->
@ -1141,6 +1172,8 @@
<!-- Text shown for settings option for sign with email -->
<string name="sign_in_with_email">Χρήση email</string>
<!-- Text shown for settings option for create new account text.'Firefox' intentionally hardcoded here.-->
<string name="sign_in_create_account_text"><![CDATA[Δεν έχετε λογαριασμό; <u>Δημιουργήστε έναν</u> για συγχρονισμό του Firefox μεταξύ συσκευών.]]></string>
<!-- Text shown in confirmation dialog to sign out of account -->
<string name="sign_out_confirmation_message">Το Firefox θα σταματήσει να συγχρονίζεται με το λογαριασμό σας, αλλά δεν θα διαγράψει τα δεδομένα περιήγησης από αυτή τη συσκευή.</string>
<!-- Text shown in confirmation dialog to sign out of account. The first parameter is the name of the app (e.g. Firefox Preview) -->
@ -1214,12 +1247,20 @@
<string name="etp_social_media_trackers_description">Περιορίζει την ικανότητα των κοινωνικών δικτύων να παρακολουθούν τη δραστηριότητά σας στο διαδίκτυο.</string>
<!-- Category of trackers (cross-site tracking cookies) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_cookies_title">Cookies ιχνηλάτησης μεταξύ ιστοσελίδων</string>
<!-- Description of cross-site tracking cookies that can be blocked by Enhanced Tracking Protection -->
<string name="etp_cookies_description">Αποκλείει τα cookies που χρησιμοποιούν τα δίκτυα διαφημίσεων και οι εταιρείες ανάλυσης δεδομένων για τη συλλογή δεδομένων περιήγησής σας από πολλαπλές ιστοσελίδες.</string>
<!-- Category of trackers (cryptominers) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_cryptominers_title">Cryptominers</string>
<!-- Description of cryptominers that can be blocked by Enhanced Tracking Protection -->
<string name="etp_cryptominers_description">Εμποδίζει τα κακόβουλα σενάρια από το να προσπελάσουν τη συσκευή σας για εξόρυξη ψηφιακού νομίσματος.</string>
<!-- Category of trackers (fingerprinters) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_fingerprinters_title">Fingerprinters</string>
<!-- Description of fingerprinters that can be blocked by Enhanced Tracking Protection -->
<string name="etp_fingerprinters_description">Διακόπτει τη συλλογή μοναδικών, αναγνωριστικών δεδομένων της συσκευής σας που μπορούν να χρησιμοποιηθούν για σκοπούς καταγραφής.</string>
<!-- Category of trackers (tracking content) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_tracking_content_title">Περιεχόμενο καταγραφής</string>
<!-- Description of tracking content that can be blocked by Enhanced Tracking Protection -->
<string name="etp_tracking_content_description">Διακόπτει τη φόρτωση εξωτερικών διαφημίσεων, βίντεο και άλλου περιεχομένου που περιέχει κώδικα καταγραφής. Ίσως επηρεάσει τη λειτουργικότητα μερικών ιστοσελίδων.</string>
<!-- Enhanced Tracking Protection Onboarding Message shown in a dialog above the toolbar. The first parameter is the name of the application (For example: Fenix) -->
<string name="etp_onboarding_cfr_message">Κάθε φορά που η ασπίδα είναι μωβ, το %s έχει αποκλείσει ιχνηλάτες σε μια σελίδα. Πατήστε για περισσότερες πληροφορίες.</string>
<!-- Enhanced Tracking Protection message that protection is currently on for this site -->
@ -1242,6 +1283,12 @@
The first parameter is the app name -->
<string name="open_source_licenses_title">%s | Βιβλιοθήκες OSS</string>
<!-- Category of trackers (redirect trackers) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_title">Ανακατεύθυνση ιχνηλατών</string>
<!-- Description of redirect tracker cookies that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_description">Διαγράφει τα cookies που τοποθετούνται από ανακατευθύνσεις σε γνωστές ιστοσελίδες καταγραφής.</string>
<!-- About page link text to open support link -->
<string name="about_support">Υποστήριξη</string>
<!-- About page link text to list of past crashes (like about:crashes on desktop) -->
@ -1289,6 +1336,9 @@
<!-- Placeholder text for the TextView in the Add to Homescreen dialog -->
<string name="add_to_homescreen_text_placeholder">Όνομα συντόμευσης</string>
<!-- Describes the add to homescreen functionality -->
<string name="add_to_homescreen_description_2">Μπορείτε εύκολα να προσθέσετε αυτή την ιστοσελίδα στην αρχική οθόνη για άμεση πρόσβαση και ταχύτερη περιήγηση, σαν να ήταν εφαρμογή.</string>
<!-- Preference for managing the settings for logins and passwords in Fenix -->
<string name="preferences_passwords_logins_and_passwords">Συνδέσεις και κωδικοί πρόσβασης</string>
<!-- Preference for managing the saving of logins and passwords in Fenix -->
@ -1373,6 +1423,10 @@
<string name="saved_login_hide_password">Απόκρυψη κωδικού πρόσβασης</string>
<!-- Message displayed in biometric prompt displayed for authentication before allowing users to view their logins -->
<string name="logins_biometric_prompt_message">Ξεκλειδώστε για να δείτε τις αποθηκευμένες συνδέσεις σας</string>
<!-- Title of warning dialog if users have no device authentication set up -->
<string name="logins_warning_dialog_title">Προστασία στοιχείων σύνδεσης</string>
<!-- Message of warning dialog if users have no device authentication set up -->
<string name="logins_warning_dialog_message">Ορίστε ένα μοτίβο κλειδώματος συσκευής, ένα ΡΙΝ ή έναν κωδικό πρόσβασης για προστασία των αποθηκευμένων στοιχείων σύνδεσης, σε περίπτωση που κάποιος τρίτος αποκτήσει πρόσβαση στη συσκευή σας.</string>
<!-- Negative button to ignore warning dialog if users have no device authentication set up -->
<string name="logins_warning_dialog_later">Αργότερα</string>
<!-- Positive button to send users to set up a pin of warning dialog if users have no device authentication set up -->

@ -1196,6 +1196,8 @@
<string name="sign_in_with_camera">Inicia sesión con tu cámara</string>
<!-- Text shown for settings option for sign with email -->
<string name="sign_in_with_email">Usa el correo electrónico</string>
<!-- Text shown for settings option for create new account text.'Firefox' intentionally hardcoded here.-->
<string name="sign_in_create_account_text"><![CDATA[¿No tienes cuenta? <u>Crea una</u> para sincronizar Firefox entre dispositivos.]]></string>
<!-- Text shown in confirmation dialog to sign out of account -->
<string name="sign_out_confirmation_message">Firefox dejará de sincronizarse con tu cuenta, pero no eliminará ninguno de tus datos de navegación en este dispositivo.</string>
<!-- Text shown in confirmation dialog to sign out of account. The first parameter is the name of the app (e.g. Firefox Preview) -->
@ -1305,6 +1307,11 @@
The first parameter is the app name -->
<string name="open_source_licenses_title">%s | Bibliotecas OSS</string>
<!-- Category of trackers (redirect trackers) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_title">Rastreadores de redirección</string>
<!-- Description of redirect tracker cookies that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_description">Borra las cookies establecidas por redirecciones a sitios web de rastreo conocidos.</string>
<!-- About page link text to open support link -->
<string name="about_support">Ayuda</string>
<!-- About page link text to list of past crashes (like about:crashes on desktop) -->

@ -147,6 +147,8 @@
<string name="browser_menu_install_on_homescreen">Instalatu</string>
<!-- Menu option on the toolbar that takes you to synced tabs page-->
<string name="synced_tabs">Sinkronizatutako fitxak</string>
<!-- Content description (not visible, for screen readers etc.) for the Resync tabs button -->
<string name="resync_button_content_description">Sinkronizatu berriro</string>
<!-- Browser menu button that opens the find in page menu -->
<string name="browser_menu_find_in_page">Bilatu orrian</string>
<!-- Browser menu button that creates a private tab -->
@ -292,6 +294,8 @@
<string name="preferences_theme">Itxura</string>
<!-- Preference for customizing the home screen -->
<string name="preferences_home">Hasierako pantaila</string>
<!-- Preference for gestures based actions -->
<string name="preferences_gestures">Keinuak</string>
<!-- Preference for settings related to visual options -->
<string name="preferences_customize">Pertsonalizatu</string>
<!-- Preference description for banner about signing in -->
@ -326,9 +330,13 @@
<string name="preferences_search_browsing_history">Bilatu nabigatze-historia</string>
<!-- Preference title for switch preference to suggest bookmarks when searching -->
<string name="preferences_search_bookmarks">Bilatu laster-markak</string>
<!-- Preference title for switch preference to suggest synced tabs when searching -->
<string name="preferences_search_synced_tabs">Bilatu sinkronizatutako fitxak</string>
<!-- Preference for account settings -->
<string name="preferences_account_settings">Kontu-ezarpenak</string>
<!-- Preference for enabling url autocomplete-->
<string name="preferences_enable_autocomplete_urls">Osatu automatikoki URLak</string>
<!-- Preference for open links in third party apps -->
<string name="preferences_open_links_in_apps">Ireki loturak aplikazioetan</string>
@ -471,6 +479,9 @@
<!-- Preference for using following device theme -->
<string name="preference_follow_device_theme">Jarraitu gailuaren itxura</string>
<!-- Preference for using the dynamic toolbar -->
<string name="preference_gestures_dynamic_toolbar">Korritu tresna-barra ezkutatzeko</string>
<!-- Library -->
<!-- Option in Library to open Sessions page -->
<string name="library_sessions">Saioak</string>
@ -1061,10 +1072,6 @@
<string name="onboarding_account_sign_in_header">Hasi sinkronizatzen laster-markak, historia eta gehiago zure Firefox kontua erabiliz.</string>
<!-- Text for the button to learn more about signing in to your Firefox account -->
<string name="onboarding_manual_sign_in_learn_more">Argibide gehiago</string>
<!-- text for the firefox account onboarding card header when we detect you're already signed in to
another Firefox browser. (The word `Firefox` should not be translated)
The first parameter is the email of the detected user's account -->
<string name="onboarding_firefox_account_auto_signin_header_2">%s bezala saioa hasita duzu telefono honetako beste Firefox nabigatzaile batean. Kontu honekin saioa hasi nahi duzu?</string>
<!-- text for the button to confirm automatic sign-in -->
<string name="onboarding_firefox_account_auto_signin_confirm">Bai, hasi saioa</string>
<!-- text for the automatic sign-in button while signing in is in process -->
@ -1308,9 +1315,6 @@
<!-- Placeholder text for the TextView in the Add to Homescreen dialog -->
<string name="add_to_homescreen_text_placeholder">Lasterbidearen izena</string>
<!-- Describes the add to homescreen functionality -->
<string name="add_to_homescreen_description">Modu errazean gehi dezakezu webgune hau zure telefonoaren hasierako pantailan berehalako sarbidea izan eta aplikazio-moduko esperientziarekin azkarrago nabigatzeko.</string>
<!-- Preference for managing the settings for logins and passwords in Fenix -->
<string name="preferences_passwords_logins_and_passwords">Saio-hasierak eta pasahitzak</string>
<!-- Preference for managing the saving of logins and passwords in Fenix -->
@ -1537,7 +1541,7 @@
<string name="saved_login_duplicate">Erabiltzaile-izen hori badago lehendik ere</string>
<!-- Synced Tabs -->
<!-- Text displayed to ask user to connect another device as no devices found with account -->
<!-- Text displayed to ask user to connect another device as no devices found with account -->
<string name="synced_tabs_connect_another_device">Konektatu beste gailu bat.</string>
<!-- Text displayed asking user to re-authenticate -->
<string name="synced_tabs_reauth">Autentifikatu berriro mesedez.</string>
@ -1557,8 +1561,6 @@
<!-- Top Sites -->
<!-- Title text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_title">Gune erabilienen mugara iritsi da</string>
<!-- Content description text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_content">Gune erabilienetako bat gehitzeko, kendu aurretik dagoen bat. Sakatu eta mantendu gunea eta hautatu kentzeko aukera.</string>
<!-- Confirmation dialog button text when top sites limit is reached. -->
<string name="top_sites_max_limit_confirmation_button">Ados, ulertuta</string>
@ -1568,7 +1570,7 @@
<!-- Content description for close button in collection placeholder. -->
<string name="remove_home_collection_placeholder_content_description">Kendu</string>
<!-- depcrecated: text for the firefox account onboarding card header
<!-- Deprecated: text for the firefox account onboarding card header
The first parameter is the name of the app (e.g. Firefox Preview) -->
<string name="onboarding_firefox_account_header">Atera %s(r)i ahalik eta zuku gehiena.</string>
@ -1576,4 +1578,9 @@
<string name="no_collections_header1">Bildu zuretzat garrantzizkoa dena</string>
<!-- Deprecated: Label to describe what collections are to a new user without any collections -->
<string name="no_collections_description1">Multzokatu antzerako bilaketak, guneak eta fitxak sarbide azkarrago baterako.</string>
<!-- Deprecated: text for the firefox account onboarding card header when we detect you're already signed in to -->
<string name="onboarding_firefox_account_auto_signin_header_2">%s bezala saioa hasita duzu telefono honetako beste Firefox nabigatzaile batean. Kontu honekin saioa hasi nahi duzu?</string>
<!-- Deprecated: Describes the add to homescreen functionality -->
<string name="add_to_homescreen_description">Modu errazean gehi dezakezu webgune hau zure telefonoaren hasierako pantailan berehalako sarbidea izan eta aplikazio-moduko esperientziarekin azkarrago nabigatzeko.</string>
</resources>

@ -1199,6 +1199,8 @@ Cependant, il peut être moins stable. Téléchargez la version bêta de notre n
<string name="sign_in_with_camera">Connectez-vous avec votre appareil photo</string>
<!-- Text shown for settings option for sign with email -->
<string name="sign_in_with_email">Utiliser plutôt une adresse électronique</string>
<!-- Text shown for settings option for create new account text.'Firefox' intentionally hardcoded here.-->
<string name="sign_in_create_account_text"><![CDATA[Vous navez pas de compte ? <u>Créez-en un</u> pour synchroniser Firefox entre vos appareils.]]></string>
<!-- Text shown in confirmation dialog to sign out of account -->
<string name="sign_out_confirmation_message">Firefox ne se synchronisera plus avec votre compte, mais ne supprimera aucune de vos données de navigation sur cet appareil.</string>
<!-- Text shown in confirmation dialog to sign out of account. The first parameter is the name of the app (e.g. Firefox Preview) -->
@ -1306,6 +1308,11 @@ Cependant, il peut être moins stable. Téléchargez la version bêta de notre n
The first parameter is the app name -->
<string name="open_source_licenses_title">%s | Bibliothèques open source</string>
<!-- Category of trackers (redirect trackers) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_title">Traqueurs par redirection</string>
<!-- Description of redirect tracker cookies that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_description">Efface les cookies définis par redirection vers des sites web connus pour le pistage.</string>
<!-- About page link text to open support link -->
<string name="about_support">Assistance</string>
<!-- About page link text to list of past crashes (like about:crashes on desktop) -->

@ -1163,6 +1163,8 @@
<string name="sign_in_with_camera">Meld jo oan mei jo kamera</string>
<!-- Text shown for settings option for sign with email -->
<string name="sign_in_with_email">E-mail brûke</string>
<!-- Text shown for settings option for create new account text.'Firefox' intentionally hardcoded here.-->
<string name="sign_in_create_account_text"><![CDATA[Gjin account? <u>Meitsje der ien oan</u> om Firefox tusken apparaten te syngronisearjen.]]></string>
<!-- Text shown in confirmation dialog to sign out of account -->
<string name="sign_out_confirmation_message">Firefox stoppet de syngronisaasje mei jo account, mar sil gjin sneupgegevens op dit apparaat fuortsmite.</string>
<!-- Text shown in confirmation dialog to sign out of account. The first parameter is the name of the app (e.g. Firefox Preview) -->
@ -1271,6 +1273,11 @@
The first parameter is the app name -->
<string name="open_source_licenses_title">%s | OSS-biblioteken</string>
<!-- Category of trackers (redirect trackers) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_title">Trochliedingstrackers</string>
<!-- Description of redirect tracker cookies that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_description">Wisket cookies dy\'t ynsteld binne troch trochliedingen nei bekende trackingwebsites.</string>
<!-- About page link text to open support link -->
<string name="about_support">Stipe</string>
<!-- About page link text to list of past crashes (like about:crashes on desktop) -->

@ -1171,6 +1171,8 @@
<string name="sign_in_with_camera">Prijavi se pomoću kamere</string>
<!-- Text shown for settings option for sign with email -->
<string name="sign_in_with_email">Umjesto toga koristi e-poštu</string>
<!-- Text shown for settings option for create new account text.'Firefox' intentionally hardcoded here.-->
<string name="sign_in_create_account_text"><![CDATA[Nemaš račun? <u>Stvori ga</u> i sinkroniziraj Firefox među uređajima.]]></string>
<!-- Text shown in confirmation dialog to sign out of account -->
<string name="sign_out_confirmation_message">Firefox će prestati sinkronizirati s tvojim računom, ali neće izbrisati podatke o tvom pregledavanju na ovom uređaju.</string>
<!-- Text shown in confirmation dialog to sign out of account. The first parameter is the name of the app (e.g. Firefox Preview) -->
@ -1281,6 +1283,11 @@
The first parameter is the app name -->
<string name="open_source_licenses_title">%s | OSS biblioteke</string>
<!-- Category of trackers (redirect trackers) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_title">Pratitelji preusmjeravanja</string>
<!-- Description of redirect tracker cookies that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_description">Čisti kolačiće koje postavljaju preusmjeravanja na poznate stranice za praćenje.</string>
<!-- About page link text to open support link -->
<string name="about_support">Podrška</string>
<!-- About page link text to list of past crashes (like about:crashes on desktop) -->

@ -147,6 +147,8 @@
<string name="browser_menu_install_on_homescreen">Pasang</string>
<!-- Menu option on the toolbar that takes you to synced tabs page-->
<string name="synced_tabs">Tab yang disinkronkan</string>
<!-- Content description (not visible, for screen readers etc.) for the Resync tabs button -->
<string name="resync_button_content_description">Sinkron ulang</string>
<!-- Browser menu button that opens the find in page menu -->
<string name="browser_menu_find_in_page">Temukan di laman</string>
<!-- Browser menu button that creates a private tab -->
@ -275,6 +277,8 @@
<string name="preferences_open_links_in_a_private_tab">Buka tautan di tab pribadi</string>
<!-- Preference for allowing screenshots to be taken while in a private tab-->
<string name="preferences_allow_screenshots_in_private_mode">Izinkan tangkapan layar di penjelajahan pribadi</string>
<!-- Will inform the user of the risk of activating Allow screenshots in private browsing option -->
<string name="preferences_screenshots_in_private_mode_disclaimer">Jika diizinkan, tab pribadi juga akan terlihat saat beberapa aplikasi terbuka</string>
<!-- Preference for adding private browsing shortcut -->
<string name="preferences_add_private_browsing_shortcut">Tambahkan pintasan penjelajahan pribadi</string>
<!-- Preference for accessibility -->
@ -295,6 +299,8 @@
<string name="preferences_theme">Tema</string>
<!-- Preference for customizing the home screen -->
<string name="preferences_home">Beranda</string>
<!-- Preference for gestures based actions -->
<string name="preferences_gestures">Gestur</string>
<!-- Preference for settings related to visual options -->
<string name="preferences_customize">Ubahsuai</string>
<!-- Preference description for banner about signing in -->
@ -329,9 +335,13 @@
<string name="preferences_search_browsing_history">Cari riwayat jelajah</string>
<!-- Preference title for switch preference to suggest bookmarks when searching -->
<string name="preferences_search_bookmarks">Cari markah</string>
<!-- Preference title for switch preference to suggest synced tabs when searching -->
<string name="preferences_search_synced_tabs">Cari tab tersinkron</string>
<!-- Preference for account settings -->
<string name="preferences_account_settings">Setelan akun</string>
<!-- Preference for enabling url autocomplete-->
<string name="preferences_enable_autocomplete_urls">URL lengkapi-otomatis</string>
<!-- Preference for open links in third party apps -->
<string name="preferences_open_links_in_apps">Buka tautan di aplikasi</string>
@ -474,6 +484,16 @@
<!-- Preference for using following device theme -->
<string name="preference_follow_device_theme">Ikuti tema peranti</string>
<!-- Gestures Preferences-->
<!-- Preferences for using pull to refresh in a webpage -->
<string name="preference_gestures_website_pull_to_refresh">Tarik untuk menyegarkan</string>
<!-- Preference for using the dynamic toolbar -->
<string name="preference_gestures_dynamic_toolbar">Gulir untuk menyembunyikan bilah alat</string>
<!-- Preference for switching tabs by swiping horizontally on the toolbar -->
<string name="preference_gestures_swipe_toolbar_switch_tabs">Geser bilah alat ke samping untuk beralih tab</string>
<!-- Preference for showing the opened tabs by swiping up on the toolbar-->
<string name="preference_gestures_swipe_toolbar_show_tabs">Geser bilah alat ke atas untuk membuka tab</string>
<!-- Library -->
<!-- Option in Library to open Sessions page -->
<string name="library_sessions">Sesi</string>
@ -1072,7 +1092,7 @@
<!-- text for the firefox account onboarding card header when we detect you're already signed in to
another Firefox browser. (The word `Firefox` should not be translated)
The first parameter is the email of the detected user's account -->
<string name="onboarding_firefox_account_auto_signin_header_2">Anda masuk sebagai %s di peramban Firefox lainnya pada ponsel ini. Apakah Anda ingin masuk dengan akun tersebut?</string>
<string name="onboarding_firefox_account_auto_signin_header_3">Anda masuk sebagai %s di peramban Firefox lainnya pada perangkat ini. Apakah Anda ingin masuk dengan akun tersebut?</string>
<!-- text for the button to confirm automatic sign-in -->
<string name="onboarding_firefox_account_auto_signin_confirm">Ya, masukkan saya</string>
<!-- text for the automatic sign-in button while signing in is in process -->
@ -1161,6 +1181,8 @@
<string name="sign_in_with_camera">Masuk dengan kamera Anda</string>
<!-- Text shown for settings option for sign with email -->
<string name="sign_in_with_email">Gunakan surel saja</string>
<!-- Text shown for settings option for create new account text.'Firefox' intentionally hardcoded here.-->
<string name="sign_in_create_account_text"><![CDATA[Tidak ada akun? <u>Buat satu</u> untuk menyinkronkan Firefox antar perangkat.]]></string>
<!-- Text shown in confirmation dialog to sign out of account -->
<string name="sign_out_confirmation_message">Firefox akan berhenti menyinkronkan dengan akun Anda, tetapi tidak akan menghapus data penjelajahan Anda di peranti ini.</string>
<!-- Text shown in confirmation dialog to sign out of account. The first parameter is the name of the app (e.g. Firefox Preview) -->
@ -1269,6 +1291,11 @@
The first parameter is the app name -->
<string name="open_source_licenses_title">%s | Pustaka OSS</string>
<!-- Category of trackers (redirect trackers) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_title">Alihkan Pelacak</string>
<!-- Description of redirect tracker cookies that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_description">Menghapus kuki yang ditetapkan oleh pengalihan ke situs web pelacakan yang dikenal.</string>
<!-- About page link text to open support link -->
<string name="about_support">Dukungan</string>
<!-- About page link text to list of past crashes (like about:crashes on desktop) -->
@ -1315,7 +1342,7 @@
<string name="add_to_homescreen_text_placeholder">Nama pintasan</string>
<!-- Describes the add to homescreen functionality -->
<string name="add_to_homescreen_description">Anda dapat dengan mudah menambahkan situs web ini ke layar Beranda ponsel Anda untuk mendapatkan akses instan dan penjelajahan lebih cepat seperti menggunakan aplikasi.</string>
<string name="add_to_homescreen_description_2">Anda dapat dengan mudah menambahkan situs web ini ke layar Beranda perangkat Anda untuk mendapatkan akses instan dan penjelajahan lebih cepat seperti menggunakan aplikasi.</string>
<!-- Preference for managing the settings for logins and passwords in Fenix -->
<string name="preferences_passwords_logins_and_passwords">Info masuk dan kata sandi</string>
@ -1385,8 +1412,12 @@
<string name="logins_site_copied">Situs disalin ke clipboard</string>
<!-- Content Description (for screenreaders etc) read for the button to copy a password in logins-->
<string name="saved_logins_copy_password">Salin sandi</string>
<!-- Content Description (for screenreaders etc) read for the button to clear a password while editing a login-->
<string name="saved_logins_clear_password">Hapus sandi</string>
<!-- Content Description (for screenreaders etc) read for the button to copy a username in logins -->
<string name="saved_login_copy_username">Salin nama pengguna</string>
<!-- Content Description (for screenreaders etc) read for the button to clear a username while editing a login -->
<string name="saved_login_clear_username">Hapus nama pengguna</string>
<!-- Content Description (for screenreaders etc) read for the button to copy a site in logins -->
<string name="saved_login_copy_site">Salin situs</string>
<!-- Content Description (for screenreaders etc) read for the button to open a site in logins -->
@ -1541,7 +1572,7 @@
<string name="saved_login_duplicate">Sebuah info masuk dengan nama pengguna tersebut sudah ada</string>
<!-- Synced Tabs -->
<!-- Text displayed to ask user to connect another device as no devices found with account -->
<!-- Text displayed to ask user to connect another device as no devices found with account -->
<string name="synced_tabs_connect_another_device">Hubungkan perangkat lain</string>
<!-- Text displayed asking user to re-authenticate -->
<string name="synced_tabs_reauth">Harap autentikasi ulang.</string>
@ -1562,7 +1593,7 @@
<!-- Title text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_title">Batas situs teratas tercapai</string>
<!-- Content description text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_content">Untuk menambahkan situs teratas, hapus salah satu. Tekan lama situs yang dimaksud dan pilih hapus.</string>
<string name="top_sites_max_limit_content_2">Untuk menambahkan situs teratas, hapus salah satu. Tekan lama situs yang dimaksud dan pilih hapus.</string>
<!-- Confirmation dialog button text when top sites limit is reached. -->
<string name="top_sites_max_limit_confirmation_button">Oke, Paham!</string>
@ -1572,7 +1603,7 @@
<!-- Content description for close button in collection placeholder. -->
<string name="remove_home_collection_placeholder_content_description">Hapus</string>
<!-- depcrecated: text for the firefox account onboarding card header
<!-- Deprecated: text for the firefox account onboarding card header
The first parameter is the name of the app (e.g. Firefox Preview) -->
<string name="onboarding_firefox_account_header">Dapatkan hasil maksimal dari %s.</string>
@ -1580,4 +1611,9 @@
<string name="no_collections_header1">Kumpulkan hal-hal yang penting bagi Anda</string>
<!-- Deprecated: Label to describe what collections are to a new user without any collections -->
<string name="no_collections_description1">Kelompokkan pencarian, situs, dan tab yang serupa agar dapat diakses cepat nanti.</string>
<!-- Deprecated: text for the firefox account onboarding card header when we detect you're already signed in to -->
<string name="onboarding_firefox_account_auto_signin_header_2">Anda masuk sebagai %s di peramban Firefox lainnya pada ponsel ini. Apakah Anda ingin masuk dengan akun tersebut?</string>
<!-- Deprecated: Describes the add to homescreen functionality -->
<string name="add_to_homescreen_description">Anda dapat dengan mudah menambahkan situs web ini ke layar Beranda ponsel Anda untuk mendapatkan akses instan dan penjelajahan lebih cepat seperti menggunakan aplikasi.</string>
</resources>

@ -1198,6 +1198,8 @@
<string name="sign_in_with_camera">Accedi con la fotocamera</string>
<!-- Text shown for settings option for sign with email -->
<string name="sign_in_with_email">Accedi con lemail</string>
<!-- Text shown for settings option for create new account text.'Firefox' intentionally hardcoded here.-->
<string name="sign_in_create_account_text"><![CDATA[Non hai laccount? <u>Creane uno</u> per sincronizzare Firefox tra vari dispositivi.]]></string>
<!-- Text shown in confirmation dialog to sign out of account -->
<string name="sign_out_confirmation_message">I dati di navigazione non verranno più sincronizzati con laccount Firefox, ma non saranno rimossi da questo dispositivo.</string>
<!-- Text shown in confirmation dialog to sign out of account. The first parameter is the name of the app (e.g. Firefox Preview) -->
@ -1305,6 +1307,12 @@
The first parameter is the app name -->
<string name="open_source_licenses_title">%s | Librerie OSS</string>
<!-- Category of trackers (redirect trackers) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_title">Traccianti di reindirizzamento</string>
<!-- Description of redirect tracker cookies that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_description">Elimina i cookie impostati per reindirizzare a siti web noti per il tracciamento.</string>
<!-- About page link text to open support link -->
<string name="about_support">Supporto</string>
<!-- About page link text to list of past crashes (like about:crashes on desktop) -->

@ -311,7 +311,7 @@
<!-- Preference category for developer tools -->
<string name="developer_tools_category">შემმუშავებლის ხელსაწყოები</string>
<!-- Preference for developers -->
<string name="preferences_remote_debugging">დაშორებული გამართვა USB-ით</string>
<string name="preferences_remote_debugging">დაშორებული USB-გამართვა</string>
<!-- Preference title for switch preference to show search engines -->
<string name="preferences_show_search_engines">საძიებო სისტემების ჩვენება</string>
@ -336,7 +336,7 @@
<!-- Preference for open links in third party apps -->
<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>
<string name="preferences_external_download_manager">გარეშე მმართველი ჩამოტვირთვების</string>
<!-- Preference for add_ons -->
<string name="preferences_addons">დამატებები</string>
@ -992,7 +992,7 @@
<!-- Subtitle for the cookies item in Delete browsing data -->
<string name="preferences_delete_browsing_data_cookies_subtitle">გამოხვალთ ანგარიშებიდან უმეტეს საიტზე</string>
<!-- Title for the cached images and files item in Delete browsing data -->
<string name="preferences_delete_browsing_data_cached_files">შენახული სურათებისა და ფაილების ასლებ</string>
<string name="preferences_delete_browsing_data_cached_files">სურათები და ფაილების კეშ</string>
<!-- Subtitle for the cached images and files item in Delete browsing data -->
<string name="preferences_delete_browsing_data_cached_files_subtitle">გაათავისუფლებს მეხსიერებას</string>
<!-- Title for the site permissions item in Delete browsing data -->
@ -1163,6 +1163,8 @@
<string name="sign_in_with_camera">შედით კამერის დახმარებით</string>
<!-- Text shown for settings option for sign with email -->
<string name="sign_in_with_email">ელფოსტის გამოყენება სანაცვლოდ</string>
<!-- Text shown for settings option for create new account text.'Firefox' intentionally hardcoded here.-->
<string name="sign_in_create_account_text"><![CDATA[არ გაქვთ ანგარიში? <u>შექმენით</u> და დაასინქრონეთ Firefox მოწყობილობებზე.]]></string>
<!-- Text shown in confirmation dialog to sign out of account -->
<string name="sign_out_confirmation_message">Firefox შეწყვეტს ამ ანგარიშის სინქრონიზაციას, მაგრამ ამ მოწყობილობიდან დათვალიერების მონაცემები არ წაიშლება.</string>
<!-- Text shown in confirmation dialog to sign out of account. The first parameter is the name of the app (e.g. Firefox Preview) -->
@ -1172,7 +1174,7 @@
<!-- Option to cancel signing out shown in confirmation dialog to sign out of account -->
<string name="sign_out_cancel">გაუქმება</string>
<!-- Error message snackbar shown after the user tried to select a default folder which cannot be altered -->
<string name="bookmark_cannot_edit_root">ნაგულისხმევი საქაღალდეების ჩასწორება ვერ ხერხდება</string>
<string name="bookmark_cannot_edit_root">ნაგულისხმევი საქაღალდეების ჩასწორება ვერ მოხერხდება</string>
<!-- Enhanced Tracking Protection -->
<!-- Link displayed in enhanced tracking protection panel to access tracking protection settings -->
@ -1273,6 +1275,11 @@
The first parameter is the app name -->
<string name="open_source_licenses_title">%s | OSS-ბიბლიოთეკები</string>
<!-- Category of trackers (redirect trackers) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_title">გადამმისამართებელი მეთვალყურეები</string>
<!-- Description of redirect tracker cookies that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_description">ასუფთავებს გადამისამართებით მიღებულ ფუნთუშებს, ცნობილი მეთვალყურეებისგან.</string>
<!-- About page link text to open support link -->
<string name="about_support">მხარდაჭერა</string>
<!-- About page link text to list of past crashes (like about:crashes on desktop) -->

@ -148,6 +148,8 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara
<string name="browser_menu_install_on_homescreen">Sebded</string>
<!-- Menu option on the toolbar that takes you to synced tabs page-->
<string name="synced_tabs">Iccaren yemtawin</string>
<!-- Content description (not visible, for screen readers etc.) for the Resync tabs button -->
<string name="resync_button_content_description">Ales amtawi</string>
<!-- Browser menu button that opens the find in page menu -->
<string name="browser_menu_find_in_page">Nadi deg usebter</string>
<!-- Browser menu button that creates a private tab -->
@ -270,6 +272,8 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara
<string name="preferences_open_links_in_a_private_tab">Ldi iseɣwan deg iccer uslig</string>
<!-- Preference for allowing screenshots to be taken while in a private tab-->
<string name="preferences_allow_screenshots_in_private_mode">Sireg tuṭṭfa n ugdil deg tunigin tusligt</string>
<!-- Will inform the user of the risk of activating Allow screenshots in private browsing option -->
<string name="preferences_screenshots_in_private_mode_disclaimer">Ma yella yettusireg, accaren usligen ad d-banen ula d nutni ma yili aṭas n yisnasen i yeldin</string>
<!-- Preference for adding private browsing shortcut -->
<string name="preferences_add_private_browsing_shortcut">Rnu anegzum i tunigin tusligin</string>
<!-- Preference for accessibility -->
@ -290,6 +294,8 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara
<string name="preferences_theme">Asentel</string>
<!-- Preference for customizing the home screen -->
<string name="preferences_home">Asebter agejdan</string>
<!-- Preference for gestures based actions -->
<string name="preferences_gestures">Isillifen</string>
<!-- Preference for settings related to visual options -->
<string name="preferences_customize">Sagen</string>
<!-- Preference description for banner about signing in -->
@ -324,9 +330,13 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara
<string name="preferences_search_browsing_history">Nadi azray n tunigin</string>
<!-- Preference title for switch preference to suggest bookmarks when searching -->
<string name="preferences_search_bookmarks">Nadi ticraḍ n yisebtar</string>
<!-- Preference title for switch preference to suggest synced tabs when searching -->
<string name="preferences_search_synced_tabs">Nadi accaren yemtawin</string>
<!-- Preference for account settings -->
<string name="preferences_account_settings">Iɣewwaṛen n umiḍan</string>
<!-- Preference for enabling url autocomplete-->
<string name="preferences_enable_autocomplete_urls">Tacaṛt tawurmant n URLs</string>
<!-- Preference for open links in third party apps -->
<string name="preferences_open_links_in_apps">Ldi iseɣwan deg isnasen</string>
@ -467,6 +477,17 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara
<!-- Preference for using following device theme -->
<string name="preference_follow_device_theme">Ḍfer asntel n ibnek</string>
<!-- Gestures Preferences-->
<!-- Preferences for using pull to refresh in a webpage -->
<string name="preference_gestures_website_pull_to_refresh">Zuɣer i usesfer</string>
<!-- Preference for using the dynamic toolbar -->
<string name="preference_gestures_dynamic_toolbar">Drurem i wakken ad teffreḍ agalis n yifecka</string>
<!-- Preference for switching tabs by swiping horizontally on the toolbar -->
<string name="preference_gestures_swipe_toolbar_switch_tabs">Err agalis n yifecka deg tama i ubeddel n waccaren</string>
<!-- Preference for showing the opened tabs by swiping up on the toolbar-->
<string name="preference_gestures_swipe_toolbar_show_tabs">Err agalis n yifecka deg usawen i twaledyawt n waccaren</string>
<!-- Library -->
<!-- Option in Library to open Sessions page -->
<string name="library_sessions">Tiɣimiyen</string>
@ -1057,6 +1078,10 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara
<string name="onboarding_account_sign_in_header">Bdu amtawi n ticraḍ, awalen uffiren, akked waṭas-nniḍen s umiḍan n Firefox.</string>
<!-- Text for the button to learn more about signing in to your Firefox account -->
<string name="onboarding_manual_sign_in_learn_more">Issin ugar</string>
<!-- text for the firefox account onboarding card header when we detect you're already signed in to
another Firefox browser. (The word `Firefox` should not be translated)
The first parameter is the email of the detected user's account -->
<string name="onboarding_firefox_account_auto_signin_header_3">Aql-ak·akem teqqneḍ d %s ɣef yiminig-nniḍen n Firefox s yibenk-a. Tebɣiḍ ad teqqneḍ s umiḍan-a?</string>
<!-- text for the button to confirm automatic sign-in -->
<string name="onboarding_firefox_account_auto_signin_confirm">Ih, qqen-iyi</string>
<!-- text for the automatic sign-in button while signing in is in process -->
@ -1148,6 +1173,8 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara
<string name="sign_in_with_camera">Qqen s tkamirat-ik</string>
<!-- Text shown for settings option for sign with email -->
<string name="sign_in_with_email">Seqdec tansa n yimayl deg umḍiq-is</string>
<!-- Text shown for settings option for create new account text.'Firefox' intentionally hardcoded here.-->
<string name="sign_in_create_account_text"><![CDATA[Ulac amiḍan? <u>Rnu yiwen</u> i umtaw n Firefox gar yibenkan.]]></string>
<!-- Text shown in confirmation dialog to sign out of account -->
<string name="sign_out_confirmation_message">Firefox ad iseḥbes amtawi d umiḍan-inek, acukan ur ittekkes ara isefka-inek n tunigin seg uselkim-a.</string>
<!-- Text shown in confirmation dialog to sign out of account. The first parameter is the name of the app (e.g. Firefox Preview) -->
@ -1258,6 +1285,12 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara
The first parameter is the app name -->
<string name="open_source_licenses_title">%s | timkerḍiyin OSS </string>
<!-- Category of trackers (redirect trackers) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_title">Welleh i yineḍfaren</string>
<!-- Description of redirect tracker cookies that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_description">Yesfaḍay inagan n tuqqna yettusbadun s yiwellhen n yismal web n uḍfar yettwassnen.</string>
<!-- About page link text to open support link -->
<string name="about_support">Tallelt</string>
<!-- About page link text to list of past crashes (like about:crashes on desktop) -->
@ -1302,6 +1335,9 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara
<!-- Placeholder text for the TextView in the Add to Homescreen dialog -->
<string name="add_to_homescreen_text_placeholder">Isem n unegzum</string>
<!-- Describes the add to homescreen functionality -->
<string name="add_to_homescreen_description_2">Tzemreḍ s sshal ad ternuḍ asmel-a web ɣer ugdil agejdan n yibenk-inek·inem i wakken ad tesɛuḍ anekcum askudan d tunigin taruradt s termt i icuban asnas.</string>
<!-- Preference for managing the settings for logins and passwords in Fenix -->
<string name="preferences_passwords_logins_and_passwords">Inekcam d wawalen uffiren</string>
<!-- Preference for managing the saving of logins and passwords in Fenix -->
@ -1374,6 +1410,8 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara
<string name="saved_logins_clear_password">Sfeḍ awal uffir</string>
<!-- Content Description (for screenreaders etc) read for the button to copy a username in logins -->
<string name="saved_login_copy_username">Nɣel isem n useqdac</string>
<!-- Content Description (for screenreaders etc) read for the button to clear a username while editing a login -->
<string name="saved_login_clear_username">Sfeḍ isem n useqdac</string>
<!-- Content Description (for screenreaders etc) read for the button to copy a site in logins -->
<string name="saved_login_copy_site">Nɣel asmel</string>
<!-- Content Description (for screenreaders etc) read for the button to open a site in logins -->
@ -1552,6 +1590,8 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara
<!-- Top Sites -->
<!-- Title text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_title">Tewwḍeḍ ɣer talast n usmel</string>
<!-- Content description text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_content_2">I tmerna n usmel afellay amaynut, kkes yiwen. Nnal ɣef usmel-nni war aḥbas syen fren &quot;kkes&quot;.</string>
<!-- Confirmation dialog button text when top sites limit is reached. -->
<string name="top_sites_max_limit_confirmation_button">IH, awi-t-id</string>
@ -1561,7 +1601,7 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara
<!-- Content description for close button in collection placeholder. -->
<string name="remove_home_collection_placeholder_content_description">Kkes</string>
<!-- depcrecated: text for the firefox account onboarding card header
<!-- Deprecated: text for the firefox account onboarding card header
The first parameter is the name of the app (e.g. Firefox Preview) -->
<string name="onboarding_firefox_account_header">Awi %s amaynut akk.</string>

@ -144,6 +144,8 @@
<string name="browser_menu_install_on_homescreen">Įdiegti</string>
<!-- Menu option on the toolbar that takes you to synced tabs page-->
<string name="synced_tabs">Sinchronizuotos kortelės</string>
<!-- Content description (not visible, for screen readers etc.) for the Resync tabs button -->
<string name="resync_button_content_description">Sinchronizuoti iš naujo</string>
<!-- Browser menu button that opens the find in page menu -->
<string name="browser_menu_find_in_page">Rasti tinklalapyje</string>
<!-- Browser menu button that creates a private tab -->
@ -325,6 +327,8 @@
<string name="preferences_search_browsing_history">Ieškoti naršymo žurnale</string>
<!-- Preference title for switch preference to suggest bookmarks when searching -->
<string name="preferences_search_bookmarks">Ieškoti adresyne</string>
<!-- Preference title for switch preference to suggest synced tabs when searching -->
<string name="preferences_search_synced_tabs">Ieškoti tarp sinchronizuotų kortelių</string>
<!-- Preference for account settings -->
<string name="preferences_account_settings">Paskyros nuostatos</string>
<!-- Preference for enabling url autocomplete-->
@ -1166,6 +1170,8 @@
<string name="sign_in_with_camera">Prisijunkite su savo kamera</string>
<!-- Text shown for settings option for sign with email -->
<string name="sign_in_with_email">Naudoti el. paštą</string>
<!-- Text shown for settings option for create new account text.'Firefox' intentionally hardcoded here.-->
<string name="sign_in_create_account_text"><![CDATA[Neturite paskyros? <u>Susikurkite</u>, norėdami sinchronizuoti „Firefox“ tarp įrenginių.]]></string>
<!-- Text shown in confirmation dialog to sign out of account -->
<string name="sign_out_confirmation_message">„Firefox“ nebesinchronizuos duomenų su jūsų paskyra, tačiau šiame įrenginyje esančių naršymo duomenų nepašalins.</string>
<!-- Text shown in confirmation dialog to sign out of account. The first parameter is the name of the app (e.g. Firefox Preview) -->
@ -1274,6 +1280,11 @@
The first parameter is the app name -->
<string name="open_source_licenses_title">„%s“ | Atvirosios bibliotekos</string>
<!-- Category of trackers (redirect trackers) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_title">Nukreipimų stebėjimo elementai</string>
<!-- Description of redirect tracker cookies that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_description">Išvalo slapukus, įrašomus nukreipiant į žinomas stebėjimo svetaines.</string>
<!-- About page link text to open support link -->
<string name="about_support">Žinynas</string>
<!-- About page link text to list of past crashes (like about:crashes on desktop) -->

@ -65,6 +65,7 @@
<color name="tab_tray_item_media_background_normal_theme">@color/tab_tray_item_media_background_dark_theme</color>
<color name="tab_tray_heading_icon_normal_theme">@color/tab_tray_heading_icon_dark_theme</color>
<color name="tab_tray_heading_icon_inactive_normal_theme">@color/tab_tray_heading_icon_inactive_dark_theme</color>
<color name="tab_tray_heading_icon_menu_normal_theme">@color/tab_tray_heading_icon_menu_dark_theme</color>
<color name="tab_tray_item_thumbnail_background_normal_theme">@color/tab_tray_item_thumbnail_background_dark_theme</color>
<color name="tab_tray_item_thumbnail_icon_normal_theme">@color/tab_tray_item_thumbnail_icon_dark_theme</color>
<color name="tab_tray_selected_mask_normal_theme">@color/tab_tray_selected_mask_dark_theme</color>

@ -1175,6 +1175,8 @@
<string name="sign_in_with_camera">Meld u aan met uw camera</string>
<!-- Text shown for settings option for sign with email -->
<string name="sign_in_with_email">E-mail gebruiken</string>
<!-- Text shown for settings option for create new account text.'Firefox' intentionally hardcoded here.-->
<string name="sign_in_create_account_text"><![CDATA[Geen account? <u>Maak er een aan</u> om Firefox tussen apparaten te synchroniseren.]]></string>
<!-- Text shown in confirmation dialog to sign out of account -->
<string name="sign_out_confirmation_message">Firefox stopt de synchronisatie met uw account, maar zal geen surfgegevens op dit apparaat verwijderen.</string>
<!-- Text shown in confirmation dialog to sign out of account. The first parameter is the name of the app (e.g. Firefox Preview) -->
@ -1282,6 +1284,11 @@
The first parameter is the app name -->
<string name="open_source_licenses_title">%s | OSS-bibliotheken</string>
<!-- Category of trackers (redirect trackers) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_title">Doorleidingstrackers</string>
<!-- Description of redirect tracker cookies that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_description">Wist cookies die zijn ingesteld door doorgeleidingen naar bekende trackingwebsites.</string>
<!-- About page link text to open support link -->
<string name="about_support">Ondersteuning</string>
<!-- About page link text to list of past crashes (like about:crashes on desktop) -->

@ -1171,6 +1171,8 @@
<string name="sign_in_with_camera">Zaloguj się za pomocą aparatu</string>
<!-- Text shown for settings option for sign with email -->
<string name="sign_in_with_email">Użyj adresu e-mail</string>
<!-- Text shown for settings option for create new account text.'Firefox' intentionally hardcoded here.-->
<string name="sign_in_create_account_text"><![CDATA[Nie masz konta? <u>Utwórz je</u>, aby synchronizować Firefoksa między urządzeniami.]]></string>
<!-- Text shown in confirmation dialog to sign out of account -->
<string name="sign_out_confirmation_message">Firefox zatrzyma synchronizację z tym kontem, ale nie usunie danych przeglądania na tym urządzeniu.</string>
<!-- Text shown in confirmation dialog to sign out of account. The first parameter is the name of the app (e.g. Firefox Preview) -->
@ -1279,6 +1281,11 @@
The first parameter is the app name -->
<string name="open_source_licenses_title">%s | Biblioteki open source</string>
<!-- Category of trackers (redirect trackers) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_title">Elementy śledzące przez przekierowania</string>
<!-- Description of redirect tracker cookies that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_description">Czyści ciasteczka ustawione przez przekierowania do znanych witryn śledzących użytkowników.</string>
<!-- About page link text to open support link -->
<string name="about_support">Pomoc</string>
<!-- About page link text to list of past crashes (like about:crashes on desktop) -->

@ -1167,6 +1167,8 @@
<string name="sign_in_with_camera">Inicie sessão com a sua câmara</string>
<!-- Text shown for settings option for sign with email -->
<string name="sign_in_with_email">Como alternativa, utilizar o e-mail</string>
<!-- Text shown for settings option for create new account text.'Firefox' intentionally hardcoded here.-->
<string name="sign_in_create_account_text"><![CDATA[Sem conta? <u>Crie uma conta</u> para sincronizar o Firefox entre os dispositivos.]]></string>
<!-- Text shown in confirmation dialog to sign out of account -->
<string name="sign_out_confirmation_message">O Firefox irá parar a sincronização com a sua conta, mas não irá apagar quaisquer dados de navegação seus neste dispositivo.</string>
<!-- Text shown in confirmation dialog to sign out of account. The first parameter is the name of the app (e.g. Firefox Preview) -->
@ -1277,6 +1279,11 @@
The first parameter is the app name -->
<string name="open_source_licenses_title">%s | Bibliotecas de código aberto</string>
<!-- Category of trackers (redirect trackers) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_title">Rastreadores de redirecionamento</string>
<!-- Description of redirect tracker cookies that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_description">Limpa as cookies definidas por redirecionamentos para sites de rastreamento conhecidos.</string>
<!-- About page link text to open support link -->
<string name="about_support">Apoio</string>
<!-- About page link text to list of past crashes (like about:crashes on desktop) -->

@ -142,6 +142,8 @@
<string name="browser_menu_install_on_homescreen">Installar</string>
<!-- Menu option on the toolbar that takes you to synced tabs page-->
<string name="synced_tabs">Tabs sincronisads</string>
<!-- Content description (not visible, for screen readers etc.) for the Resync tabs button -->
<string name="resync_button_content_description">Resincronisar</string>
<!-- Browser menu button that opens the find in page menu -->
<string name="browser_menu_find_in_page">Tschertgar en la pagina</string>
<!-- Browser menu button that creates a private tab -->
@ -263,6 +265,8 @@
<string name="preferences_open_links_in_a_private_tab">Avrir colliaziuns en in tab privat</string>
<!-- Preference for allowing screenshots to be taken while in a private tab-->
<string name="preferences_allow_screenshots_in_private_mode">Permetter maletgs dal visur en il modus privat</string>
<!-- Will inform the user of the risk of activating Allow screenshots in private browsing option -->
<string name="preferences_screenshots_in_private_mode_disclaimer">Sche permess, èn tabs privats era visibels en cas che pliras apps èn avertas</string>
<!-- Preference for adding private browsing shortcut -->
<string name="preferences_add_private_browsing_shortcut">Agiuntar ina scursanida al modus privat</string>
<!-- Preference for accessibility -->
@ -283,6 +287,8 @@
<string name="preferences_theme">Design</string>
<!-- Preference for customizing the home screen -->
<string name="preferences_home">Pagina iniziala</string>
<!-- Preference for gestures based actions -->
<string name="preferences_gestures">Gests</string>
<!-- Preference for settings related to visual options -->
<string name="preferences_customize">Persunalisar</string>
<!-- Preference description for banner about signing in -->
@ -318,8 +324,12 @@
<string name="preferences_search_browsing_history">Tschertgar en la cronologia da navigaziun</string>
<!-- Preference title for switch preference to suggest bookmarks when searching -->
<string name="preferences_search_bookmarks">Tschertgar en ils segnapaginas</string>
<!-- Preference title for switch preference to suggest synced tabs when searching -->
<string name="preferences_search_synced_tabs">Tschertgar en ils tabs sincronisads</string>
<!-- Preference for account settings -->
<string name="preferences_account_settings">Parameters dal conto</string>
<!-- Preference for enabling url autocomplete-->
<string name="preferences_enable_autocomplete_urls">Cumplettar automaticamain URLs</string>
<!-- Preference for open links in third party apps -->
<string name="preferences_open_links_in_apps">Avrir colliaziuns en apps</string>
<!-- Preference for open download with an external download manager app -->
@ -456,6 +466,16 @@
<!-- Preference for using following device theme -->
<string name="preference_follow_device_theme">Tenor il design tschernì sin l\'apparat</string>
<!-- Gestures Preferences-->
<!-- Preferences for using pull to refresh in a webpage -->
<string name="preference_gestures_website_pull_to_refresh">Trair per actualisar</string>
<!-- Preference for using the dynamic toolbar -->
<string name="preference_gestures_dynamic_toolbar">Scrollar per zuppentar la trav d\'utensils</string>
<!-- Preference for switching tabs by swiping horizontally on the toolbar -->
<string name="preference_gestures_swipe_toolbar_switch_tabs">Stritgar da la vart la trav d\'utensils per midar tab</string>
<!-- Preference for showing the opened tabs by swiping up on the toolbar-->
<string name="preference_gestures_swipe_toolbar_show_tabs">Stritgar ensi la trav d\'utensils per avrir tabs</string>
<!-- Library -->
<!-- Option in Library to open Sessions page -->
<string name="library_sessions">Sesidas</string>
@ -1045,7 +1065,7 @@
<!-- text for the firefox account onboarding card header when we detect you're already signed in to
another Firefox browser. (The word `Firefox` should not be translated)
The first parameter is the email of the detected user's account -->
<string name="onboarding_firefox_account_auto_signin_header_2">Ti es annunzià sco %s en in auter navigatur da Firefox sin quest telefonin. Vuls ti t\'annunziar cun quest conto?</string>
<string name="onboarding_firefox_account_auto_signin_header_3">Ti es annunzià sco %s en in auter navigatur da Firefox sin quest apparat. Vuls ti t\'annunziar cun quest conto?</string>
<!-- text for the button to confirm automatic sign-in -->
<string name="onboarding_firefox_account_auto_signin_confirm">Gea, m\'annunziar</string>
<!-- text for the automatic sign-in button while signing in is in process -->
@ -1137,6 +1157,8 @@
<string name="sign_in_with_camera">T\'annunzia cun tia camera</string>
<!-- Text shown for settings option for sign with email -->
<string name="sign_in_with_email">Utilisar l\'e-mail</string>
<!-- Text shown for settings option for create new account text.'Firefox' intentionally hardcoded here.-->
<string name="sign_in_create_account_text"><![CDATA[Nagin conto? <u>Creescha in</u> per sincronisar Firefox tranter differents apparats.]]></string>
<!-- Text shown in confirmation dialog to sign out of account -->
<string name="sign_out_confirmation_message">Firefox chala da sincronisar cun tes conto, ma las datas da navigaziun restan sin quest apparat.</string>
<!-- Text shown in confirmation dialog to sign out of account. The first parameter is the name of the app (e.g. Firefox Preview) -->
@ -1295,9 +1317,6 @@
<!-- Placeholder text for the TextView in the Add to Homescreen dialog -->
<string name="add_to_homescreen_text_placeholder">Num da la scursanida</string>
<!-- Describes the add to homescreen functionality -->
<string name="add_to_homescreen_description">Ti pos agiuntar a moda simpla questa website al visur da partenza da tes telefonin per avair access direct e navigar pli svelt, sco sch\'i fiss ina app.</string>
<!-- Preference for managing the settings for logins and passwords in Fenix -->
<string name="preferences_passwords_logins_and_passwords">Infurmaziuns d\'annunzia e pleds-clav</string>
<!-- Preference for managing the saving of logins and passwords in Fenix -->
@ -1526,7 +1545,7 @@
<string name="saved_login_duplicate">Datas d\'annunzia cun quest num d\'utilisader existan gia</string>
<!-- Synced Tabs -->
<!-- Text displayed to ask user to connect another device as no devices found with account -->
<!-- Text displayed to ask user to connect another device as no devices found with account -->
<string name="synced_tabs_connect_another_device">Colliar in auter apparat.</string>
<!-- Text displayed asking user to re-authenticate -->
<string name="synced_tabs_reauth">Re-autentifitgescha per plaschair.</string>
@ -1546,8 +1565,6 @@
<!-- Top Sites -->
<!-- Title text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_title">Cuntanschì la limita da paginas preferidas</string>
<!-- Content description text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_content">Per agiuntar ina nova pagina preferida stos ti l\'emprim allontanar in\'autra. Smatga ditg sin ina pagina e tscherna «Allontanar».</string>
<!-- Confirmation dialog button text when top sites limit is reached. -->
<string name="top_sites_max_limit_confirmation_button">OK, chapì</string>
@ -1557,7 +1574,7 @@
<!-- Content description for close button in collection placeholder. -->
<string name="remove_home_collection_placeholder_content_description">Allontanar</string>
<!-- depcrecated: text for the firefox account onboarding card header
<!-- Deprecated: text for the firefox account onboarding card header
The first parameter is the name of the app (e.g. Firefox Preview) -->
<string name="onboarding_firefox_account_header">Rabla ora il maximum da %s.</string>
@ -1565,4 +1582,9 @@
<string name="no_collections_header1">Rimna las chaussas che t\'èn impurtantas</string>
<!-- Deprecated: Label to describe what collections are to a new user without any collections -->
<string name="no_collections_description1">Gruppescha retschertgas, paginas e tabs sumegliants per pli tard pudair acceder pli svelt.</string>
</resources>
<!-- Deprecated: text for the firefox account onboarding card header when we detect you're already signed in to -->
<string name="onboarding_firefox_account_auto_signin_header_2">Ti es annunzià sco %s en in auter navigatur da Firefox sin quest telefonin. Vuls ti t\'annunziar cun quest conto?</string>
<!-- Deprecated: Describes the add to homescreen functionality -->
<string name="add_to_homescreen_description">Ti pos agiuntar a moda simpla questa website al visur da partenza da tes telefonin per avair access direct e navigar pli svelt, sco sch\'i fiss ina app.</string>
</resources>

@ -155,6 +155,8 @@
<string name="browser_menu_install_on_homescreen">Установить</string>
<!-- Menu option on the toolbar that takes you to synced tabs page-->
<string name="synced_tabs">Облачные вкладки</string>
<!-- Content description (not visible, for screen readers etc.) for the Resync tabs button -->
<string name="resync_button_content_description">Повторная синхронизация</string>
<!-- Browser menu button that opens the find in page menu -->
<string name="browser_menu_find_in_page">Найти на странице</string>
<!-- Browser menu button that creates a private tab -->
@ -1219,6 +1221,8 @@
<string name="sign_in_with_camera">Войти с помощью камеры</string>
<!-- Text shown for settings option for sign with email -->
<string name="sign_in_with_email">Использовать электронную почту</string>
<!-- Text shown for settings option for create new account text.'Firefox' intentionally hardcoded here.-->
<string name="sign_in_create_account_text"><![CDATA[Ещё нет аккаунта? <u>Создайте его</u>, чтобы синхронизировать Firefox между устройствами.]]></string>
<!-- Text shown in confirmation dialog to sign out of account -->
<string name="sign_out_confirmation_message">Firefox прекратит синхронизацию с вашим аккаунтом, но не будет удалять ничего из ваших данных веб-сёрфинга на этом устройстве.</string>
<!-- Text shown in confirmation dialog to sign out of account. The first parameter is the name of the app (e.g. Firefox Preview) -->
@ -1326,6 +1330,11 @@
The first parameter is the app name -->
<string name="open_source_licenses_title">%s | Свободные библиотеки</string>
<!-- Category of trackers (redirect trackers) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_title">Трекеры перенаправлений</string>
<!-- Description of redirect tracker cookies that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_description">Удаляет куки, установленные в ходе перенаправлений на известные веб-сайты с отслеживанием.</string>
<!-- About page link text to open support link -->
<string name="about_support">Поддержка</string>
<!-- About page link text to list of past crashes (like about:crashes on desktop) -->

@ -143,6 +143,8 @@
<string name="browser_menu_install_on_homescreen">Instaloje</string>
<!-- Menu option on the toolbar that takes you to synced tabs page-->
<string name="synced_tabs">Skeda të njëkohësuara</string>
<!-- Content description (not visible, for screen readers etc.) for the Resync tabs button -->
<string name="resync_button_content_description">Rinjëkohëso</string>
<!-- Browser menu button that opens the find in page menu -->
<string name="browser_menu_find_in_page">Gjej në faqe</string>
<!-- Browser menu button that creates a private tab -->
@ -324,6 +326,8 @@
<string name="preferences_search_browsing_history">Kërkoni në historik shfletimi</string>
<!-- Preference title for switch preference to suggest bookmarks when searching -->
<string name="preferences_search_bookmarks">Kërkoni te faqerojtësit</string>
<!-- Preference title for switch preference to suggest synced tabs when searching -->
<string name="preferences_search_synced_tabs">Kërko në skeda të njëkohësuara</string>
<!-- Preference for account settings -->
<string name="preferences_account_settings">Rregullime llogarie</string>
<!-- Preference for enabling url autocomplete-->
@ -1158,6 +1162,8 @@
<string name="sign_in_with_camera">Hyni me kamerën tuaj</string>
<!-- Text shown for settings option for sign with email -->
<string name="sign_in_with_email">Më mirë përdorni email</string>
<!-- Text shown for settings option for create new account text.'Firefox' intentionally hardcoded here.-->
<string name="sign_in_create_account_text"><![CDATA[Pa llogari? <u>Krijoni një të tillë</u> që të njëkohësoni Firefox-in mes pajisjesh.]]></string>
<!-- Text shown in confirmation dialog to sign out of account -->
<string name="sign_out_confirmation_message">Firefox-i do të reshtë së njëkohësuari me llogarinë tuaj, por sdo të fshijë ndonjë nga të dhënat e shfletimit tuaj në këtë pajisje.</string>
<!-- Text shown in confirmation dialog to sign out of account. The first parameter is the name of the app (e.g. Firefox Preview) -->
@ -1267,6 +1273,11 @@
The first parameter is the app name -->
<string name="open_source_licenses_title">%s | Biblioteka OSS</string>
<!-- Category of trackers (redirect trackers) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_title">Gjurmues Ridrejtimesh</string>
<!-- Description of redirect tracker cookies that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_description">Spastron cookies të depozituara nga ridrejtime për te sajte të njohur për gjurmim.</string>
<!-- About page link text to open support link -->
<string name="about_support">Asistencë</string>
<!-- About page link text to list of past crashes (like about:crashes on desktop) -->

@ -1164,6 +1164,8 @@
<string name="sign_in_with_camera">Пријава преко камере</string>
<!-- Text shown for settings option for sign with email -->
<string name="sign_in_with_email">Користи адресу е-поште</string>
<!-- Text shown for settings option for create new account text.'Firefox' intentionally hardcoded here.-->
<string name="sign_in_create_account_text"><![CDATA[Немате налог? <u>Направите га</u> за синхронизацију Firefox-а међу уређајима.]]></string>
<!-- Text shown in confirmation dialog to sign out of account -->
<string name="sign_out_confirmation_message">Firefox ће престати са снихронизацијом вашег налога али неће обрисати ваше податке прегледања на овом уређају.</string>
<!-- Text shown in confirmation dialog to sign out of account. The first parameter is the name of the app (e.g. Firefox Preview) -->
@ -1277,6 +1279,11 @@
The first parameter is the app name -->
<string name="open_source_licenses_title">%s | библиотеке отвореног кода</string>
<!-- Category of trackers (redirect trackers) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_title">Преусмерите пратиоце</string>
<!-- Description of redirect tracker cookies that can be blocked by Enhanced Tracking Protection -->
<string name="etp_redirect_trackers_description">Брише колачиће постављене преусмеравањем на познате страница за праћење.</string>
<!-- About page link text to open support link -->
<string name="about_support">Подршка</string>
<!-- About page link text to list of past crashes (like about:crashes on desktop) -->

@ -832,6 +832,9 @@
<!-- Title for the cookies item in Delete browsing data -->
<string name="preferences_delete_browsing_data_cookies">Кукиҳо</string>
<!-- Title for the Delete browsing data on quit preference -->
<string name="preferences_delete_browsing_data_on_quit">Нест кардани маълумоти баррасӣ ҳангоми баромад</string>
<!-- Text for the cancel button for the data deletion dialog -->
<string name="delete_browsing_data_prompt_cancel">Бекор кардан</string>
<!-- Text for the allow button for the data deletion dialog -->

@ -76,6 +76,19 @@
<!-- Text for the negative button -->
<string name="search_widget_cfr_neg_button_text">ابھی نہیں</string>
<!-- Text for the positive action button -->
<string name="open_in_app_cfr_positive_button_text">سیٹنگز پر جائیں</string>
<!-- Text for the negative action button -->
<string name="open_in_app_cfr_negative_button_text">برخاست کریں</string>
<!-- Text for the positive action button to go to Android Settings to grant permissions. -->
<string name="camera_permissions_needed_positive_button_text">سیٹنگز پر جائیں</string>
<!-- Text for the negative action button to dismiss the dialog. -->
<string name="camera_permissions_needed_negative_button_text">برخاست کریں</string>
<!-- Text for the negative action button to dismiss the Close Tabs Banner. -->
<string name="tab_tray_close_tabs_banner_negative_button_text">برخاست کریں</string>
<!-- Home screen icons - Long press shortcuts -->
<!-- Shortcut action to open new tab -->
<string name="home_screen_shortcut_open_new_tab_2">نیا ٹیب</string>
@ -472,6 +485,10 @@
<!-- Content description (not visible, for screen readers etc.): "Close button for library settings" -->
<string name="content_description_close_button">بند کریں</string>
<!-- Tab Management -->
<!-- Title of preference that allows a user to auto close tabs after a specified amount of time -->
<string name="preferences_close_tabs">ٹیب بند کریں</string>
<!-- Sessions -->
<!-- Title for the list of tabs -->
<string name="tab_header_label">ٹیبس کھولیں</string>
@ -502,9 +519,9 @@
<!-- Content description (not visible, for screen readers etc.): Removes tab from collection button. Removes the selected tab from collection when pressed -->
<string name="remove_tab_from_collection">مجموعہ سے ٹیب کو ہٹا دیں</string>
<!-- Content description (not visible, for screen readers etc.): Close tab button. Closes the current session when pressed -->
<string name="close_tab">ٹیب بند کر دیں</string>
<string name="close_tab">ٹیب بند کریں</string>
<!-- Content description (not visible, for screen readers etc.): Close tab <title> button. First parameter is tab title -->
<string name="close_tab_title">%s ٹیب بند کر دیں</string>
<string name="close_tab_title">%s ٹیب بند کریں</string>
<!-- Content description (not visible, for screen readers etc.): Opens the open tabs menu when pressed -->
<string name="open_tabs_menu">ٹیبز کا مینو کھولیں</string>
<!-- Open tabs menu item to close all tabs -->
@ -594,7 +611,7 @@
<!-- Send crash report checkbox text on the tab crash page -->
<string name="tab_crash_send_report">تباہی کی رپورٹ Mozilla کو ارسال کریں</string>
<!-- Close tab button text on the tab crash page -->
<string name="tab_crash_close">ٹیب بند کر دیں</string>
<string name="tab_crash_close">ٹیب بند کریں</string>
<!-- Restore tab button text on the tab crash page -->
<string name="tab_crash_restore">ٹیب بحال کریں</string>
@ -743,10 +760,6 @@
<!-- Content description (not visible, for screen readers etc.): Opens the collection menu when pressed -->
<string name="collection_menu_button_content_description">مجموعہ کا مینو</string>
<!-- No Open Tabs Message Header -->
<string name="no_collections_header1">آپ کے لئے اہم چیزیں جمع کریں</string>
<!-- Label to describe what collections are to a new user without any collections -->
<string name="no_collections_description1">ایک طرح کی تلاشیں ، سائٹیں اور ٹیبز کو بعد میں فوری رسائی کے لئے گروہ بندی کریں۔</string>
<!-- Title for the "select tabs" step of the collection creator -->
<string name="create_collection_select_tabs">ٹیب کو منتخب کریں</string>
<!-- Title for the "select collection" step of the collection creator -->
@ -983,8 +996,8 @@
<string name="onboarding_whats_new_header1">دیکھیں نیا کیا ہے</string>
<!-- text for underlined clickable link that is part of "what's new" onboarding card description that links to an FAQ -->
<string name="onboarding_whats_new_description_linktext">یہاں جوابات حاصل کریں</string>
<!-- text for the firefox account onboarding card header -->
<string name="onboarding_firefox_account_header">%s کا زیادہ سے زیادہ فائدہ اٹھائیں۔</string>
<!-- Text for the button to learn more about signing in to your Firefox account -->
<string name="onboarding_manual_sign_in_learn_more">مزید سیکھیں</string>
<!-- text for the button to confirm automatic sign-in -->
<string name="onboarding_firefox_account_auto_signin_confirm">ہاں ، مجھے سائن ان کریں</string>
<!-- text for the automatic sign-in button while signing in is in process -->
@ -1427,7 +1440,7 @@
<string name="saved_login_duplicate">اس صارف نام کے ساتھ لاگ ان پہلے سے موجود ہے</string>
<!-- Synced Tabs -->
<!-- Text displayed to ask user to connect another device as no devices found with account -->
<!-- Text displayed to ask user to connect another device as no devices found with account -->
<string name="synced_tabs_connect_another_device">ایک اور آلہ جوڑیں۔</string>
<!-- Text displayed asking user to re-authenticate -->
<string name="synced_tabs_reauth">برائے مہربانی دوبارہ توثیق کریں۔</string>
@ -1446,9 +1459,18 @@
<!-- Top Sites -->
<!-- Title text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_title">اعلٰی سائٹ کی حد پوری ہو گئی </string>
<!-- Content description text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_content">نئی ٹاپ سائٹ شامل کرنے کے لئے ، ایک سائٹ کو ہٹائیں۔ سائٹ پر دیر تک دبائیں اور ہٹانا منتخب کریں۔</string>
<!-- Confirmation dialog button text when top sites limit is reached. -->
<string name="top_sites_max_limit_confirmation_button">ٹھیک ہے سمجھ گیا</string>
</resources>
<!-- Content description for close button in collection placeholder. -->
<string name="remove_home_collection_placeholder_content_description">ہٹائیں</string>
<!-- Deprecated: text for the firefox account onboarding card header
The first parameter is the name of the app (e.g. Firefox Preview) -->
<string name="onboarding_firefox_account_header">%s کا زیادہ سے زیادہ فائدہ اٹھائیں۔</string>
<!-- Deprecated: No Open Tabs Message Header -->
<string name="no_collections_header1">آپ کے لئے اہم چیزیں جمع کریں</string>
<!-- Deprecated: Label to describe what collections are to a new user without any collections -->
<string name="no_collections_description1">ایک طرح کی تلاشیں ، سائٹیں اور ٹیبز کو بعد میں فوری رسائی کے لئے گروہ بندی کریں۔</string>
</resources>

@ -75,6 +75,7 @@
<color name="tab_tray_item_media_background_light_theme">#312A65</color>
<color name="tab_tray_heading_icon_light_theme">@color/ink_20</color>
<color name="tab_tray_heading_icon_inactive_light_theme">@color/ink_20_48a</color>
<color name="tab_tray_heading_icon_menu_light_theme">@color/accent_normal_theme</color>
<color name="tab_tray_item_thumbnail_background_light_theme">@color/photonLightGrey10</color>
<color name="tab_tray_item_thumbnail_icon_light_theme">@color/photonLightGrey60</color>
<color name="tab_tray_selected_mask_light_theme">@color/violet_70_12a</color>
@ -141,7 +142,8 @@
<color name="tab_tray_item_divider_dark_theme">@color/photonDarkGrey10</color>
<color name="tab_tray_item_media_background_dark_theme">#9059FF</color>
<color name="tab_tray_heading_icon_dark_theme">@color/violet_50</color>
<color name="tab_tray_heading_icon_inactive_dark_theme">@color/violet_50_48a</color>
<color name="tab_tray_heading_icon_inactive_dark_theme">@color/photonLightGrey05</color>
<color name="tab_tray_heading_icon_menu_dark_theme">@color/photonLightGrey05</color>
<color name="tab_tray_item_thumbnail_background_dark_theme">@color/photonDarkGrey50</color>
<color name="tab_tray_item_thumbnail_icon_dark_theme">@color/photonDarkGrey05</color>
<color name="tab_tray_selected_mask_dark_theme">@color/violet_50_32a</color>
@ -264,6 +266,7 @@
<color name="tab_tray_item_media_background_normal_theme">@color/tab_tray_item_media_background_light_theme</color>
<color name="tab_tray_heading_icon_normal_theme">@color/tab_tray_heading_icon_light_theme</color>
<color name="tab_tray_heading_icon_inactive_normal_theme">@color/tab_tray_heading_icon_inactive_light_theme</color>
<color name="tab_tray_heading_icon_menu_normal_theme">@color/tab_tray_heading_icon_menu_light_theme</color>
<color name="tab_tray_item_thumbnail_background_normal_theme">@color/tab_tray_item_thumbnail_background_light_theme</color>
<color name="tab_tray_item_thumbnail_icon_normal_theme">@color/tab_tray_item_thumbnail_icon_light_theme</color>
<color name="tab_tray_selected_mask_normal_theme">@color/tab_tray_selected_mask_light_theme</color>

@ -35,6 +35,8 @@
<string name="pref_key_delete_browsing_data_on_quit_categories" translatable="false">pref_key_delete_browsing_data_on_quit_categories</string>
<string name="pref_key_last_known_mode_private" translatable="false">pref_key_last_known_mode_private</string>
<string name="pref_key_addons" translatable="false">pref_key_addons</string>
<string name="pref_key_override_amo_user" translatable="false">pref_key_override_amo_user</string>
<string name="pref_key_override_amo_collection" translatable="false">pref_key_override_amo_collection</string>
<string name="pref_key_last_maintenance" translatable="false">pref_key_last_maintenance</string>
<string name="pref_key_help" translatable="false">pref_key_help</string>
<string name="pref_key_rate" translatable="false">pref_key_rate</string>

@ -347,6 +347,20 @@
<!-- Preference for notifications -->
<string name="preferences_notifications">Notifications</string>
<!-- Add-on Preferences -->
<!-- Preference to customize the configured AMO (addons.mozilla.org) collection -->
<string name="preferences_customize_amo_collection">Custom Add-on collection</string>
<!-- Button caption to confirm the add-on collection configuration -->
<string name="customize_addon_collection_ok">OK</string>
<!-- Button caption to abort the add-on collection configuration -->
<string name="customize_addon_collection_cancel">Cancel</string>
<!-- Hint displayed on input field for custom collection name -->
<string name="customize_addon_collection_hint">Collection name</string>
<!-- Hint displayed on input field for custom collection user ID-->
<string name="customize_addon_collection_user_hint">Collection owner (User ID)</string>
<!-- Toast shown after confirming the custom add-on collection configuration -->
<string name="toast_customize_addon_collection_done">Add-on collection modified. Quitting the application to apply changes…</string>
<!-- Account Preferences -->
<!-- Preference for triggering sync -->
<string name="preferences_sync_now">Sync now</string>

@ -131,6 +131,11 @@
android:key="@string/pref_key_addons"
android:title="@string/preferences_addons" />
<androidx.preference.Preference
android:icon="@drawable/ic_addons_extensions"
android:key="@string/pref_key_override_amo_collection"
android:title="@string/preferences_customize_amo_collection"/>
<androidx.preference.SwitchPreference
android:defaultValue="false"
android:icon="@drawable/ic_open_in_app"

@ -202,6 +202,7 @@ class IntentReceiverActivityTest {
every { activity.settings() } returns settings
every { activity.components.analytics } returns mockk(relaxed = true)
every { activity.components.intentProcessors } returns intentProcessors
every { activity.components.strictMode } returns mockk(relaxed = true)
}
private inline fun <reified T : IntentProcessor> mockIntentProcessor(): T {

@ -11,13 +11,13 @@ import io.mockk.confirmVerified
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.mockkStatic
import io.mockk.slot
import io.mockk.unmockkObject
import io.mockk.unmockkStatic
import io.mockk.verify
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.fail
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@ -26,48 +26,91 @@ import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
@RunWith(FenixRobolectricTestRunner::class)
class StrictModeManagerTest {
private lateinit var debugManager: StrictModeManager
private lateinit var releaseManager: StrictModeManager
@MockK(relaxUnitFun = true) private lateinit var fragmentManager: FragmentManager
@Before
fun setup() {
MockKAnnotations.init(this)
mockkStatic(StrictMode::class)
mockkObject(Config)
// These tests log a warning that mockk couldn't set the backing field of Config.channel
// but it doesn't seem to impact their correctness so I'm ignoring it.
val debugConfig: Config = mockk { every { channel } returns ReleaseChannel.Debug }
debugManager = StrictModeManager(debugConfig)
val releaseConfig: Config = mockk { every { channel } returns ReleaseChannel.Release }
releaseManager = StrictModeManager(releaseConfig)
}
@After
fun teardown() {
unmockkStatic(StrictMode::class)
unmockkObject(Config)
}
@Test
fun `test enableStrictMode in release`() {
every { Config.channel } returns ReleaseChannel.Release
StrictModeManager.enableStrictMode(false)
fun `GIVEN we're in a release build WHEN we enable strict mode THEN we don't set policies`() {
releaseManager.enableStrictMode(false)
verify(exactly = 0) { StrictMode.setThreadPolicy(any()) }
verify(exactly = 0) { StrictMode.setVmPolicy(any()) }
}
@Test
fun `test enableStrictMode in debug`() {
every { Config.channel } returns ReleaseChannel.Debug
StrictModeManager.enableStrictMode(false)
fun `GIVEN we're in a debug build WHEN we enable strict mode THEN we set policies`() {
debugManager.enableStrictMode(false)
verify { StrictMode.setThreadPolicy(any()) }
verify { StrictMode.setVmPolicy(any()) }
}
@Test
fun `test changeStrictModePolicies`() {
fun `GIVEN we're in a debug build WHEN we attach a listener THEN we attach to the fragment lifecycle and detach when onFragmentResumed is called`() {
val callbacks = slot<FragmentManager.FragmentLifecycleCallbacks>()
StrictModeManager.changeStrictModePolicies(fragmentManager)
debugManager.attachListenerToDisablePenaltyDeath(fragmentManager)
verify { fragmentManager.registerFragmentLifecycleCallbacks(capture(callbacks), false) }
confirmVerified(fragmentManager)
callbacks.captured.onFragmentResumed(fragmentManager, mockk())
verify { fragmentManager.unregisterFragmentLifecycleCallbacks(callbacks.captured) }
}
@Test
fun `GIVEN we're in a release build WHEN resetAfter is called THEN we return the value from the function block`() {
val expected = "Hello world"
val actual = releaseManager.resetAfter(StrictMode.allowThreadDiskReads()) { expected }
assertEquals(expected, actual)
}
@Test
fun `GIVEN we're in a debug build WHEN resetAfter is called THEN we return the value from the function block`() {
val expected = "Hello world"
val actual = debugManager.resetAfter(StrictMode.allowThreadDiskReads()) { expected }
assertEquals(expected, actual)
}
@Test
fun `GIVEN we're in a release build WHEN resetAfter is called THEN the old policy is not set`() {
releaseManager.resetAfter(StrictMode.allowThreadDiskReads()) { "" }
verify(exactly = 0) { StrictMode.setThreadPolicy(any()) }
}
@Test
fun `GIVEN we're in a debug build WHEN resetAfter is called THEN the old policy is set`() {
val expectedPolicy = StrictMode.allowThreadDiskReads()
debugManager.resetAfter(expectedPolicy) { "" }
verify { StrictMode.setThreadPolicy(expectedPolicy) }
}
@Test
fun `GIVEN we're in a debug build WHEN resetAfter is called and an exception is thrown from the function THEN the old policy is set`() {
val expectedPolicy = StrictMode.allowThreadDiskReads()
try {
debugManager.resetAfter(expectedPolicy) { throw IllegalStateException() }
@Suppress("UNREACHABLE_CODE") fail("Expected previous method to throw.")
} catch (e: IllegalStateException) { /* Do nothing */ }
verify { StrictMode.setThreadPolicy(expectedPolicy) }
}
}

@ -10,13 +10,10 @@ import io.mockk.impl.annotations.MockK
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.ExperimentalCoroutinesApi
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.ReaderState
import mozilla.components.browser.state.state.createTab
import mozilla.components.feature.tab.collections.Tab
import mozilla.components.lib.publicsuffixlist.PublicSuffixList
import mozilla.components.support.test.robolectric.createAddedTestFragment
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
@ -31,9 +28,6 @@ private const val SESSION_ID_MOZILLA = "0"
private const val URL_BCC = "www.bcc.co.uk"
private const val SESSION_ID_BCC = "1"
private const val SESSION_ID_BAD_1 = "not a real session id"
private const val SESSION_ID_BAD_2 = "definitely not a real session id"
@ExperimentalCoroutinesApi
@RunWith(FenixRobolectricTestRunner::class)
class CollectionCreationFragmentTest {
@ -71,63 +65,4 @@ class CollectionCreationFragmentTest {
fragment.dismiss()
assertNull(fragment.dialog)
}
@Test
fun `GIVEN tabs are present in state WHEN getTabs is called THEN tabs will be returned`() {
val tabs = state.getTabs(arrayOf(SESSION_ID_MOZILLA, SESSION_ID_BCC), publicSuffixList)
val hosts = tabs.map { it.hostname }
assertEquals(URL_MOZILLA, hosts[0])
assertEquals(URL_BCC, hosts[1])
}
@Test
fun `GIVEN some tabs are present in state WHEN getTabs is called THEN only valid tabs will be returned`() {
val tabs = state.getTabs(arrayOf(SESSION_ID_MOZILLA, SESSION_ID_BAD_1), publicSuffixList)
val hosts = tabs.map { it.hostname }
assertEquals(URL_MOZILLA, hosts[0])
assertEquals(1, hosts.size)
}
@Test
fun `GIVEN tabs are not present in state WHEN getTabs is called THEN an empty list will be returned`() {
val tabs = state.getTabs(arrayOf(SESSION_ID_BAD_1, SESSION_ID_BAD_2), publicSuffixList)
assertEquals(emptyList<Tab>(), tabs)
}
@Test
fun `WHEN getTabs is called will null tabIds THEN an empty list will be returned`() {
val tabs = state.getTabs(null, publicSuffixList)
assertEquals(emptyList<Tab>(), tabs)
}
@Test
fun `toTab uses active reader URL`() {
val tabWithoutReaderState = createTab(url = "https://example.com", id = "1")
val tabWithInactiveReaderState = createTab(url = "https://blog.mozilla.org", id = "2",
readerState = ReaderState(active = false, activeUrl = null)
)
val tabWithActiveReaderState = createTab(url = "moz-extension://123", id = "3",
readerState = ReaderState(active = true, activeUrl = "https://blog.mozilla.org/123")
)
val state = BrowserState(
tabs = listOf(tabWithoutReaderState, tabWithInactiveReaderState, tabWithActiveReaderState)
)
val tabs = state.getTabs(
arrayOf(tabWithoutReaderState.id, tabWithInactiveReaderState.id, tabWithActiveReaderState.id),
publicSuffixList
)
assertEquals(tabWithoutReaderState.content.url, tabs[0].url)
assertEquals(tabWithInactiveReaderState.content.url, tabs[1].url)
assertEquals("https://blog.mozilla.org/123", tabs[2].url)
}
}

@ -4,17 +4,54 @@
package org.mozilla.fenix.collections
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.mockk
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.ExperimentalCoroutinesApi
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.ReaderState
import mozilla.components.browser.state.state.createTab
import mozilla.components.lib.publicsuffixlist.PublicSuffixList
import mozilla.components.support.test.ext.joinBlocking
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.components.TabCollectionStorage
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.home.Tab
private const val URL_MOZILLA = "www.mozilla.org"
private const val SESSION_ID_MOZILLA = "0"
private const val URL_BCC = "www.bcc.co.uk"
private const val SESSION_ID_BCC = "1"
private const val SESSION_ID_BAD_1 = "not a real session id"
private const val SESSION_ID_BAD_2 = "definitely not a real session id"
@ExperimentalCoroutinesApi
@RunWith(FenixRobolectricTestRunner::class)
class CollectionCreationStoreTest {
@MockK private lateinit var tabCollectionStorage: TabCollectionStorage
@MockK(relaxed = true) private lateinit var publicSuffixList: PublicSuffixList
private val sessionMozilla = createTab(URL_MOZILLA, id = SESSION_ID_MOZILLA)
private val sessionBcc = createTab(URL_BCC, id = SESSION_ID_BCC)
private val state = BrowserState(
tabs = listOf(sessionMozilla, sessionBcc)
)
@Before
fun before() {
MockKAnnotations.init(this)
every { tabCollectionStorage.cachedTabCollections } returns emptyList()
every { publicSuffixList.stripPublicSuffix(URL_MOZILLA) } returns CompletableDeferred(URL_MOZILLA)
every { publicSuffixList.stripPublicSuffix(URL_BCC) } returns CompletableDeferred(URL_BCC)
}
@Test
fun `select and deselect all tabs`() {
val tabs = listOf<Tab>(mockk(), mockk())
@ -73,4 +110,121 @@ class CollectionCreationStoreTest {
assertEquals(SaveCollectionStep.RenameCollection, store.state.saveCollectionStep)
assertEquals(3, store.state.defaultCollectionNumber)
}
@Test
fun `GIVEN no selected tab ids WHEN create initial state THEN only tab will be selected`() {
val result = createInitialCollectionCreationState(
browserState = state,
tabCollectionStorage = tabCollectionStorage,
publicSuffixList = publicSuffixList,
saveCollectionStep = SaveCollectionStep.NameCollection,
tabIds = arrayOf(SESSION_ID_MOZILLA),
selectedTabIds = null,
selectedTabCollectionId = 0
)
assertEquals(SaveCollectionStep.NameCollection, result.saveCollectionStep)
assertEquals(1, result.tabs.size)
assertEquals(SESSION_ID_MOZILLA, result.tabs[0].sessionId)
assertEquals(1, result.selectedTabs.size)
assertEquals(SESSION_ID_MOZILLA, result.selectedTabs.first().sessionId)
}
@Test
fun `GIVEN no selected tab ids WHEN create initial state with many tabs THEN nothing will be selected`() {
val result = createInitialCollectionCreationState(
browserState = state,
tabCollectionStorage = tabCollectionStorage,
publicSuffixList = publicSuffixList,
saveCollectionStep = SaveCollectionStep.NameCollection,
tabIds = arrayOf(SESSION_ID_MOZILLA, SESSION_ID_BCC),
selectedTabIds = null,
selectedTabCollectionId = 0
)
assertEquals(SaveCollectionStep.NameCollection, result.saveCollectionStep)
assertEquals(2, result.tabs.size)
assertEquals(SESSION_ID_MOZILLA, result.tabs[0].sessionId)
assertEquals(SESSION_ID_BCC, result.tabs[1].sessionId)
assertEquals(0, result.selectedTabs.size)
}
@Test
fun `GIVEN selected tab ids WHEN create initial state THEN select tabs`() {
val result = createInitialCollectionCreationState(
browserState = state,
tabCollectionStorage = tabCollectionStorage,
publicSuffixList = publicSuffixList,
saveCollectionStep = SaveCollectionStep.RenameCollection,
tabIds = arrayOf(SESSION_ID_MOZILLA, SESSION_ID_BCC),
selectedTabIds = arrayOf(SESSION_ID_BCC),
selectedTabCollectionId = 0
)
assertEquals(SaveCollectionStep.RenameCollection, result.saveCollectionStep)
assertEquals(2, result.tabs.size)
assertEquals(SESSION_ID_MOZILLA, result.tabs[0].sessionId)
assertEquals(SESSION_ID_BCC, result.tabs[1].sessionId)
assertEquals(1, result.selectedTabs.size)
assertEquals(SESSION_ID_BCC, result.selectedTabs.first().sessionId)
}
@Test
fun `GIVEN tabs are present in state WHEN getTabs is called THEN tabs will be returned`() {
val tabs = state.getTabs(arrayOf(SESSION_ID_MOZILLA, SESSION_ID_BCC), publicSuffixList)
val hosts = tabs.map { it.hostname }
assertEquals(URL_MOZILLA, hosts[0])
assertEquals(URL_BCC, hosts[1])
}
@Test
fun `GIVEN some tabs are present in state WHEN getTabs is called THEN only valid tabs will be returned`() {
val tabs = state.getTabs(arrayOf(SESSION_ID_MOZILLA, SESSION_ID_BAD_1), publicSuffixList)
val hosts = tabs.map { it.hostname }
assertEquals(URL_MOZILLA, hosts[0])
assertEquals(1, hosts.size)
}
@Test
fun `GIVEN tabs are not present in state WHEN getTabs is called THEN an empty list will be returned`() {
val tabs = state.getTabs(arrayOf(SESSION_ID_BAD_1, SESSION_ID_BAD_2), publicSuffixList)
assertEquals(emptyList<Tab>(), tabs)
}
@Test
fun `WHEN getTabs is called will null tabIds THEN an empty list will be returned`() {
val tabs = state.getTabs(null, publicSuffixList)
assertEquals(emptyList<Tab>(), tabs)
}
@Test
fun `toTab uses active reader URL`() {
val tabWithoutReaderState = createTab(url = "https://example.com", id = "1")
val tabWithInactiveReaderState = createTab(url = "https://blog.mozilla.org", id = "2",
readerState = ReaderState(active = false, activeUrl = null)
)
val tabWithActiveReaderState = createTab(url = "moz-extension://123", id = "3",
readerState = ReaderState(active = true, activeUrl = "https://blog.mozilla.org/123")
)
val state = BrowserState(
tabs = listOf(tabWithoutReaderState, tabWithInactiveReaderState, tabWithActiveReaderState)
)
val tabs = state.getTabs(
arrayOf(tabWithoutReaderState.id, tabWithInactiveReaderState.id, tabWithActiveReaderState.id),
publicSuffixList
)
assertEquals(tabWithoutReaderState.content.url, tabs[0].url)
assertEquals(tabWithInactiveReaderState.content.url, tabs[1].url)
assertEquals("https://blog.mozilla.org/123", tabs[2].url)
}
}

@ -25,7 +25,7 @@ class AccountAbnormalitiesTest {
val crashReporter: CrashReporter = mockk()
// no account present
val accountAbnormalities = AccountAbnormalities(testContext, crashReporter)
val accountAbnormalities = AccountAbnormalities(testContext, crashReporter, mockk(relaxed = true))
try {
accountAbnormalities.userRequestedLogout()
@ -52,7 +52,7 @@ class AccountAbnormalitiesTest {
val crashReporter: CrashReporter = mockk(relaxed = true)
val accountManager: FxaAccountManager = mockk(relaxed = true)
val accountAbnormalities = AccountAbnormalities(testContext, crashReporter, this.coroutineContext)
val accountAbnormalities = AccountAbnormalities(testContext, crashReporter, mockk(relaxed = true), this.coroutineContext)
accountAbnormalities.accountManagerStarted(accountManager)
// Logout action must be preceded by auth.
@ -65,7 +65,7 @@ class AccountAbnormalitiesTest {
val crashReporter: CrashReporter = mockk(relaxed = true)
val accountManager: FxaAccountManager = mockk(relaxed = true)
val accountAbnormalities = AccountAbnormalities(testContext, crashReporter, this.coroutineContext)
val accountAbnormalities = AccountAbnormalities(testContext, crashReporter, mockk(relaxed = true), this.coroutineContext)
accountAbnormalities.accountManagerStarted(accountManager)
accountAbnormalities.onAuthenticated(mockk(), mockk())
@ -83,7 +83,7 @@ class AccountAbnormalitiesTest {
val crashReporter: CrashReporter = mockk(relaxed = true)
val accountManager: FxaAccountManager = mockk(relaxed = true)
val accountAbnormalities = AccountAbnormalities(testContext, crashReporter, this.coroutineContext)
val accountAbnormalities = AccountAbnormalities(testContext, crashReporter, mockk(relaxed = true), this.coroutineContext)
accountAbnormalities.accountManagerStarted(accountManager)
// User didn't request this logout.
@ -96,7 +96,7 @@ class AccountAbnormalitiesTest {
val crashReporter: CrashReporter = mockk(relaxed = true)
val accountManager: FxaAccountManager = mockk(relaxed = true)
val accountAbnormalities = AccountAbnormalities(testContext, crashReporter, this.coroutineContext)
val accountAbnormalities = AccountAbnormalities(testContext, crashReporter, mockk(relaxed = true), this.coroutineContext)
accountAbnormalities.accountManagerStarted(accountManager)
accountAbnormalities.onAuthenticated(mockk(), mockk())
@ -104,7 +104,7 @@ class AccountAbnormalitiesTest {
every { accountManager.authenticatedAccount() } returns null
// Pretend we restart, and instantiate a new middleware instance.
val accountAbnormalities2 = AccountAbnormalities(testContext, crashReporter, this.coroutineContext)
val accountAbnormalities2 = AccountAbnormalities(testContext, crashReporter, mockk(relaxed = true), this.coroutineContext)
// mock accountManager doesn't have an account, but we expect it to have one since we
// were authenticated before our "restart".
accountAbnormalities2.accountManagerStarted(accountManager)
@ -117,7 +117,7 @@ class AccountAbnormalitiesTest {
val crashReporter: CrashReporter = mockk()
val accountManager: FxaAccountManager = mockk(relaxed = true)
val accountAbnormalities = AccountAbnormalities(testContext, crashReporter, this.coroutineContext)
val accountAbnormalities = AccountAbnormalities(testContext, crashReporter, mockk(relaxed = true), this.coroutineContext)
accountAbnormalities.accountManagerStarted(accountManager)
// We saw an auth event, then user requested a logout.

@ -28,7 +28,7 @@ class TestComponents(private val context: Context) : Components(context) {
)
}
override val intentProcessors by lazy { mockk<IntentProcessors>(relaxed = true) }
override val analytics by lazy { Analytics(context) }
override val analytics by lazy { Analytics(context, strictMode) }
override val clipboardHandler by lazy { ClipboardHandler(context) }

@ -17,7 +17,11 @@ import mozilla.components.concept.fetch.Client
import mozilla.components.feature.pwa.WebAppShortcutManager
import mozilla.components.feature.top.sites.DefaultTopSitesStorage
class TestCore(context: Context, crashReporter: CrashReporting) : Core(context, crashReporter) {
class TestCore(context: Context, crashReporter: CrashReporting) : Core(
context,
crashReporter,
mockk()
) {
override val engine = mockk<Engine>(relaxed = true) {
every { this@mockk getProperty "settings" } returns mockk<Settings>(relaxed = true)

@ -5,6 +5,7 @@
package org.mozilla.fenix.components.metrics
import android.content.Context.MODE_PRIVATE
import io.mockk.mockk
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
@ -30,10 +31,18 @@ class LeanplumMetricsServiceTest {
assertNull(sharedPreferences.getString("LP_DEVICE_ID", null))
val leanplumMetricService = LeanplumMetricsService(testContext.application, idGenerator)
val leanplumMetricService = LeanplumMetricsService(
testContext.application,
mockk(relaxed = true),
idGenerator
)
assertEquals("TEST_DEVICE_ID", leanplumMetricService.deviceId)
val leanplumMetricService2 = LeanplumMetricsService(testContext.application, idGenerator)
val leanplumMetricService2 = LeanplumMetricsService(
testContext.application,
mockk(relaxed = true),
idGenerator
)
assertEquals("TEST_DEVICE_ID", leanplumMetricService2.deviceId)
assertEquals(1, callCount)

@ -1,79 +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.ext
import android.os.StrictMode
import io.mockk.Runs
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.mockkStatic
import io.mockk.unmockkObject
import io.mockk.unmockkStatic
import io.mockk.verify
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.Config
import org.mozilla.fenix.ReleaseChannel
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
@RunWith(FenixRobolectricTestRunner::class)
class StrictModeTest {
private lateinit var threadPolicy: StrictMode.ThreadPolicy
private lateinit var functionBlock: () -> String
@Before
fun setup() {
threadPolicy = StrictMode.ThreadPolicy.LAX
functionBlock = mockk()
mockkStatic(StrictMode::class)
mockkObject(Config)
every { StrictMode.setThreadPolicy(threadPolicy) } just Runs
every { functionBlock() } returns "Hello world"
}
@After
fun teardown() {
unmockkStatic(StrictMode::class)
unmockkObject(Config)
}
@Test
fun `runs function block in release`() {
every { Config.channel } returns ReleaseChannel.Release
assertEquals("Hello world", threadPolicy.resetPoliciesAfter(functionBlock))
verify(exactly = 0) { StrictMode.setThreadPolicy(any()) }
}
@Test
fun `runs function block in debug`() {
every { Config.channel } returns ReleaseChannel.Debug
assertEquals("Hello world", threadPolicy.resetPoliciesAfter(functionBlock))
verify { StrictMode.setThreadPolicy(threadPolicy) }
}
@Test
fun `sets thread policy even if function throws`() {
every { Config.channel } returns ReleaseChannel.Debug
every { functionBlock() } throws IllegalStateException()
var exception: IllegalStateException? = null
try {
threadPolicy.resetPoliciesAfter(functionBlock)
} catch (e: IllegalStateException) {
exception = e
}
verify { StrictMode.setThreadPolicy(threadPolicy) }
assertNotNull(exception)
}
}

@ -0,0 +1,76 @@
/* 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.settings
import androidx.fragment.app.FragmentActivity
import androidx.preference.Preference
import io.mockk.every
import io.mockk.mockk
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.getPreferenceKey
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.utils.Settings
import org.robolectric.Robolectric
@RunWith(FenixRobolectricTestRunner::class)
class SettingsFragmentTest {
@Test
fun `Add-on collection override pref is visible if debug menu active`() {
val settingsFragment = SettingsFragment()
val activity = Robolectric.buildActivity(FragmentActivity::class.java).create().get()
activity.supportFragmentManager.beginTransaction()
.add(settingsFragment, "test")
.commitNow()
val preferenceAmoCollectionOverride = settingsFragment.findPreference<Preference>(
settingsFragment.getPreferenceKey(R.string.pref_key_override_amo_collection)
)
settingsFragment.setupAmoCollectionOverridePreference(mockk(relaxed = true))
assertNotNull(preferenceAmoCollectionOverride)
assertFalse(preferenceAmoCollectionOverride!!.isVisible)
val settings: Settings = mockk(relaxed = true)
every { settings.showSecretDebugMenuThisSession } returns true
settingsFragment.setupAmoCollectionOverridePreference(settings)
assertTrue(preferenceAmoCollectionOverride.isVisible)
}
@Test
fun `Add-on collection override pref is visible if already configured`() {
val settingsFragment = SettingsFragment()
val activity = Robolectric.buildActivity(FragmentActivity::class.java).create().get()
activity.supportFragmentManager.beginTransaction()
.add(settingsFragment, "test")
.commitNow()
val preferenceAmoCollectionOverride = settingsFragment.findPreference<Preference>(
settingsFragment.getPreferenceKey(R.string.pref_key_override_amo_collection)
)
settingsFragment.setupAmoCollectionOverridePreference(mockk(relaxed = true))
assertNotNull(preferenceAmoCollectionOverride)
assertFalse(preferenceAmoCollectionOverride!!.isVisible)
val settings: Settings = mockk(relaxed = true)
every { settings.showSecretDebugMenuThisSession } returns false
every { settings.amoCollectionOverrideConfigured() } returns false
settingsFragment.setupAmoCollectionOverridePreference(settings)
assertFalse(preferenceAmoCollectionOverride.isVisible)
every { settings.amoCollectionOverrideConfigured() } returns true
settingsFragment.setupAmoCollectionOverridePreference(settings)
assertTrue(preferenceAmoCollectionOverride.isVisible)
}
}

@ -4,6 +4,7 @@
package org.mozilla.fenix.settings.logins
import io.mockk.every
import io.mockk.mockk
import mozilla.components.concept.storage.Login
import mozilla.components.support.test.ext.joinBlocking
@ -12,6 +13,7 @@ import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test
import org.mozilla.fenix.utils.Settings
class LoginsFragmentStoreTest {
@ -34,6 +36,26 @@ class LoginsFragmentStoreTest {
duplicateLogins = listOf()
)
@Test
fun `create initial state`() {
val settings = mockk<Settings>()
every { settings.savedLoginsSortingStrategy } returns SortingStrategy.LastUsed
every { settings.savedLoginsMenuHighlightedItem } returns SavedLoginsSortingStrategyMenu.Item.LastUsedSort
assertEquals(
LoginsListState(
isLoading = true,
loginList = emptyList(),
filteredItems = emptyList(),
searchedForText = null,
sortingStrategy = SortingStrategy.LastUsed,
highlightedItem = SavedLoginsSortingStrategyMenu.Item.LastUsedSort,
duplicateLogins = emptyList()
),
createInitialLoginsListState(settings)
)
}
@Test
fun `convert login to saved login`() {
val login = Login(

@ -5,6 +5,7 @@
package org.mozilla.fenix.share
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import androidx.navigation.NavController
@ -143,6 +144,31 @@ class ShareControllerTest {
}
}
@Test
fun `handleShareToApp should dismiss with an error start when a ActivityNotFoundException occurs`() {
val appPackageName = "package"
val appClassName = "activity"
val appShareOption = AppShareOption("app", mockk(), appPackageName, appClassName)
val shareIntent = slot<Intent>()
// Our share Intent uses `FLAG_ACTIVITY_NEW_TASK` but when resolving the startActivity call
// needed for capturing the actual Intent used the `slot` one doesn't have this flag so we
// need to use an Activity Context.
val activityContext: Context = mockk<Activity>()
val testController = DefaultShareController(activityContext, shareSubject, shareData, mockk(),
snackbar, mockk(), mockk(), testCoroutineScope, dismiss)
every { activityContext.startActivity(capture(shareIntent)) } throws ActivityNotFoundException()
every { activityContext.getString(R.string.share_error_snackbar) } returns "Cannot share to this app"
testController.handleShareToApp(appShareOption)
verifyOrder {
activityContext.startActivity(shareIntent.captured)
snackbar.setText("Cannot share to this app")
snackbar.show()
dismiss(ShareController.Result.SHARE_ERROR)
}
}
@Test
@Suppress("DeferredResultUnused")
fun `handleShareToDevice should share to account device, inform callbacks and dismiss`() {

@ -576,4 +576,34 @@ class SettingsTest {
settings.getSitePermissionsCustomSettingsRules()
)
}
@Test
fun overrideAmoCollection() {
// When just created
// Then
assertEquals("", settings.overrideAmoCollection)
assertFalse(settings.amoCollectionOverrideConfigured())
// When
settings.overrideAmoCollection = "testCollection"
// Then
assertEquals("testCollection", settings.overrideAmoCollection)
assertTrue(settings.amoCollectionOverrideConfigured())
}
@Test
fun overrideAmoUser() {
// When just created
// Then
assertEquals("", settings.overrideAmoUser)
assertFalse(settings.amoCollectionOverrideConfigured())
// When
settings.overrideAmoUser = "testAmoUser"
// Then
assertEquals("testAmoUser", settings.overrideAmoUser)
assertTrue(settings.amoCollectionOverrideConfigured())
}
}

Loading…
Cancel
Save