1
0
Fork 0

Copione merged onto master
continuous-integration/drone/push Build is failing Details

master
blallo 2020-06-18 00:00:31 +02:00
commit a7bf91e814
40 changed files with 982 additions and 391 deletions

View File

@ -158,6 +158,7 @@ class HistoryTest {
}.openHistory {
}.openThreeDotMenu {
}.clickDelete {
verifyDeleteSnackbarText("Deleted")
verifyEmptyHistoryView()
}
}
@ -174,6 +175,7 @@ class HistoryTest {
clickDeleteHistoryButton()
verifyDeleteConfirmationMessage()
confirmDeleteAllHistory()
verifyDeleteSnackbarText("Browsing data deleted")
verifyEmptyHistoryView()
}
}

View File

@ -83,6 +83,8 @@ class HistoryRobot {
.click()
}
fun verifyDeleteSnackbarText(text: String) = assertSnackBarText(text)
class Transition {
fun goBack(interact: HistoryRobot.() -> Unit): Transition {
goBackButton().click()
@ -152,3 +154,6 @@ private fun assertDeleteConfirmationMessage() =
.check(matches(isDisplayed()))
private fun assertCopySnackBarText() = snackBarText().check(matches(withText("URL copied")))
private fun assertSnackBarText(text: String) =
snackBarText().check(matches(withText(Matchers.containsString(text))))

View File

@ -22,21 +22,8 @@ class AppRequestInterceptor(private val context: Context) : RequestInterceptor {
hasUserGesture: Boolean,
isSameDomain: Boolean
): RequestInterceptor.InterceptionResponse? {
var result: RequestInterceptor.InterceptionResponse? = null
// WebChannel-driven authentication does not require a separate redirect interceptor.
@Suppress("ConstantConditionIf")
if (FeatureFlags.asFeatureWebChannelsDisabled) {
result = context.components.services.accountsAuthFeature.interceptor.onLoadRequest(
engineSession, uri, hasUserGesture, isSameDomain)
}
if (result == null) {
result = context.components.services.appLinksInterceptor.onLoadRequest(
engineSession, uri, hasUserGesture, isSameDomain)
}
return result
return context.components.services.appLinksInterceptor
.onLoadRequest(engineSession, uri, hasUserGesture, isSameDomain)
}
override fun onErrorRequest(

View File

@ -10,25 +10,6 @@ object FeatureFlags {
*/
const val pullToRefreshEnabled = false
/**
* Disables FxA Application Services Web Channels feature
*/
const val asFeatureWebChannelsDisabled = false
/**
* Disables FxA Application Services Sync feature
*/
const val asFeatureSyncDisabled = false
/**
* Integration of push support provided by `feature-push` component into the Gecko engine.
*
* Behind nightly flag until all fatal bugs are resolved.
*
* https://github.com/mozilla-mobile/fenix/issues/9059
*/
const val webPushIntegration = true
/**
* Enables tip feature
*/

View File

@ -37,7 +37,6 @@ 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.FeatureFlags.webPushIntegration
import org.mozilla.fenix.components.Components
import org.mozilla.fenix.components.metrics.MetricServiceType
import org.mozilla.fenix.ext.settings
@ -239,10 +238,7 @@ open class FenixApplication : LocaleAwareApplication() {
// Install the AutoPush singleton to receive messages.
PushProcessor.install(it)
if (webPushIntegration) {
// WebPush integration to observe and deliver push messages to engine.
WebPushEngineIntegration(components.core.engine, it).start()
}
WebPushEngineIntegration(components.core.engine, it).start()
// Perform a one-time initialization of the account manager if a message is received.
PushFxaIntegration(it, lazy { components.backgroundServices.accountManager }).launch()

View File

@ -7,13 +7,10 @@ package org.mozilla.fenix.addons
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.text.method.LinkMovementMethod
import android.view.View
import androidx.core.text.HtmlCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.navArgs
import kotlinx.android.synthetic.main.fragment_add_on_details.view.*
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch
@ -21,108 +18,35 @@ import kotlinx.coroutines.withContext
import mozilla.components.feature.addons.Addon
import mozilla.components.feature.addons.ui.showInformationDialog
import mozilla.components.feature.addons.ui.translatedName
import mozilla.components.feature.addons.ui.translatedDescription
import mozilla.components.feature.addons.update.DefaultAddonUpdater.UpdateAttemptStorage
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.showToolbar
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.Locale
/**
* A fragment to show the details of an add-on.
*/
class AddonDetailsFragment : Fragment(R.layout.fragment_add_on_details) {
private val updateAttemptStorage: UpdateAttemptStorage by lazy {
UpdateAttemptStorage(requireContext())
}
class AddonDetailsFragment : Fragment(R.layout.fragment_add_on_details), AddonDetailsInteractor {
private val updateAttemptStorage by lazy { UpdateAttemptStorage(requireContext()) }
private val args by navArgs<AddonDetailsFragmentArgs>()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
bind(args.addon, view)
showToolbar(title = args.addon.translatedName)
AddonDetailsView(view, interactor = this).bind(args.addon)
}
private fun bind(addon: Addon, view: View) {
val title = addon.translatedName
showToolbar(title)
bindDetails(addon, view)
bindAuthors(addon, view)
bindVersion(addon, view)
bindLastUpdated(addon, view)
bindWebsite(addon, view)
bindRating(addon, view)
override fun openWebsite(addonSiteUrl: Uri) {
startActivity(Intent(Intent.ACTION_VIEW, addonSiteUrl))
}
private fun bindRating(addon: Addon, view: View) {
addon.rating?.let {
val ratingView = view.rating_view
val userCountView = view.users_count
val ratingContentDescription =
getString(R.string.mozac_feature_addons_rating_content_description)
ratingView.contentDescription = String.format(ratingContentDescription, it.average)
ratingView.rating = it.average
userCountView.text = getFormattedAmount(it.reviews)
}
}
private fun bindWebsite(addon: Addon, view: View) {
view.home_page_label.setOnClickListener {
val intent =
Intent(Intent.ACTION_VIEW).setData(Uri.parse(addon.siteUrl))
startActivity(intent)
}
}
private fun bindLastUpdated(addon: Addon, view: View) {
view.last_updated_text.text = formatDate(addon.updatedAt)
}
private fun bindVersion(addon: Addon, view: View) {
view.version_text.text =
addon.installedState?.version?.ifEmpty { addon.version } ?: addon.version
if (addon.isInstalled()) {
view.version_text.setOnLongClickListener {
showUpdaterDialog(addon)
true
override fun showUpdaterDialog(addon: Addon) {
viewLifecycleOwner.lifecycleScope.launch(Main) {
val updateAttempt = withContext(IO) {
updateAttemptStorage.findUpdateAttemptBy(addon.id)
}
updateAttempt?.showInformationDialog(requireContext())
}
}
private fun showUpdaterDialog(addon: Addon) {
viewLifecycleOwner.lifecycleScope.launch(IO) {
val updateAttempt = updateAttemptStorage.findUpdateAttemptBy(addon.id)
updateAttempt?.let {
withContext(Main) {
it.showInformationDialog(requireContext())
}
}
}
}
private fun bindAuthors(addon: Addon, view: View) {
view.author_text.text = addon.authors.joinToString { author ->
author.name
}.trim()
}
private fun bindDetails(addon: Addon, view: View) {
val detailsView = view.details
val detailsText = addon.translatedDescription
val parsedText = detailsText.replace("\n", "<br/>")
val text = HtmlCompat.fromHtml(parsedText, HtmlCompat.FROM_HTML_MODE_COMPACT)
detailsView.text = text
detailsView.movementMethod = LinkMovementMethod.getInstance()
}
private fun formatDate(text: String): String {
val formatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.getDefault())
return DateFormat.getDateInstance().format(formatter.parse(text)!!)
}
}

View File

@ -0,0 +1,112 @@
/* 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.addons
import android.net.Uri
import android.text.method.LinkMovementMethod
import android.view.View
import androidx.core.net.toUri
import androidx.core.text.HtmlCompat
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.fragment_add_on_details.*
import mozilla.components.feature.addons.Addon
import mozilla.components.feature.addons.ui.translatedDescription
import org.mozilla.fenix.R
import java.text.DateFormat
import java.text.NumberFormat
import java.text.SimpleDateFormat
import java.util.Locale
interface AddonDetailsInteractor {
/**
* Open the given addon siteUrl in the browser.
*/
fun openWebsite(addonSiteUrl: Uri)
/**
* Display the updater dialog.
*/
fun showUpdaterDialog(addon: Addon)
}
/**
* Shows the details of an add-on.
*/
class AddonDetailsView(
override val containerView: View,
private val interactor: AddonDetailsInteractor
) : LayoutContainer {
private val dateParser = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.getDefault())
private val dateFormatter = DateFormat.getDateInstance()
private val numberFormatter = NumberFormat.getNumberInstance(Locale.getDefault())
fun bind(addon: Addon) {
bindDetails(addon)
bindAuthors(addon)
bindVersion(addon)
bindLastUpdated(addon)
bindWebsite(addon)
bindRating(addon)
}
private fun bindRating(addon: Addon) {
addon.rating?.let { rating ->
val resources = containerView.resources
val ratingContentDescription =
resources.getString(R.string.mozac_feature_addons_rating_content_description)
rating_view.contentDescription = String.format(ratingContentDescription, rating.average)
rating_view.rating = rating.average
users_count.text = numberFormatter.format(rating.reviews)
}
}
private fun bindWebsite(addon: Addon) {
home_page_label.setOnClickListener {
interactor.openWebsite(addon.siteUrl.toUri())
}
}
private fun bindLastUpdated(addon: Addon) {
last_updated_text.text = formatDate(addon.updatedAt)
}
private fun bindVersion(addon: Addon) {
var version = addon.installedState?.version
if (version.isNullOrEmpty()) {
version = addon.version
}
version_text.text = version
if (addon.isInstalled()) {
version_text.setOnLongClickListener {
interactor.showUpdaterDialog(addon)
true
}
} else {
version_text.setOnLongClickListener(null)
}
}
private fun bindAuthors(addon: Addon) {
author_text.text = addon.authors.joinToString { author -> author.name }.trim()
}
private fun bindDetails(addon: Addon) {
val detailsText = addon.translatedDescription
val parsedText = detailsText.replace("\n", "<br/>")
val text = HtmlCompat.fromHtml(parsedText, HtmlCompat.FROM_HTML_MODE_COMPACT)
details.text = text
details.movementMethod = LinkMovementMethod.getInstance()
}
private fun formatDate(text: String): String {
return dateFormatter.format(dateParser.parse(text)!!)
}
}

View File

@ -13,7 +13,7 @@ import androidx.core.content.res.ResourcesCompat
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.navigation.Navigation
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.android.synthetic.main.fragment_add_ons_management.*
import kotlinx.android.synthetic.main.fragment_add_ons_management.view.*
@ -23,10 +23,10 @@ import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.launch
import mozilla.components.feature.addons.Addon
import mozilla.components.feature.addons.AddonManagerException
import mozilla.components.feature.addons.ui.AddonInstallationDialogFragment
import mozilla.components.feature.addons.ui.AddonsManagerAdapter
import mozilla.components.feature.addons.ui.AddonsManagerAdapterDelegate
import mozilla.components.feature.addons.ui.PermissionsDialogFragment
import mozilla.components.feature.addons.ui.AddonInstallationDialogFragment
import mozilla.components.feature.addons.ui.translatedName
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
@ -140,7 +140,7 @@ class AddonsManagementFragment : Fragment(R.layout.fragment_add_ons_management),
AddonsManagementFragmentDirections.actionAddonsManagementFragmentToInstalledAddonDetails(
addon
)
Navigation.findNavController(requireView()).navigate(directions)
findNavController().navigate(directions)
}
private fun showDetailsFragment(addon: Addon) {
@ -148,7 +148,7 @@ class AddonsManagementFragment : Fragment(R.layout.fragment_add_ons_management),
AddonsManagementFragmentDirections.actionAddonsManagementFragmentToAddonDetailsFragment(
addon
)
Navigation.findNavController(requireView()).navigate(directions)
findNavController().navigate(directions)
}
private fun showNotYetSupportedAddonFragment(unsupportedAddons: ArrayList<Addon>) {
@ -156,7 +156,7 @@ class AddonsManagementFragment : Fragment(R.layout.fragment_add_ons_management),
AddonsManagementFragmentDirections.actionAddonsManagementFragmentToNotYetSupportedAddonFragment(
unsupportedAddons.toTypedArray()
)
Navigation.findNavController(requireView()).navigate(directions)
findNavController().navigate(directions)
}
private fun findPreviousDialogFragment(): PermissionsDialogFragment? {

View File

@ -7,17 +7,6 @@ package org.mozilla.fenix.addons
import android.view.View
import androidx.fragment.app.Fragment
import org.mozilla.fenix.components.FenixSnackbar
import java.text.NumberFormat
import java.util.Locale
/**
* Get the formatted number amount for the current default locale.
*
* @param amount The number of addons to be formatted for the current default locale..
*/
internal fun getFormattedAmount(amount: Int): String {
return NumberFormat.getNumberInstance(Locale.getDefault()).format(amount)
}
/**
* Shows the Fenix Snackbar in the given view along with the provided text.

View File

@ -560,22 +560,19 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
view.swipeRefresh.setOnChildScrollUpCallback { _, _ -> true }
}
@Suppress("ConstantConditionIf")
if (!FeatureFlags.asFeatureWebChannelsDisabled) {
webchannelIntegration.set(
feature = FxaWebChannelFeature(
requireContext(),
customTabSessionId,
requireComponents.core.engine,
requireComponents.core.store,
requireComponents.backgroundServices.accountManager,
requireComponents.backgroundServices.serverConfig,
setOf(FxaCapability.CHOOSE_WHAT_TO_SYNC)
),
owner = this,
view = view
)
}
webchannelIntegration.set(
feature = FxaWebChannelFeature(
requireContext(),
customTabSessionId,
requireComponents.core.engine,
requireComponents.core.store,
requireComponents.backgroundServices.accountManager,
requireComponents.backgroundServices.serverConfig,
setOf(FxaCapability.CHOOSE_WHAT_TO_SYNC)
),
owner = this,
view = view
)
initializeEngineView(toolbarHeight)
}

View File

@ -81,23 +81,14 @@ class BackgroundServices(
// Enabling this for all channels is tracked in https://github.com/mozilla-mobile/fenix/issues/6704
secureStateAtRest = Config.channel.isNightlyOrDebug
)
// If sync has been turned off on the server then disable syncing.
@Suppress("ConstantConditionIf")
@VisibleForTesting(otherwise = PRIVATE)
val syncConfig = if (FeatureFlags.asFeatureSyncDisabled) {
null
@VisibleForTesting
val supportedEngines = if (FeatureFlags.syncedTabs) {
setOf(SyncEngine.History, SyncEngine.Bookmarks, SyncEngine.Passwords, SyncEngine.Tabs)
} else {
val supportedEngines = if (FeatureFlags.syncedTabs) {
setOf(SyncEngine.History, SyncEngine.Bookmarks, SyncEngine.Passwords, SyncEngine.Tabs)
} else {
setOf(SyncEngine.History, SyncEngine.Bookmarks, SyncEngine.Passwords)
}
SyncConfig(
supportedEngines,
syncPeriodInMinutes = 240L) // four hours
setOf(SyncEngine.History, SyncEngine.Bookmarks, SyncEngine.Passwords)
}
private val syncConfig = SyncConfig(supportedEngines, syncPeriodInMinutes = 240L) // four hours
init {
/* Make the "history", "bookmark", "passwords", and "tabs" stores accessible to workers

View File

@ -6,7 +6,6 @@ package org.mozilla.fenix.components
import android.content.Context
import mozilla.components.service.fxa.ServerConfig
import mozilla.components.service.fxa.ServerConfig.Server
import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.ext.settings
/**
@ -14,22 +13,15 @@ import org.mozilla.fenix.ext.settings
*/
object FxaServer {
const val CLIENT_ID = "a2270f727f45f648"
const val REDIRECT_URL = "https://accounts.firefox.com/oauth/success/$CLIENT_ID"
@Suppress("ConstantConditionIf", "UNUSED_PARAMETER")
fun redirectUrl(context: Context) = if (FeatureFlags.asFeatureWebChannelsDisabled) {
REDIRECT_URL
} else {
"urn:ietf:wg:oauth:2.0:oob:oauth-redirect-webchannel"
}
private const val CLIENT_ID = "a2270f727f45f648"
const val REDIRECT_URL = "urn:ietf:wg:oauth:2.0:oob:oauth-redirect-webchannel"
fun config(context: Context): ServerConfig {
val serverOverride = context.settings().overrideFxAServer
val tokenServerOverride = context.settings().overrideSyncTokenServer.ifEmpty { null }
if (serverOverride.isEmpty()) {
return ServerConfig(Server.RELEASE, CLIENT_ID, redirectUrl(context), tokenServerOverride)
return ServerConfig(Server.RELEASE, CLIENT_ID, REDIRECT_URL, tokenServerOverride)
}
return ServerConfig(serverOverride, CLIENT_ID, redirectUrl(context), tokenServerOverride)
return ServerConfig(serverOverride, CLIENT_ID, REDIRECT_URL, tokenServerOverride)
}
}

View File

@ -26,7 +26,7 @@ class Services(
private val accountManager: FxaAccountManager
) {
val accountsAuthFeature by lazy {
FirefoxAccountsAuthFeature(accountManager, FxaServer.redirectUrl(context)) { context, authUrl ->
FirefoxAccountsAuthFeature(accountManager, FxaServer.REDIRECT_URL) { context, authUrl ->
CoroutineScope(Dispatchers.Main).launch {
val intent = SupportUtils.createAuthCustomTabIntent(context, authUrl)
context.startActivity(intent)

View File

@ -35,7 +35,7 @@ interface BookmarkController {
fun handleBookmarkSharing(item: BookmarkNode)
fun handleOpeningBookmark(item: BookmarkNode, mode: BrowsingMode)
fun handleBookmarkDeletion(nodes: Set<BookmarkNode>, eventType: Event)
fun handleBookmarkFolderDeletion(node: BookmarkNode)
fun handleBookmarkFolderDeletion(nodes: Set<BookmarkNode>)
fun handleBackPressed()
}
@ -45,7 +45,7 @@ class DefaultBookmarkController(
private val navController: NavController,
private val showSnackbar: (String) -> Unit,
private val deleteBookmarkNodes: (Set<BookmarkNode>, Event) -> Unit,
private val deleteBookmarkFolder: (BookmarkNode) -> Unit,
private val deleteBookmarkFolder: (Set<BookmarkNode>) -> Unit,
private val invokePendingDeletion: () -> Unit
) : BookmarkController {
@ -94,8 +94,8 @@ class DefaultBookmarkController(
deleteBookmarkNodes(nodes, eventType)
}
override fun handleBookmarkFolderDeletion(node: BookmarkNode) {
deleteBookmarkFolder(node)
override fun handleBookmarkFolderDeletion(nodes: Set<BookmarkNode>) {
deleteBookmarkFolder(nodes)
}
override fun handleBackPressed() {

View File

@ -274,13 +274,17 @@ class BookmarkFragment : LibraryPageFragment<BookmarkNode>(), UserInteractionHan
}
private fun deleteMulti(selected: Set<BookmarkNode>, eventType: Event = Event.RemoveBookmarks) {
selected.forEach { if (it.type == BookmarkNodeType.FOLDER) {
showRemoveFolderDialog(selected)
return
} }
updatePendingBookmarksToDelete(selected)
pendingBookmarkDeletionJob = getDeleteOperation(eventType)
val message = when (eventType) {
is Event.RemoveBookmarks -> {
getRemoveBookmarksSnackBarMessage(selected)
getRemoveBookmarksSnackBarMessage(selected, containsFolders = false)
}
is Event.RemoveBookmarkFolder,
is Event.RemoveBookmark -> {
@ -301,9 +305,16 @@ class BookmarkFragment : LibraryPageFragment<BookmarkNode>(), UserInteractionHan
)
}
private fun getRemoveBookmarksSnackBarMessage(selected: Set<BookmarkNode>): String {
private fun getRemoveBookmarksSnackBarMessage(
selected: Set<BookmarkNode>,
containsFolders: Boolean
): String {
return if (selected.size > 1) {
getString(R.string.bookmark_deletion_multiple_snackbar_message_2)
return if (containsFolders) {
getString(R.string.bookmark_deletion_multiple_snackbar_message_3)
} else {
getString(R.string.bookmark_deletion_multiple_snackbar_message_2)
}
} else {
val bookmarkNode = selected.first()
getString(
@ -314,29 +325,38 @@ class BookmarkFragment : LibraryPageFragment<BookmarkNode>(), UserInteractionHan
}
}
private fun getDialogConfirmationMessage(selected: Set<BookmarkNode>): String {
return if (selected.size > 1) {
getString(R.string.bookmark_delete_multiple_folders_confirmation_dialog, getString(R.string.app_name))
} else {
getString(R.string.bookmark_delete_folder_confirmation_dialog)
}
}
override fun onDestroyView() {
super.onDestroyView()
_bookmarkInteractor = null
}
private fun showRemoveFolderDialog(selected: BookmarkNode) {
private fun showRemoveFolderDialog(selected: Set<BookmarkNode>) {
activity?.let { activity ->
AlertDialog.Builder(activity).apply {
setMessage(R.string.bookmark_delete_folder_confirmation_dialog)
val dialogConfirmationMessage = getDialogConfirmationMessage(selected)
setMessage(dialogConfirmationMessage)
setNegativeButton(R.string.delete_browsing_data_prompt_cancel) { dialog: DialogInterface, _ ->
dialog.cancel()
}
setPositiveButton(R.string.delete_browsing_data_prompt_allow) { dialog: DialogInterface, _ ->
updatePendingBookmarksToDelete(setOf(selected))
updatePendingBookmarksToDelete(selected)
pendingBookmarkDeletionJob = getDeleteOperation(Event.RemoveBookmarkFolder)
dialog.dismiss()
val message = getDeleteDialogString(selected)
val snackbarMessage = getRemoveBookmarksSnackBarMessage(selected, containsFolders = true)
viewLifecycleOwner.lifecycleScope.allowUndo(
requireView(),
message,
snackbarMessage,
getString(R.string.bookmark_undo_deletion),
{
undoPendingDeletion(setOf(selected))
undoPendingDeletion(selected)
},
operation = getDeleteOperation(Event.RemoveBookmarkFolder)
)
@ -353,14 +373,6 @@ class BookmarkFragment : LibraryPageFragment<BookmarkNode>(), UserInteractionHan
bookmarkInteractor.onBookmarksChanged(bookmarkTree)
}
private fun getDeleteDialogString(selected: BookmarkNode): String {
return getString(
R.string.bookmark_deletion_snackbar_message,
context?.components?.publicSuffixList?.let { selected.url?.toShortUrl(it) }
?: selected.title
)
}
private suspend fun undoPendingDeletion(selected: Set<BookmarkNode>) {
pendingBookmarksToDelete.removeAll(selected)
pendingBookmarkDeletionJob = null

View File

@ -90,7 +90,7 @@ class BookmarkFragmentInteractor(
null -> Event.RemoveBookmarks
}
if (eventType == Event.RemoveBookmarkFolder) {
bookmarksController.handleBookmarkFolderDeletion(nodes.first())
bookmarksController.handleBookmarkFolderDeletion(nodes)
} else {
bookmarksController.handleBookmarkDeletion(nodes, eventType)
}

View File

@ -33,6 +33,8 @@ class HistoryAdapter(
private var mode: HistoryFragmentState.Mode = HistoryFragmentState.Mode.Normal
override val selectedItems get() = mode.selectedItems
var pendingDeletionIds = emptySet<Long>()
private val itemsWithHeaders: MutableMap<HistoryItemTimeGroup, Int> = mutableMapOf()
override fun getItemViewType(position: Int): Int = HistoryListItemViewHolder.LAYOUT_ID
@ -48,13 +50,33 @@ class HistoryAdapter(
}
override fun onBindViewHolder(holder: HistoryListItemViewHolder, position: Int) {
val previous = if (position == 0) null else getItem(position - 1)
val current = getItem(position) ?: return
val headerForCurrentItem = timeGroupForHistoryItem(current)
val isPendingDeletion = pendingDeletionIds.contains(current.visitedAt)
var timeGroup: HistoryItemTimeGroup? = null
val previousHeader = previous?.let(::timeGroupForHistoryItem)
val currentHeader = timeGroupForHistoryItem(current)
val timeGroup = if (currentHeader != previousHeader) currentHeader else null
holder.bind(current, timeGroup, position == 0, mode)
// Add or remove the header and position to the map depending on it's deletion status
if (itemsWithHeaders.containsKey(headerForCurrentItem)) {
if (isPendingDeletion && itemsWithHeaders[headerForCurrentItem] == position) {
itemsWithHeaders.remove(headerForCurrentItem)
} else if (isPendingDeletion && itemsWithHeaders[headerForCurrentItem] != position) {
// do nothing
} else {
if (position <= itemsWithHeaders[headerForCurrentItem] as Int) {
itemsWithHeaders[headerForCurrentItem] = position
timeGroup = headerForCurrentItem
}
}
} else if (!isPendingDeletion) {
itemsWithHeaders[headerForCurrentItem] = position
timeGroup = headerForCurrentItem
}
holder.bind(current, timeGroup, position == 0, mode, isPendingDeletion)
}
fun updatePendingDeletionIds(pendingDeletionIds: Set<Long>) {
this.pendingDeletionIds = pendingDeletionIds
}
companion object {

View File

@ -17,8 +17,11 @@ import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavDirections
import androidx.navigation.fragment.findNavController
import kotlinx.android.synthetic.main.fragment_history.view.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
@ -31,17 +34,18 @@ import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.addons.showSnackBar
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
import org.mozilla.fenix.components.Components
import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.components.StoreProvider
import org.mozilla.fenix.components.history.createSynchronousPagedHistoryProvider
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.getStringWithArgSafe
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.showToolbar
import org.mozilla.fenix.ext.toShortUrl
import org.mozilla.fenix.library.LibraryPageFragment
import org.mozilla.fenix.utils.allowUndo
@SuppressWarnings("TooManyFunctions", "LargeClass")
class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandler {
@ -49,6 +53,8 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandl
private lateinit var historyView: HistoryView
private lateinit var historyInteractor: HistoryInteractor
private lateinit var viewModel: HistoryViewModel
private var undoScope: CoroutineScope? = null
private var pendingHistoryDeletionJob: (suspend () -> Unit)? = null
override fun onCreateView(
inflater: LayoutInflater,
@ -59,7 +65,10 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandl
historyStore = StoreProvider.get(this) {
HistoryFragmentStore(
HistoryFragmentState(
items = listOf(), mode = HistoryFragmentState.Mode.Normal
items = listOf(),
mode = HistoryFragmentState.Mode.Normal,
pendingDeletionIds = emptySet(),
isDeletingItems = false
)
)
}
@ -111,18 +120,18 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandl
}
private fun deleteHistoryItems(items: Set<HistoryItem>) {
val message = getMultiSelectSnackBarMessage(items)
viewLifecycleOwner.lifecycleScope.launch {
context?.components?.run {
for (item in items) {
analytics.metrics.track(Event.HistoryItemRemoved)
core.historyStorage.deleteVisit(item.url, item.visitedAt)
}
}
viewModel.invalidate()
showSnackBar(requireView(), message)
historyStore.dispatch(HistoryFragmentAction.ExitDeletionMode)
}
updatePendingHistoryToDelete(items)
undoScope = CoroutineScope(IO)
undoScope?.allowUndo(
requireView(),
getMultiSelectSnackBarMessage(items),
getString(R.string.bookmark_undo_deletion),
{
undoPendingDeletion(items)
},
getDeleteHistoryItemsOperation(items)
)
}
@ExperimentalCoroutinesApi
@ -146,8 +155,8 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandl
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
val menuRes = when (historyStore.state.mode) {
HistoryFragmentState.Mode.Normal -> R.menu.library_menu
is HistoryFragmentState.Mode.Syncing -> R.menu.library_menu
is HistoryFragmentState.Mode.Editing -> R.menu.history_select_multi
else -> return
}
inflater.inflate(menuRes, menu)
@ -162,13 +171,8 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandl
true
}
R.id.delete_history_multi_select -> {
val message = getMultiSelectSnackBarMessage(selectedItems)
viewLifecycleOwner.lifecycleScope.launch(Main) {
deleteSelectedHistory(historyStore.state.mode.selectedItems, requireComponents)
viewModel.invalidate()
historyStore.dispatch(HistoryFragmentAction.ExitDeletionMode)
showSnackBar(requireView(), message)
}
deleteHistoryItems(historyStore.state.mode.selectedItems)
historyStore.dispatch(HistoryFragmentAction.ExitEditMode)
true
}
R.id.open_history_in_new_tabs_multi_select -> {
@ -177,10 +181,7 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandl
selectedItem.url
}
nav(
R.id.historyFragment,
HistoryFragmentDirections.actionGlobalHome()
)
navigate(HistoryFragmentDirections.actionGlobalHome())
true
}
R.id.open_history_in_private_tabs_multi_select -> {
@ -193,10 +194,7 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandl
browsingModeManager.mode = BrowsingMode.Private
supportActionBar?.hide()
}
nav(
R.id.historyFragment,
HistoryFragmentDirections.actionGlobalHome()
)
navigate(HistoryFragmentDirections.actionGlobalHome())
true
}
else -> super.onOptionsItemSelected(item)
@ -206,14 +204,22 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandl
return if (historyItems.size > 1) {
getString(R.string.history_delete_multiple_items_snackbar)
} else {
getString(
requireContext().getStringWithArgSafe(
R.string.history_delete_single_item_snackbar,
historyItems.first().url.toShortUrl(requireComponents.publicSuffixList)
)
}
}
override fun onBackPressed(): Boolean = historyView.onBackPressed()
override fun onPause() {
invokePendingDeletion()
super.onPause()
}
override fun onBackPressed(): Boolean {
invokePendingDeletion()
return historyView.onBackPressed()
}
private fun openItem(item: HistoryItem, mode: BrowsingMode? = null) {
requireComponents.analytics.metrics.track(Event.HistoryItemOpened)
@ -253,23 +259,58 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandl
}
}
private suspend fun deleteSelectedHistory(
selected: Set<HistoryItem>,
components: Components = requireComponents
) {
requireComponents.analytics.metrics.track(Event.HistoryItemRemoved)
val storage = components.core.historyStorage
for (item in selected) {
storage.deleteVisit(item.url, item.visitedAt)
}
}
private fun share(data: List<ShareData>) {
requireComponents.analytics.metrics.track(Event.HistoryItemShared)
val directions = HistoryFragmentDirections.actionGlobalShareFragment(
data = data.toTypedArray()
)
nav(R.id.historyFragment, directions)
navigate(directions)
}
private fun navigate(directions: NavDirections) {
invokePendingDeletion()
findNavController().nav(
R.id.historyFragment,
directions
)
}
private fun getDeleteHistoryItemsOperation(items: Set<HistoryItem>): (suspend () -> Unit) {
return {
CoroutineScope(IO).launch {
historyStore.dispatch(HistoryFragmentAction.EnterDeletionMode)
context?.components?.run {
for (item in items) {
analytics.metrics.track(Event.HistoryItemRemoved)
core.historyStorage.deleteVisit(item.url, item.visitedAt)
}
}
historyStore.dispatch(HistoryFragmentAction.ExitDeletionMode)
pendingHistoryDeletionJob = null
}
}
}
private fun updatePendingHistoryToDelete(items: Set<HistoryItem>) {
pendingHistoryDeletionJob = getDeleteHistoryItemsOperation(items)
val ids = items.map { item -> item.visitedAt }.toSet()
historyStore.dispatch(HistoryFragmentAction.AddPendingDeletionSet(ids))
}
private fun undoPendingDeletion(items: Set<HistoryItem>) {
pendingHistoryDeletionJob = null
val ids = items.map { item -> item.visitedAt }.toSet()
historyStore.dispatch(HistoryFragmentAction.UndoRemovePendingDeletionSet(ids))
}
private fun invokePendingDeletion() {
pendingHistoryDeletionJob?.let {
viewLifecycleOwner.lifecycleScope.launch {
it.invoke()
}.invokeOnCompletion {
pendingHistoryDeletionJob = null
}
}
}
private suspend fun syncHistory() {

View File

@ -30,6 +30,8 @@ sealed class HistoryFragmentAction : Action {
object ExitEditMode : HistoryFragmentAction()
data class AddItemForRemoval(val item: HistoryItem) : HistoryFragmentAction()
data class RemoveItemForRemoval(val item: HistoryItem) : HistoryFragmentAction()
data class AddPendingDeletionSet(val itemIds: Set<Long>) : HistoryFragmentAction()
data class UndoRemovePendingDeletionSet(val itemIds: Set<Long>) : HistoryFragmentAction()
object EnterDeletionMode : HistoryFragmentAction()
object ExitDeletionMode : HistoryFragmentAction()
object StartSync : HistoryFragmentAction()
@ -41,12 +43,16 @@ sealed class HistoryFragmentAction : Action {
* @property items List of HistoryItem to display
* @property mode Current Mode of History
*/
data class HistoryFragmentState(val items: List<HistoryItem>, val mode: Mode) : State {
data class HistoryFragmentState(
val items: List<HistoryItem>,
val mode: Mode,
val pendingDeletionIds: Set<Long>,
val isDeletingItems: Boolean
) : State {
sealed class Mode {
open val selectedItems = emptySet<HistoryItem>()
object Normal : Mode()
object Deleting : Mode()
object Syncing : Mode()
data class Editing(override val selectedItems: Set<HistoryItem>) : Mode()
}
@ -73,9 +79,17 @@ private fun historyStateReducer(
)
}
is HistoryFragmentAction.ExitEditMode -> state.copy(mode = HistoryFragmentState.Mode.Normal)
is HistoryFragmentAction.EnterDeletionMode -> state.copy(mode = HistoryFragmentState.Mode.Deleting)
is HistoryFragmentAction.ExitDeletionMode -> state.copy(mode = HistoryFragmentState.Mode.Normal)
is HistoryFragmentAction.EnterDeletionMode -> state.copy(isDeletingItems = true)
is HistoryFragmentAction.ExitDeletionMode -> state.copy(isDeletingItems = false)
is HistoryFragmentAction.StartSync -> state.copy(mode = HistoryFragmentState.Mode.Syncing)
is HistoryFragmentAction.FinishSync -> state.copy(mode = HistoryFragmentState.Mode.Normal)
is HistoryFragmentAction.AddPendingDeletionSet ->
state.copy(
pendingDeletionIds = state.pendingDeletionIds + action.itemIds
)
is HistoryFragmentAction.UndoRemovePendingDeletionSet ->
state.copy(
pendingDeletionIds = state.pendingDeletionIds - action.itemIds
)
}
}

View File

@ -90,7 +90,6 @@ class HistoryView(
val view: View = LayoutInflater.from(container.context)
.inflate(R.layout.component_history, container, true)
private var items: List<HistoryItem> = listOf()
var mode: HistoryFragmentState.Mode = HistoryFragmentState.Mode.Normal
private set
@ -116,13 +115,16 @@ class HistoryView(
fun update(state: HistoryFragmentState) {
val oldMode = mode
view.progress_bar.isVisible = state.mode === HistoryFragmentState.Mode.Deleting
view.progress_bar.isVisible = state.isDeletingItems
view.swipe_refresh.isRefreshing = state.mode === HistoryFragmentState.Mode.Syncing
view.swipe_refresh.isEnabled =
state.mode === HistoryFragmentState.Mode.Normal || state.mode === HistoryFragmentState.Mode.Syncing
items = state.items
mode = state.mode
historyAdapter.updatePendingDeletionIds(state.pendingDeletionIds)
updateEmptyState(state.pendingDeletionIds.size != historyAdapter.currentList?.size)
historyAdapter.updateMode(state.mode)
val first = layoutManager.findFirstVisibleItemPosition()
val last = layoutManager.findLastVisibleItemPosition() + 1

View File

@ -11,13 +11,13 @@ import kotlinx.android.synthetic.main.library_site_item.view.*
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.hideAndDisable
import org.mozilla.fenix.ext.showAndEnable
import org.mozilla.fenix.utils.Do
import org.mozilla.fenix.library.SelectionHolder
import org.mozilla.fenix.library.history.HistoryFragmentState
import org.mozilla.fenix.library.history.HistoryInteractor
import org.mozilla.fenix.library.history.HistoryItem
import org.mozilla.fenix.library.history.HistoryItemMenu
import org.mozilla.fenix.library.history.HistoryItemTimeGroup
import org.mozilla.fenix.utils.Do
class HistoryListItemViewHolder(
view: View,
@ -44,8 +44,15 @@ class HistoryListItemViewHolder(
item: HistoryItem,
timeGroup: HistoryItemTimeGroup?,
showDeleteButton: Boolean,
mode: HistoryFragmentState.Mode
mode: HistoryFragmentState.Mode,
isPendingDeletion: Boolean = false
) {
if (isPendingDeletion) {
itemView.history_layout.visibility = View.GONE
} else {
itemView.history_layout.visibility = View.VISIBLE
}
itemView.history_layout.titleView.text = item.title
itemView.history_layout.urlView.text = item.url

View File

@ -3,14 +3,14 @@
- 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/. -->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_marginTop="6dp"
android:layout_marginBottom="6dp">
<RelativeLayout
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
@ -21,115 +21,137 @@
android:id="@+id/details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:textColor="?primaryText"
android:textColorLink="?aboutLink"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="@tools:sample/lorem/random" />
<TextView
android:id="@+id/author_label"
style="@style/AboutHeaderContentText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/details"
style="@style/AboutHeaderContentText"
android:text="@string/mozac_feature_addons_authors" />
android:layout_marginTop="20dp"
android:text="@string/mozac_feature_addons_authors"
app:layout_constraintEnd_toStartOf="@+id/author_text"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/details" />
<TextView
android:id="@+id/author_text"
style="@style/AboutHeaderContentText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/details"
android:layout_alignParentEnd="true"
style="@style/AboutHeaderContentText"
android:layout_marginTop="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/author_label"
app:layout_constraintTop_toBottomOf="@+id/details"
tools:text="@tools:sample/full_names" />
<View
android:id="@+id/author_divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_below="@+id/author_text"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:background="?android:attr/listDivider"
android:importantForAccessibility="no" />
android:importantForAccessibility="no"
app:layout_constraintTop_toBottomOf="@id/author_text"
tools:layout_editor_absoluteX="16dp" />
<TextView
android:id="@+id/version_label"
style="@style/AboutHeaderContentText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/author_divider"
style="@style/AboutHeaderContentText"
android:text="@string/mozac_feature_addons_version" />
android:layout_marginTop="10dp"
android:text="@string/mozac_feature_addons_version"
app:layout_constraintEnd_toStartOf="@+id/version_text"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/author_divider"
tools:layout_editor_absoluteY="52dp" />
<TextView
android:id="@+id/version_text"
style="@style/AboutHeaderContentText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/author_divider"
android:layout_alignParentEnd="true"
style="@style/AboutHeaderContentText"
android:layout_marginTop="10dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/version_label"
app:layout_constraintTop_toBottomOf="@+id/author_divider"
tools:text="1.2.3" />
<View
android:id="@+id/version_divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_below="@+id/version_text"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:background="?android:attr/listDivider"
android:importantForAccessibility="no" />
android:importantForAccessibility="no"
app:layout_constraintTop_toBottomOf="@+id/version_text" />
<TextView
android:id="@+id/last_updated_label"
style="@style/AboutHeaderContentText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/version_divider"
style="@style/AboutHeaderContentText"
android:text="@string/mozac_feature_addons_last_updated" />
android:layout_marginTop="10dp"
android:text="@string/mozac_feature_addons_last_updated"
app:layout_constraintEnd_toStartOf="@+id/last_updated_text"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/version_divider" />
<TextView
android:id="@+id/last_updated_text"
style="@style/AboutHeaderContentText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/version_divider"
android:layout_alignParentEnd="true"
style="@style/AboutHeaderContentText"
android:layout_marginTop="10dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/last_updated_label"
app:layout_constraintTop_toBottomOf="@+id/version_divider"
tools:text="Oct 16, 2019" />
<View
android:id="@+id/last_updated_divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_below="@+id/last_updated_text"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:background="?android:attr/listDivider"
android:importantForAccessibility="no" />
android:importantForAccessibility="no"
app:layout_constraintTop_toBottomOf="@+id/last_updated_text" />
<TextView
android:id="@+id/home_page_label"
style="@style/AboutHeaderContentText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/last_updated_divider"
style="@style/AboutHeaderContentText"
android:layout_marginTop="10dp"
android:text="@string/mozac_feature_addons_home_page"
android:textColor="?aboutLink"
android:text="@string/mozac_feature_addons_home_page" />
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/last_updated_divider" />
<View
android:id="@+id/home_page_divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_below="@+id/home_page_label"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:background="?android:attr/listDivider"
android:importantForAccessibility="no" />
android:importantForAccessibility="no"
app:layout_constraintTop_toBottomOf="@+id/home_page_label" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/home_page_divider">
android:layout_marginTop="10dp"
app:layout_constraintTop_toBottomOf="@+id/home_page_divider">
<TextView
android:id="@+id/rating_label"
@ -171,5 +193,5 @@
</androidx.constraintlayout.widget.ConstraintLayout>
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

View File

@ -1134,8 +1134,16 @@
<string name="quick_settings_sheet_secure_connection">Ασφαλής σύνδεση</string>
<!-- Label that indicates a site is using a insecure connection -->
<string name="quick_settings_sheet_insecure_connection">Μη ασφαλής σύνδεση</string>
<!-- Confirmation message for a dialog confirming if the user wants to delete all the permissions for all sites-->
<string name="confirm_clear_permissions_on_all_sites">Θέλετε σίγουρα να διαγράψετε όλα τα δικαιώματα σε όλες τις ιστοσελίδες;</string>
<!-- Confirmation message for a dialog confirming if the user wants to delete all the permissions for a site-->
<string name="confirm_clear_permissions_site">Θέλετε σίγουρα να διαγράψετε όλα τα δικαιώματα για αυτή την ιστοσελίδα;</string>
<!-- Confirmation message for a dialog confirming if the user wants to set default value a permission for a site-->
<string name="confirm_clear_permission_site">Θέλετε σίγουρα να διαγράψετε αυτό το δικαίωμα για αυτή την ιστοσελίδα;</string>
<!-- Label for the Pocket default top site -->
<string name="pocket_top_articles">Κορυφαία άρθρα</string>
<!-- Bookmark deletion confirmation -->
<string name="bookmark_deletion_confirmation">Θέλετε σίγουρα να διαγράψετε αυτό το σελιδοδείκτη;</string>
<!-- Browser menu button that adds a top site to the home fragment -->
<string name="browser_menu_add_to_top_sites">Προσθήκη στις κορυφαίες ιστοσελίδες</string>
<!-- text shown before the issuer name to indicate who its verified by, parameter is the name of

View File

@ -13,8 +13,6 @@
<string name="content_description_disable_private_browsing_button">Desactivar la navegación privada</string>
<!-- Placeholder text shown in the search bar before a user enters text -->
<string name="search_hint">Buscar o ingresar dirección</string>
<!-- No Open Tabs Message Header -->
<string name="no_open_tabs_header_2">No hay pestañas abiertas</string>
<!-- No Open Tabs Message Description -->
<string name="no_open_tabs_description">Tus pestañas abiertas se mostrarán aquí.</string>
@ -293,7 +291,7 @@
<!-- Preference for syncing logins -->
<string name="preferences_sync_logins">Credenciales</string>
<!-- Preference for syncing tabs -->
<string name="preferences_sync_tabs">Pestañas</string>
<string name="preferences_sync_tabs_2">Pestañas abiertas</string>
<!-- Preference for signing out -->
<string name="preferences_sign_out">Salir</string>
<!-- Preference displays and allows changing current FxA device name -->
@ -480,7 +478,7 @@
<!-- Open tabs menu item to share all tabs -->
<string name="tabs_menu_share_tabs">Compartir pestañas</string>
<!-- Open tabs menu item to save tabs to collection -->
<string name="tabs_menu_save_to_collection">Guardar en la colección</string>
<string name="tabs_menu_save_to_collection1">Guardar pestañas en la colección</string>
<!-- Content description (not visible, for screen readers etc.): Opens the tab menu when pressed -->
<string name="tab_menu">Menú de pestañas</string>
<!-- Tab menu item to share the tab -->
@ -692,16 +690,10 @@
<string name="delete_browsing_data_quit_off">No</string>
<!-- Collections -->
<!-- Label to describe what collections are to a new user without any collections -->
<string name="collections_description">Colecciona las cosas que te importan. Para empezar, guarda pestañas abiertas en una nueva colección.</string>
<!-- Collections header on home fragment -->
<string name="collections_header">Colecciones</string>
<!-- Content description (not visible, for screen readers etc.): Opens the collection menu when pressed -->
<string name="collection_menu_button_content_description">Menú de colecciones</string>
<!-- No Open Tabs Message Header -->
<string name="no_collections_header">No hay colecciones</string>
<!-- No Open Tabs Message Description -->
<string name="no_collections_description">Tus colecciones se mostrarán aquí.</string>
<!-- Title for the "select tabs" step of the collection creator -->
<string name="create_collection_select_tabs">Seleccionar pestañas</string>
<!-- Title for the "select collection" step of the collection creator -->
@ -907,8 +899,9 @@
<!-- text for firefox preview moving tip description -->
<string name="tip_firefox_preview_moved_description">Firefox Nightly se actualiza todas las noches y tiene funciones experimentales nuevas.
No obstante, puede ser menos estable. Baja nuestro navegador beta para una experiencia más estable.</string>
<!-- text for firefox preview moving tip button. "Mozilla Firefox Browser" is intentionally hardcoded -->
<string name="tip_firefox_preview_moved_button">Obtén el navegador Mozilla Firefox</string>
<!-- text for firefox preview moving tip button. "Firefox for Android Beta" is intentionally hardcoded -->
<string name="tip_firefox_preview_moved_button_2">Obtener Firefox para Android Beta</string>
<!-- text for firefox preview moving tip header. "Firefox Nightly" is intentionally hardcoded -->
<string name="tip_firefox_preview_moved_header_preview_installed">Firefox Nightly se mudó</string>
@ -1415,6 +1408,9 @@
<!-- Voice search prompt description displayed after the user presses the voice search button -->
<string name="voice_search_explainer">Habla ahora</string>
<!-- The error message in edit login view when a duplicate username exists. -->
<string name="saved_login_duplicate">Ya existe una credencial con ese nombre de usuario</string>
<!-- Synced Tabs -->
<!-- Text displayed when user is not logged into a Firefox Account -->
<string name="synced_tabs_connect_to_sync_account">Conectarse con una cuenta de Firefox.</string>
@ -1424,5 +1420,19 @@
<string name="synced_tabs_reauth">Por favor, vuelve a autentificarte.</string>
<!-- Text displayed when user has disabled tab syncing in Firefox Sync Account -->
<string name="synced_tabs_enable_tab_syncing">Por favor, habilita la sincronización de pestañas.</string>
<!-- Text displayed when user has no tabs that have been synced -->
<string name="synced_tabs_no_tabs">No tienes ninguna pestaña abierta en Firefox en tus otros dispositivos.</string>
<!-- Text displayed in the synced tabs screen when a user is not signed in to Firefox Sync describing Synced Tabs -->
<string name="synced_tabs_sign_in_message">Ver una lista de las pestañas de tus otros dispositivos.</string>
<!-- Text displayed on a button in the synced tabs screen to link users to sign in when a user is not signed in to Firefox Sync -->
<string name="synced_tabs_sign_in_button">Conectarse para sincronizar</string>
<!-- Top Sites -->
<!-- Title text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_title">Límite de sitios frecuentes alcanzado</string>
<!-- Content description text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_content">Para añadir un nuevo sitio frecuente, elimina uno. Mantén presionado el sitio y selecciona eliminar.</string>
<!-- Confirmation dialog button text when top sites limit is reached. -->
<string name="top_sites_max_limit_confirmation_button">Ok, ¡ya caché!</string>
</resources>

View File

@ -15,14 +15,17 @@
<string name="content_description_disable_private_browsing_button">Poista yksityinen selaus käytöstä</string>
<!-- Placeholder text shown in the search bar before a user enters text -->
<string name="search_hint">Hae tai kirjoita osoite</string>
<!-- No Open Tabs Message Header -->
<string name="no_open_tabs_header_2">Ei avoimia välilehtiä</string>
<!-- No Open Tabs Message Description -->
<string name="no_open_tabs_description">Avoimet välilehdet näytetään tässä.</string>
<!-- No Private Tabs Message Description -->
<string name="no_private_tabs_description">Yksityiset välilehdet näkyvät tässä.</string>
<!-- Message announced to the user when tab tray is selected with 1 tab -->
<string name="open_tab_tray_single">1 avoin välilehti. Napauta vaihtaaksesi.</string>
<!-- Message announced to the user when tab tray is selected with 0 or 2+ tabs -->
<string name="open_tab_tray_plural">%1$s avointa välilehteä. Napauta vaihtaaksesi.</string>
<!-- About content. The first parameter is the name of the application. (For example: Fenix) -->
<string name="about_content">%1$s on Mozillan tuote.</string>
@ -299,7 +302,7 @@
<!-- Preference for syncing logins -->
<string name="preferences_sync_logins">Kirjautumistiedot</string>
<!-- Preference for syncing tabs -->
<string name="preferences_sync_tabs">Välilehdet</string>
<string name="preferences_sync_tabs_2">Avoimet välilehdet</string>
<!-- Preference for signing out -->
<string name="preferences_sign_out">Kirjaudu ulos</string>
<!-- Preference displays and allows changing current FxA device name -->
@ -472,6 +475,8 @@
<string name="tab_tray_menu_open_new_tab">Uusi välilehti</string>
<!-- Shortcut action to open the home screen -->
<string name="tab_tray_menu_home">Siirry kotiin</string>
<!-- Shortcut action to toggle private mode -->
<string name="tab_tray_menu_toggle">Vaihda välilehtitilaa</string>
<!-- 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">Poista välilehti kokoelmasta</string>
<!-- Content description (not visible, for screen readers etc.): Close tab button. Closes the current session when pressed -->
@ -485,7 +490,7 @@
<!-- Open tabs menu item to share all tabs -->
<string name="tabs_menu_share_tabs">Jaa välilehdet</string>
<!-- Open tabs menu item to save tabs to collection -->
<string name="tabs_menu_save_to_collection">Tallenna kokoelmaan</string>
<string name="tabs_menu_save_to_collection1">Tallenna välilehdet kokoelmaan</string>
<!-- Content description (not visible, for screen readers etc.): Opens the tab menu when pressed -->
<string name="tab_menu">Välilehtivalikko</string>
<!-- Tab menu item to share the tab -->
@ -701,16 +706,14 @@
<string name="delete_browsing_data_quit_off">Pois</string>
<!-- Collections -->
<!-- Label to describe what collections are to a new user without any collections -->
<string name="collections_description">Kerää merkitykselliset asiat yhteen. Aloita tallentamalla avoimet välilehdet uudeksi kokoelmaksi.</string>
<!-- Collections header on home fragment -->
<string name="collections_header">Kokoelmat</string>
<!-- Content description (not visible, for screen readers etc.): Opens the collection menu when pressed -->
<string name="collection_menu_button_content_description">Kokoelmavalikko</string>
<!-- No Open Tabs Message Header -->
<string name="no_collections_header">Ei kokoelmia</string>
<!-- No Open Tabs Message Description -->
<string name="no_collections_description">Kokoelmasi näytetään tässä.</string>
<string name="no_collections_header1">Kerää yhteen sinulle tärkeät asiat</string>
<!-- Label to describe what collections are to a new user without any collections -->
<string name="no_collections_description1">Kokoa yhteen samanlaiset haut, sivustot ja välilehdet nopeaa käyttöä varten.</string>
<!-- Title for the "select tabs" step of the collection creator -->
<string name="create_collection_select_tabs">Valitse välilehdet</string>
<!-- Title for the "select collection" step of the collection creator -->
@ -740,6 +743,9 @@
<!-- Button to save currently selected tabs in the "select tabs" step of the collection creator-->
<string name="create_collection_save">Tallenna</string>
<!-- Snackbar action to view the collection the user just created or updated -->
<string name="create_collection_view">Näytä</string>
<!-- Default name for a new collection in "name new collection" step of the collection creator. %d is a placeholder for the number of collections-->
<string name="create_collection_default_name">Kokoelma %d</string>
@ -919,8 +925,9 @@
<!-- text for firefox preview moving tip description -->
<string name="tip_firefox_preview_moved_description">Firefox Nightly saa päivityksiä joka yö ja se sisältää kokeellisia uusia ominaisuuksia.
Se saattaa kuitenkin olla betaversiota epävakaampi. Lataa betavaiheen selain Nightlya vakaamman kokemuksen saamiseksi.</string>
<!-- text for firefox preview moving tip button. "Mozilla Firefox Browser" is intentionally hardcoded -->
<string name="tip_firefox_preview_moved_button">Hanki Mozilla Firefox -selain</string>
<!-- text for firefox preview moving tip button. "Firefox for Android Beta" is intentionally hardcoded -->
<string name="tip_firefox_preview_moved_button_2">Hanki Firefoxin betaversio Androidille</string>
<!-- text for firefox preview moving tip header. "Firefox Nightly" is intentionally hardcoded -->
<string name="tip_firefox_preview_moved_header_preview_installed">Firefox Nightly on muuttanut</string>
@ -975,12 +982,19 @@
<string name="onboarding_firefox_account_automatic_signin_failed">Sisäänkirjautuminen epäonnistui</string>
<!-- text for the tracking protection onboarding card header -->
<string name="onboarding_tracking_protection_header_2">Automaattinen yksityisyys</string>
<!-- text for the tracking protection card description
The first parameter is the name of the app (e.g. Firefox Preview) -->
<string name="onboarding_tracking_protection_description_2">Tietosuoja- ja suojausasetukset estävät seuraimia, haittaohjelmia ja sinua seuraavia yrityksiä.</string>
<!-- text for tracking protection radio button option for standard level of blocking -->
<string name="onboarding_tracking_protection_standard_button_2">Tavallinen (oletus)</string>
<!-- text for standard blocking option button description -->
<string name="onboarding_tracking_protection_standard_button_description_2">Estää vähemmän seuraimia. Sivut latautuvat normaalisti.</string>
<!-- text for tracking protection radio button option for strict level of blocking -->
<string name="onboarding_tracking_protection_strict_button">Tiukka (suositeltu)</string>
<!-- text for tracking protection radio button option for strict level of blocking -->
<string name="onboarding_tracking_protection_strict_option">Tiukka</string>
<!-- text for strict blocking option button description -->
<string name="onboarding_tracking_protection_strict_button_description_2">Estää enemmän seuraimia, mainoksia ja ponnahdusikkunoita. Sivut latautuvat nopeammin, mutta osa toiminnoista ei välttämättä toimi.</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 -->
@ -1069,10 +1083,14 @@
<string name="preference_enhanced_tracking_protection_explanation_learn_more">Lue lisää</string>
<!-- Preference for enhanced tracking protection for the standard protection settings -->
<string name="preference_enhanced_tracking_protection_standard_default_1">Tavallinen (oletus)</string>
<!-- Preference description for enhanced tracking protection for the standard protection settings -->
<string name="preference_enhanced_tracking_protection_standard_description_3">Estää vähemmän seuraimia. Sivut latautuvat normaalisti.</string>
<!-- Accessibility text for the Standard protection information icon -->
<string name="preference_enhanced_tracking_protection_standard_info_button">Mitä tavallinen seurannan suojaus estää</string>
<!-- Preference for enhanced tracking protection for the strict protection settings -->
<string name="preference_enhanced_tracking_protection_strict">Tiukka</string>
<!-- Preference description for enhanced tracking protection for the strict protection settings -->
<string name="preference_enhanced_tracking_protection_strict_description_2">Estää enemmän seuraimia, mainoksia ja ponnahdusikkunoita. Sivut latautuvat nopeammin, mutta osa toiminnoista ei välttämättä toimi.</string>
<!-- Accessibility text for the Strict protection information icon -->
<string name="preference_enhanced_tracking_protection_strict_info_button">Mitä tiukka seurannan suojaus estää</string>
<!-- Preference for enhanced tracking protection for the custom protection settings -->
@ -1127,6 +1145,8 @@
<string name="etp_tracking_content_title">Seurantaan tarkoitettu sisältö</string>
<!-- Description of tracking content that can be blocked by Enhanced Tracking Protection -->
<string name="etp_tracking_content_description">Estää lataamasta ulkopuolisia mainoksia, videoita ja muuta sisältöä, joka sisältää seurantakoodin. Voi vaikuttaa joidenkin verkkosivustojen toimimiseen.</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">Aina kun kilven väri on violetti, %s on estänyt seuraimia sivustolla. Napauta saadaksesi lisätietoja.</string>
<!-- Enhanced Tracking Protection message that protection is currently on for this site -->
<string name="etp_panel_on">Suojaukset ovat PÄÄLLÄ tällä sivustolla</string>
<!-- Enhanced Tracking Protection message that protection is currently off for this site -->
@ -1413,6 +1433,9 @@
<!-- Voice search prompt description displayed after the user presses the voice search button -->
<string name="voice_search_explainer">Puhu nyt</string>
<!-- The error message in edit login view when a duplicate username exists. -->
<string name="saved_login_duplicate">Kirjautumistieto tällä käyttäjänimellä on jo olemassa</string>
<!-- Synced Tabs -->
<!-- Text displayed when user is not logged into a Firefox Account -->
<string name="synced_tabs_connect_to_sync_account">Yhdistä Firefox-tilillä.</string>
@ -1422,5 +1445,19 @@
<string name="synced_tabs_reauth">Tunnistaudu uudelleen.</string>
<!-- Text displayed when user has disabled tab syncing in Firefox Sync Account -->
<string name="synced_tabs_enable_tab_syncing">Ota välilehtien synkronointi käyttöön.</string>
<!-- Text displayed when user has no tabs that have been synced -->
<string name="synced_tabs_no_tabs">Sinulla ei ole muilla laitteilla avoimia välilehtiä Firefoxissa.</string>
<!-- Text displayed in the synced tabs screen when a user is not signed in to Firefox Sync describing Synced Tabs -->
<string name="synced_tabs_sign_in_message">Katso välilehtilistaus muilta laitteiltasi.</string>
<!-- Text displayed on a button in the synced tabs screen to link users to sign in when a user is not signed in to Firefox Sync -->
<string name="synced_tabs_sign_in_button">Kirjaudu sisään synkronoidaksesi</string>
<!-- Top Sites -->
<!-- Title text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_title">Ykkössivustojen määrä täynnä</string>
<!-- Content description text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_content">Lisätäksesi uuden ykkössivuston, poista yksi olemassa oleva. Paina pitkään sivustoa ja valitse poista.</string>
<!-- Confirmation dialog button text when top sites limit is reached. -->
<string name="top_sites_max_limit_confirmation_button">Selvä</string>
</resources>

View File

@ -13,14 +13,18 @@
<string name="content_description_disable_private_browsing_button">Priveenavigaasje útskeakelje</string>
<!-- Placeholder text shown in the search bar before a user enters text -->
<string name="search_hint">Fier sykterm of adres yn</string>
<!-- No Open Tabs Message Header -->
<string name="no_open_tabs_header_2">Gjin iepen ljepblêden</string>
<!-- No Open Tabs Message Description -->
<string name="no_open_tabs_description">Jo iepene ljepblêden wurde hjir werjûn.</string>
<!-- No Private Tabs Message Description -->
<string name="no_private_tabs_description">Jo priveeljepblêden wurde hjir werjûn.</string>
<!-- Message announced to the user when tab tray is selected with 1 tab -->
<string name="open_tab_tray_single">1 iepen ljepblêd. Tik om tusken ljepblêden te wikseljen.</string>
<!-- Message announced to the user when tab tray is selected with 0 or 2+ tabs -->
<string name="open_tab_tray_plural">%1$s iepen ljepblêden. Tik om tusken ljepblêden te wikseljen.</string>
<!-- About content. The first parameter is the name of the application. (For example: Fenix) -->
<string name="about_content">%1$s is makke troch Mozilla.</string>
@ -295,7 +299,7 @@
<!-- Preference for syncing logins -->
<string name="preferences_sync_logins">Oanmeldingen</string>
<!-- Preference for syncing tabs -->
<string name="preferences_sync_tabs">Ljepblêden</string>
<string name="preferences_sync_tabs_2">Iepen ljeplêden</string>
<!-- Preference for signing out -->
<string name="preferences_sign_out">Ofmelde</string>
<!-- Preference displays and allows changing current FxA device name -->
@ -480,7 +484,7 @@
<!-- Open tabs menu item to share all tabs -->
<string name="tabs_menu_share_tabs">Ljepblêden diele</string>
<!-- Open tabs menu item to save tabs to collection -->
<string name="tabs_menu_save_to_collection">Yn kolleksje bewarje</string>
<string name="tabs_menu_save_to_collection1">Ljepblêden yn kolleksje bewarje</string>
<!-- Content description (not visible, for screen readers etc.): Opens the tab menu when pressed -->
<string name="tab_menu">Ljepblêdmenu</string>
<!-- Tab menu item to share the tab -->
@ -691,16 +695,14 @@
<string name="delete_browsing_data_quit_off">Ut</string>
<!-- Collections -->
<!-- Label to describe what collections are to a new user without any collections -->
<string name="collections_description">Sammelje de dingen dy\'t wichtich foar jo binne. Bewarje earst iepen ljepblêden yn in nije kolleksje.</string>
<!-- Collections header on home fragment -->
<string name="collections_header">Kolleksjes</string>
<!-- Content description (not visible, for screen readers etc.): Opens the collection menu when pressed -->
<string name="collection_menu_button_content_description">Kolleksjesmenu</string>
<!-- No Open Tabs Message Header -->
<string name="no_collections_header">Gjin kolleksjes</string>
<!-- No Open Tabs Message Description -->
<string name="no_collections_description">Jo kolleksjes wurde hjir toand.</string>
<string name="no_collections_header1">Sammelje de dingen dy\'t wichtich foar jo binne</string>
<!-- Label to describe what collections are to a new user without any collections -->
<string name="no_collections_description1">Groepearje fergelykbere sykopdrachten, websites en ljepblêden foar flugge tagong letter.</string>
<!-- Title for the "select tabs" step of the collection creator -->
<string name="create_collection_select_tabs">Ljepblêden selektearje</string>
<!-- Title for the "select collection" step of the collection creator -->
@ -908,8 +910,9 @@
<!-- text for firefox preview moving tip description -->
<string name="tip_firefox_preview_moved_description">Firefox Nightly wurdt elke nacht bywurke en hat eksperimintele nije funksjes.
        It kin lykwols minder stabyl wêze. Download ús bètabrowser foar in stabiler ûnderfining.</string>
<!-- text for firefox preview moving tip button. "Mozilla Firefox Browser" is intentionally hardcoded -->
<string name="tip_firefox_preview_moved_button">Download Mozilla Firefox Browser</string>
<!-- text for firefox preview moving tip button. "Firefox for Android Beta" is intentionally hardcoded -->
<string name="tip_firefox_preview_moved_button_2">Download Firefox foar Android Beta</string>
<!-- text for firefox preview moving tip header. "Firefox Nightly" is intentionally hardcoded -->
<string name="tip_firefox_preview_moved_header_preview_installed">Firefox Nightly is ferpleatst</string>
@ -1414,6 +1417,9 @@
<!-- Voice search prompt description displayed after the user presses the voice search button -->
<string name="voice_search_explainer">No sprekke</string>
<!-- The error message in edit login view when a duplicate username exists. -->
<string name="saved_login_duplicate">Der bestiet al in oanmelding mei dy brûkersnamme</string>
<!-- Synced Tabs -->
<!-- Text displayed when user is not logged into a Firefox Account -->
<string name="synced_tabs_connect_to_sync_account">Ferbine mei in Firefox-account.</string>
@ -1424,4 +1430,19 @@
<!-- Text displayed when user has disabled tab syncing in Firefox Sync Account -->
<string name="synced_tabs_enable_tab_syncing">Skeakelje it syngronisearjen fan ljepblêden yn.</string>
<!-- Text displayed when user has no tabs that have been synced -->
<string name="synced_tabs_no_tabs">Jo hawwe gjin ljepblêden iepene yn Firefox op jo oare apparaten.</string>
<!-- Text displayed in the synced tabs screen when a user is not signed in to Firefox Sync describing Synced Tabs -->
<string name="synced_tabs_sign_in_message">Besjoch in list mei ljepblêden fan jo oare apparaten.</string>
<!-- Text displayed on a button in the synced tabs screen to link users to sign in when a user is not signed in to Firefox Sync -->
<string name="synced_tabs_sign_in_button">Oanmelde om te syngronisearjen</string>
<!-- Top Sites -->
<!-- Title text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_title">Limyt foar topwebsites berikke</string>
<!-- Content description text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_content">Smyt in topwebsite fuort om in nije ta te foegjen. Hâld de website yndrukt en selektearje Fuortsmite.</string>
<!-- Confirmation dialog button text when top sites limit is reached. -->
<string name="top_sites_max_limit_confirmation_button">OK, begrepen</string>
</resources>

View File

@ -20,6 +20,11 @@
<!-- No Private Tabs Message Description -->
<string name="no_private_tabs_description">Accaren-ik n tbaḍnit ad d-ttwaseknen dagi.</string>
<!-- Message announced to the user when tab tray is selected with 1 tab -->
<string name="open_tab_tray_single">1 yiccer i yeldin. Sit akken ad tbeddleḍ iccer.</string>
<!-- Message announced to the user when tab tray is selected with 0 or 2+ tabs -->
<string name="open_tab_tray_plural">%1$s waccaren yeldin. Sit akken ad tettbeddileḍ gar waccaren.</string>
<!-- About content. The first parameter is the name of the application. (For example: Fenix) -->
<string name="about_content">%1$s d afares n Mozilla.</string>
@ -700,6 +705,10 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara
<string name="collections_header">Tigrummiwin</string>
<!-- Content description (not visible, for screen readers etc.): Opens the collection menu when pressed -->
<string name="collection_menu_button_content_description">Umuɣ n tegrumma</string>
<!-- No Open Tabs Message Header -->
<string name="no_collections_header1">Sdukkel-d akk tiɣawsiwin i yesεan azal ɣur-k/m</string>
<!-- Label to describe what collections are to a new user without any collections -->
<string name="no_collections_description1">Segrew akk akken inadiyen, ismal d waccaren yemṣadan akken ad yishil unekcum ɣer-sen mbeεd.</string>
<!-- Title for the "select tabs" step of the collection creator -->
<string name="create_collection_select_tabs">Fren iccaren</string>
<!-- Title for the "select collection" step of the collection creator -->
@ -1439,6 +1448,9 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara
<!-- Text displayed on a button in the synced tabs screen to link users to sign in when a user is not signed in to Firefox Sync -->
<string name="synced_tabs_sign_in_button">Kcem akken ad yemtawi</string>
<!-- 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>
<!-- Confirmation dialog button text when top sites limit is reached. -->
<string name="top_sites_max_limit_confirmation_button">IH, awi-t-id</string>

View File

@ -996,7 +996,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">이 휴대폰의 다른 Firefox 브라우저에서 %s로 로그인했습니다. 이 계정으로 로그인 하시겠습니까?</string>
<string name="onboarding_firefox_account_auto_signin_header_2">이 휴대폰의 다른 Firefox 브라우저에서 %s로 로그인했습니다. 이 계정으로 로그인하시겠습니까?</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 -->

View File

@ -13,8 +13,6 @@
<string name="content_description_disable_private_browsing_button">ປຶດນຳໃຊ້ການທ່ອງເວັບແບບສ່ວນຕົວ</string>
<!-- Placeholder text shown in the search bar before a user enters text -->
<string name="search_hint">ຄົ້ນຫາ ຫລື ປ້ອນທີ່ຢູ່ໃສ່</string>
<!-- No Open Tabs Message Header -->
<string name="no_open_tabs_header_2">ບໍ່ມີແທັບທີ່ເປີດຢູ່</string>
<!-- No Open Tabs Message Description -->
<string name="no_open_tabs_description">ແທັບທີ່ເປີດຂອງທ່ານຈະຖືກສະແດງຢູ່ບ່ອນນີ້.</string>
@ -268,7 +266,7 @@
<!-- Preference for syncing logins -->
<string name="preferences_sync_logins">ລັອກອິນ</string>
<!-- Preference for syncing tabs -->
<string name="preferences_sync_tabs">ແທັບ</string>
<string name="preferences_sync_tabs_2">ເປີດແທັບ</string>
<!-- Preference for signing out -->
<string name="preferences_sign_out">ອອກ​ຈາກ​ລະ​ບົບ</string>
<!-- Preference displays and allows changing current FxA device name -->
@ -280,6 +278,8 @@
<!-- Label summary indicating that sync failed. The first parameter is the date stamp showing last time it succeeded -->
<string name="sync_failed_summary">ການ sync ລົ້ມເຫລວ. ການ sync ສຳເລັດຄັ້ງສຸດທ້າຍ: %s</string>
<!-- Label summary showing never synced -->
<string name="sync_failed_never_synced_summary">ການ sync ລົ້ມເຫລວ. ການ sync ຄັ້ງສຸດທ້າຍ: ຍັງບໍ່ເຄີຍ sync</string>
<!-- Label summary the date we last synced. The first parameter is date stamp showing last time synced -->
<string name="sync_last_synced_summary">Sync ຄັ້ງຫລ້າສຸດ: %s</string>
<!-- Label summary showing never synced -->
@ -375,24 +375,35 @@
<!-- Preference for using dark theme -->
<string name="preference_dark_theme">ເຂັ້ມ</string>
<!-- Library -->
<!-- Option in Library to open Sessions page -->
<string name="library_sessions">ເຊດຊັນ</string>
<!-- Option in Library to open Screenshots page -->
<string name="library_screenshots">ພາບຫນ້າຈໍ</string>
<!-- Option in Library to open Downloads page -->
<string name="library_downloads">ດາວໂຫລດ</string>
<!-- Option in library to open Bookmarks page -->
<string name="library_bookmarks">ບຸກມາກ</string>
<!-- Option in library to open Desktop Bookmarks root page -->
<string name="library_desktop_bookmarks_root">ບຸກມາກເດສກທັອບ</string>
<!-- Option in library to open Desktop Bookmarks "menu" page -->
<string name="library_desktop_bookmarks_menu">ເມນູບຸກມາກ</string>
<!-- Option in library to open Desktop Bookmarks "toolbar" page -->
<string name="library_desktop_bookmarks_toolbar">ແຖບເຄື່ອງມືບຸກມາກ</string>
<!-- Option in library to open Desktop Bookmarks "unfiled" page -->
<string name="library_desktop_bookmarks_unfiled">ບຸກມາກອື່ນໆ</string>
<!-- Option in Library to open History page -->
<string name="library_history">ປະຫວັດການໃຊ້ງານ</string>
<!-- Option in Library to open Synced Tabs page -->
<string name="library_synced_tabs">ແທັບ Synced</string>
<!-- Option in Library to open Reading List -->
<string name="library_reading_list">ລາຍການອ່ານ</string>
<!-- Menu Item Label for Search in Library -->
<string name="library_search">ຄົ້ນຫາ</string>
<!-- Settings Page Title -->
<string name="settings_title">ການຕັ້ງຄ່າ</string>
<!-- Content description (not visible, for screen readers etc.): "Menu icon for items on a history item" -->
<string name="content_description_history_menu">ເມນູລາຍການປະຫວັດການໃຊ້ງານ</string>
<!-- Content description (not visible, for screen readers etc.): "Close button for library settings" -->
<string name="content_description_close_button">ປິດ</string>
@ -400,13 +411,31 @@
<!-- Title for the list of tabs -->
<string name="tab_header_label">ເປີດແທັບ</string>
<!-- Title for the list of tabs in the current private session -->
<string name="tabs_header_private_title">ເຊດຊັນສ່ວນຕົວ</string>
<!-- Title for the list of tabs in the current private session -->
<string name="tabs_header_private_tabs_title">ແທັບສ່ວນຕົວ</string>
<!-- Content description (not visible, for screen readers etc.): Add tab button. Adds a news tab when pressed -->
<string name="add_tab">ເພີ່ມແທັບ</string>
<!-- Content description (not visible, for screen readers etc.): Add tab button. Adds a news tab when pressed -->
<string name="add_private_tab">ເພີ່ມແທັບສ່ວນຕົວ</string>
<!-- Text for the new tab button to indicate adding a new private tab in the tab -->
<string name="tab_drawer_fab_content">ສ່ວນຕົວ</string>
<!-- Text shown as the title of the open tab tray -->
<string name="tab_tray_title">ເປີດແທັບ</string>
<!-- Text shown in the menu for sharing all tabs -->
<string name="tab_tray_menu_item_share">ແບ່ງປັນແທັບທັງໝົດ</string>
<!-- Text shown in the menu for closing all tabs -->
<string name="tab_tray_menu_item_close">ປິດແທັບທັງຫມົດ</string>
<!-- Shortcut action to open new tab -->
<string name="tab_tray_menu_open_new_tab">ແທັບໃຫມ່</string>
<!-- Shortcut action to open the home screen -->
<string name="tab_tray_menu_home">ໄປຫນ້າທຳອິດ</string>
<!-- Content description (not visible, for screen readers etc.): Close tab button. Closes the current session when pressed -->
<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>
<!-- 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 -->
<string name="tabs_menu_close_all_tabs">ປິດແທັບທັງຫມົດ</string>
<!-- Open tabs menu item to share all tabs -->
@ -427,9 +456,57 @@
<!-- Text for the menu button to remove a top site -->
<string name="remove_top_site">ລຶບ</string>
<!-- Postfix for private WebApp titles, placeholder is replaced with app name -->
<string name="pwa_site_controls_title_private">%1$s (ໂຫມດສ່ວນຕົວ)</string>
<!-- History -->
<!-- Text for the button to clear all history -->
<string name="history_delete_all">ລຶບປະຫວັດການໃຊ້ງານ</string>
<!-- Text for the dialog to confirm clearing all history -->
<string name="history_delete_all_dialog">ທ່ານຫມັ້ນໃຈແລ້ວບໍ່ວ່າທ່ານຕ້ອງການລົບລ້າງປະຫວັດການໃຊ້ງານຂອງທ່ານ?</string>
<!-- Text for the snackbar to confirm that multiple browsing history items has been deleted -->
<string name="history_delete_multiple_items_snackbar">ລຶບປະຫວັດການໃຊ້ງານແລ້ວ</string>
<!-- Text for the snackbar to confirm that a single browsing history item has been deleted. The first parameter is the shortened URL of the deleted history item. -->
<string name="history_delete_single_item_snackbar">ລຶບ %1$s ແລ້ວ</string>
<!-- Text for positive action to delete history in deleting history dialog -->
<string name="history_clear_dialog">ລົບລ້າງ</string>
<!-- History overflow menu copy button -->
<string name="history_menu_copy_button">ສຳເນົາ</string>
<!-- History overflow menu share button -->
<string name="history_menu_share_button">ແບ່ງປັນ</string>
<!-- History overflow menu open in new tab button -->
<string name="history_menu_open_in_new_tab_button">ເປີດໃນແທັບໃຫມ່</string>
<!-- History overflow menu open in private tab button -->
<string name="history_menu_open_in_private_tab_button">ເປີດໃນແທັບສ່ວນຕົວ</string>
<!-- Text for the button to delete a single history item -->
<string name="history_delete_item">ລຶບ</string>
<!-- History multi select title in app bar
The first parameter is the number of bookmarks selected -->
<string name="history_multi_select_title">ເລືອກ %1$d ແລ້ວ</string>
<!-- Text for the button to clear selected history items. The first parameter
is a digit showing the number of items you have selected -->
<string name="history_delete_some">ລຶບໄອເທັມ %1$d</string>
<!-- Text for the header that groups the history for last 24 hours -->
<string name="history_24_hours">24 ຊົ່ວໂມງທີ່ຜ່ານມາ</string>
<!-- Text for the header that groups the history the past 7 days -->
<string name="history_7_days">7 ມື້ທີ່ຜ່ານມາ</string>
<!-- Text for the header that groups the history the past 30 days -->
<string name="history_30_days">30 ມື້ທີ່ຜ່ານມາ</string>
<!-- Text for the header that groups the history older than the last month -->
<string name="history_older">ເກົ່າກວ່າ</string>
<!-- Text shown when no history exists -->
<string name="history_empty_message">ບໍ່ມີປະຫວັດການໃຊ້ງານຢູ່ນີ້</string>
<!-- Crashes -->
<!-- Title text displayed on the tab crash page. This first parameter is the name of the application (For example: Fenix) -->
<string name="tab_crash_title_2">ຂໍ​ອະໄພ. %1$s ບໍ່ສາມາດໂຫລດໜ້າເວັບນັ້ນໄດ້.</string>
<!-- Description text displayed on the tab crash page -->
<string name="tab_crash_description">ທ່ານສາມາດພະຍາຍາມກູ້ຄືນ ຫຼື ປິດແຖບຂ້າງລຸ່ມນີ້.</string>
<!-- Close tab button text on the tab crash page -->
<string name="tab_crash_close">ປິດແທັບ</string>
<!-- Restore tab button text on the tab crash page -->
<string name="tab_crash_restore">ກູ້ຄືນແທັບ</string>
</resources>

View File

@ -22,6 +22,11 @@
<!-- No Private Tabs Message Description -->
<string name="no_private_tabs_description">Uw privétabbladen worden hier weergegeven.</string>
<!-- Message announced to the user when tab tray is selected with 1 tab -->
<string name="open_tab_tray_single">1 open tabblad. Tik om tussen tabbladen te wisselen.</string>
<!-- Message announced to the user when tab tray is selected with 0 or 2+ tabs -->
<string name="open_tab_tray_plural">%1$s open tabbladen. Tik om tussen tabbladen te wisselen.</string>
<!-- About content. The first parameter is the name of the application. (For example: Fenix) -->
<string name="about_content">%1$s is gemaakt door Mozilla.</string>
@ -484,6 +489,8 @@
<string name="tabs_menu_close_all_tabs">Alle tabbladen sluiten</string>
<!-- Open tabs menu item to share all tabs -->
<string name="tabs_menu_share_tabs">Tabbladen delen</string>
<!-- Open tabs menu item to save tabs to collection -->
<string name="tabs_menu_save_to_collection1">Tabbladen in collectie opslaan</string>
<!-- Content description (not visible, for screen readers etc.): Opens the tab menu when pressed -->
<string name="tab_menu">Tabbladmenu</string>
<!-- Tab menu item to share the tab -->
@ -700,6 +707,10 @@
<string name="collections_header">Collecties</string>
<!-- Content description (not visible, for screen readers etc.): Opens the collection menu when pressed -->
<string name="collection_menu_button_content_description">Collectiesmenu</string>
<!-- No Open Tabs Message Header -->
<string name="no_collections_header1">Verzamel de dingen die belangrijk voor u zijn</string>
<!-- Label to describe what collections are to a new user without any collections -->
<string name="no_collections_description1">Groepeer vergelijkbare zoekopdrachten, websites en tabbladen voor snelle toegang later.</string>
<!-- Title for the "select tabs" step of the collection creator -->
<string name="create_collection_select_tabs">Tabbladen selecteren</string>
<!-- Title for the "select collection" step of the collection creator -->
@ -1416,6 +1427,9 @@
<!-- Voice search prompt description displayed after the user presses the voice search button -->
<string name="voice_search_explainer">Nu spreken</string>
<!-- The error message in edit login view when a duplicate username exists. -->
<string name="saved_login_duplicate">Er bestaat al een aanmelding met die gebruikersnaam</string>
<!-- Synced Tabs -->
<!-- Text displayed when user is not logged into a Firefox Account -->
<string name="synced_tabs_connect_to_sync_account">Verbinden met een Firefox-account.</string>
@ -1430,6 +1444,14 @@
<!-- Text displayed in the synced tabs screen when a user is not signed in to Firefox Sync describing Synced Tabs -->
<string name="synced_tabs_sign_in_message">Bekijk een lijst met tabbladen van uw overige apparaten.</string>
<!-- Text displayed on a button in the synced tabs screen to link users to sign in when a user is not signed in to Firefox Sync -->
<string name="synced_tabs_sign_in_button">Aanmelden om te synchroniseren</string>
<!-- Top Sites -->
<!-- Title text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_title">Limiet voor topwebsites bereikt</string>
<!-- Content description text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_content">Verwijder een topwebsite om een nieuwe toe te voegen. Houd de website ingedrukt en selecteer Verwijderen.</string>
<!-- Confirmation dialog button text when top sites limit is reached. -->
<string name="top_sites_max_limit_confirmation_button">OK, begrepen</string>

View File

@ -13,11 +13,16 @@
<string name="content_description_disable_private_browsing_button">Çaktivizo shfletim privat</string>
<!-- Placeholder text shown in the search bar before a user enters text -->
<string name="search_hint">Bëni kërkim ose jepni adresë</string>
<!-- No Open Tabs Message Header -->
<string name="no_open_tabs_header_2">Ska skeda të hapura</string>
<!-- No Open Tabs Message Description -->
<string name="no_open_tabs_description">Skedat tuaja të hapura do të shfaqen këtu.</string>
<!-- No Private Tabs Message Description -->
<string name="no_private_tabs_description">Skedat tuaja private do të shfaqen këtu.</string>
<!-- Message announced to the user when tab tray is selected with 1 tab -->
<string name="open_tab_tray_single">1 skedë e hapur. Prekeni që të ndërroni skeda.</string>
<!-- Message announced to the user when tab tray is selected with 0 or 2+ tabs -->
<string name="open_tab_tray_plural">%1$s skeda të hapura. Prekeni që të ndërroni skeda.</string>
<!-- About content. The first parameter is the name of the application. (For example: Fenix) -->
<string name="about_content">%1$s prodhohet nga Mozilla.</string>
@ -33,7 +38,7 @@
<!-- Delete session button to erase your history in a private session -->
<string name="private_browsing_delete_session">Fshije sesionin</string>
<!-- Private mode shortcut "contextual feature recommender" (CFR) -->
<!-- Private mode shortcut "contextual feature recommendation" (CFR) -->
<!-- Text for the main message -->
<string name="cfr_message">Shtoni një shkurtore për të hapur skeda private që prej skenës Kreu juaj.</string>
<!-- Text for the positive button -->
@ -41,6 +46,14 @@
<!-- Text for the negative button -->
<string name="cfr_neg_button_text">Jo, faleminderit</string>
<!-- Search widget "contextual feature recommendation" (CFR) -->
<!-- Text for the main message. 'Firefox' intentionally hardcoded here.-->
<string name="search_widget_cfr_message">Kaloni më shpejt te Firefox-i. Shtoni te skena juaj e Kreut një &lt;em&gt;widget&lt;/em&gt;.</string>
<!-- Text for the positive button -->
<string name="search_widget_cfr_pos_button_text">Shtoni &lt;em&gt;widget&lt;/em&gt;</string>
<!-- Text for the negative button -->
<string name="search_widget_cfr_neg_button_text">Jo tani</string>
<!-- Home screen icons - Long press shortcuts -->
<!-- Shortcut action to open new tab -->
<string name="home_screen_shortcut_open_new_tab_2">Skedë e re</string>
@ -83,6 +96,8 @@
<string name="browser_menu_add_to_homescreen">Shtoje te skena e Kreut</string>
<!-- Browser menu toggle that installs a Progressive Web App shortcut to the site on the device home screen. -->
<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>
<!-- 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 -->
@ -91,8 +106,6 @@
<string name="browser_menu_new_tab">Skedë e re</string>
<!-- Browser menu button that saves the current tab to a collection -->
<string name="browser_menu_save_to_collection_2">Ruaje në koleksion</string>
<!-- Browser menu button that opens a dialog to report issues with the current site -->
<string name="browser_menu_report_issue">Njoftoni problem sajti</string>
<!-- Browser menu button that open a share menu to share the current site -->
<string name="browser_menu_share">Ndajeni me të tjerët</string>
<!-- Share menu title, displayed when a user is sharing their current site -->
@ -107,8 +120,10 @@
<!-- Browser menu text shown in custom tabs to indicate this is a Fenix tab
The first parameter is the name of the app defined in app_name (for example: Fenix) -->
<string name="browser_menu_powered_by2">Bazuar në %1$s</string>
<!-- Browser menu button to put the the current page in reader mode -->
<!-- Browser menu button to put the current page in reader mode -->
<string name="browser_menu_read">Pamja Lexues</string>
<!-- Browser menu button content description to close reader mode and return the user to the regular browser -->
<string name="browser_menu_read_close">Mbylle pamjen lexues</string>
<!-- Browser menu button to open the current page in an external app -->
<string name="browser_menu_open_app_link">Hape në Aplikacion</string>
<!-- Browser menu button to configure reader mode appearance e.g. the used font type and size -->
@ -250,6 +265,8 @@
<string name="preferences_show_search_shortcuts">Shfaqni shkurtore kërkimi</string>
<!-- Preference title for switch preference to show search suggestions -->
<string name="preferences_show_search_suggestions">Shfaq sugjerime kërkimi</string>
<!-- Preference title for switch preference to show voice search button -->
<string name="preferences_show_voice_search">Shfaq kërkim zanor</string>
<!-- Preference title for switch preference to show search suggestions also in private mode -->
<string name="preferences_show_search_suggestions_in_private">Shfaqi në sesione private</string>
<!-- Preference title for switch preference to show a clipboard suggestion when searching -->
@ -277,6 +294,8 @@
<string name="preferences_sync_bookmarks">Faqerojtës</string>
<!-- Preference for syncing logins -->
<string name="preferences_sync_logins">Kredenciale Hyrjesh</string>
<!-- Preference for syncing tabs -->
<string name="preferences_sync_tabs_2">Skeda të hapura</string>
<!-- Preference for signing out -->
<string name="preferences_sign_out">Dilni</string>
<!-- Preference displays and allows changing current FxA device name -->
@ -368,7 +387,7 @@
<!-- Pairing Feature strings -->
<!-- Instructions on how to access pairing -->
<string name="pair_instructions"><![CDATA[Që të merrni kodin tuaj QR, vizitoni <b>firefox.com/pair</b> në Firefox te kompjuteri juaj.]]></string>
<string name="pair_instructions_2"><![CDATA[Skanoni kodin QR te <b>firefox.com/pair</b>]]></string>
<!-- Button to open camera for pairing -->
<string name="pair_open_camera">Hap Kamerën</string>
<!-- Button to cancel pairing -->
@ -409,6 +428,8 @@
<string name="library_desktop_bookmarks_unfiled">Faqerojtës të Tjerë</string>
<!-- Option in Library to open History page -->
<string name="library_history">Historik</string>
<!-- Option in Library to open Synced Tabs page -->
<string name="library_synced_tabs">Skeda të njëkohësuara</string>
<!-- Option in Library to open Reading List -->
<string name="library_reading_list">Listë Leximesh</string>
<!-- Menu Item Label for Search in Library -->
@ -429,6 +450,26 @@
<string name="tabs_header_private_tabs_title">Skeda private</string>
<!-- Content description (not visible, for screen readers etc.): Add tab button. Adds a news tab when pressed -->
<string name="add_tab">Shtoni skedë</string>
<!-- Content description (not visible, for screen readers etc.): Add tab button. Adds a news tab when pressed -->
<string name="add_private_tab">Shtoni skedë private</string>
<!-- Text for the new tab button to indicate adding a new private tab in the tab -->
<string name="tab_drawer_fab_content">Private</string>
<!-- Text shown as the title of the open tab tray -->
<string name="tab_tray_title">Hapi Skedat</string>
<!-- Text shown in the menu for saving tabs to a collection -->
<string name="tab_tray_menu_item_save">Ruaje në koleksion</string>
<!-- Text shown in the menu for sharing all tabs -->
<string name="tab_tray_menu_item_share">Nda krejt skedat</string>
<!-- Text shown in the menu for closing all tabs -->
<string name="tab_tray_menu_item_close">Mbylli krejt skedat</string>
<!-- Shortcut action to open new tab -->
<string name="tab_tray_menu_open_new_tab">Skedë e re</string>
<!-- Shortcut action to open the home screen -->
<string name="tab_tray_menu_home">Shko te kreu</string>
<!-- Shortcut action to toggle private mode -->
<string name="tab_tray_menu_toggle">Ndërroni mënyrë skedash</string>
<!-- 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">Hiq skedë prej koleksionit</string>
<!-- Content description (not visible, for screen readers etc.): Close tab button. Closes the current session when pressed -->
<string name="close_tab">Mbylle skedën</string>
<!-- Content description (not visible, for screen readers etc.): Close tab <title> button. First parameter is tab title -->
@ -439,9 +480,9 @@
<string name="tabs_menu_close_all_tabs">Mbylli krejt skedat</string>
<!-- Open tabs menu item to share all tabs -->
<string name="tabs_menu_share_tabs">Ndani skeda me të tjerët</string>
<!-- Open tabs menu item to save tabs to collection -->
<string name="tabs_menu_save_to_collection">Ruaje në koleksion</string>
<!-- Open tabs menu item to save tabs to collection -->
<string name="tabs_menu_save_to_collection1">Ruaj skeda në koleksion</string>
<!-- Content description (not visible, for screen readers etc.): Opens the tab menu when pressed -->
<string name="tab_menu">Menu skedash</string>
<!-- Tab menu item to share the tab -->
@ -653,16 +694,14 @@
<string name="delete_browsing_data_quit_off">Off</string>
<!-- Collections -->
<!-- Label to describe what collections are to a new user without any collections -->
<string name="collections_description">Koleksiononi gjërat që kanë rëndësi për ju. Për tia filluar, ruani skedat e hapura në një koleksion të ri.</string>
<!-- Collections header on home fragment -->
<string name="collections_header">Koleksione</string>
<!-- Content description (not visible, for screen readers etc.): Opens the collection menu when pressed -->
<string name="collection_menu_button_content_description">Menu koleksionesh</string>
<!-- No Open Tabs Message Header -->
<string name="no_collections_header">Ska koleksione</string>
<!-- No Open Tabs Message Description -->
<string name="no_collections_description">Koleksionet tuaja do të shfaqen këtu.</string>
<string name="no_collections_header1">Koleksiononi gjërat që kanë rëndësi për ju</string>
<!-- Label to describe what collections are to a new user without any collections -->
<string name="no_collections_description1">Gruponi tok kërkime, sajte dhe skeda të ngjashme, për përdorim të shpejtë më pas.</string>
<!-- Title for the "select tabs" step of the collection creator -->
<string name="create_collection_select_tabs">Përzgjidhni Skeda</string>
<!-- Title for the "select collection" step of the collection creator -->
@ -692,6 +731,9 @@
<!-- Button to save currently selected tabs in the "select tabs" step of the collection creator-->
<string name="create_collection_save">Ruaje</string>
<!-- Snackbar action to view the collection the user just created or updated -->
<string name="create_collection_view">Shiheni</string>
<!-- Default name for a new collection in "name new collection" step of the collection creator. %d is a placeholder for the number of collections-->
<string name="create_collection_default_name">Koleksioni %d</string>
@ -802,14 +844,14 @@
<string name="preference_accessibility_font_size_title">Madhësi Shkronjash</string>
<!-- Title for Accessibility Text Automatic Size Scaling Preference -->
<string name="preference_accessibility_auto_size">Përmasim Automatik i Shkronjave</string>
<string name="preference_accessibility_auto_size_2">Përmasim automatik i shkronjave</string>
<!-- Summary for Accessibility Text Automatic Size Scaling Preference -->
<string name="preference_accessibility_auto_size_summary">Madhësia e shkronjave do të përputhet me rregullimet tuaja në Android. Që të administroni madhësi shkronjash prej këtu, çaktivizojeni.</string>
<!-- Title for the Delete browsing data preference -->
<string name="preferences_delete_browsing_data">Fshi të dhëna shfletimi</string>
<!-- Title for the tabs item in Delete browsing data -->
<string name="preferences_delete_browsing_data_tabs_title">Skeda të Hapura</string>
<string name="preferences_delete_browsing_data_tabs_title_2">Skeda të hapura</string>
<!-- Subtitle for the tabs item in Delete browsing data, parameter will be replaced with the number of open tabs -->
<string name="preferences_delete_browsing_data_tabs_subtitle">%d skeda</string>
@ -868,8 +910,9 @@
<!-- text for firefox preview moving tip description -->
<string name="tip_firefox_preview_moved_description">Firefox Nightly përditësohet përnatë dhe ka veçori të reja eksperimentale.
Por, është më pak i qëndrueshëm. Për funksionim më të qëndrueshëm, shkarkoni shfletuesin tonë beta.</string>
<!-- text for firefox preview moving tip button. "Mozilla Firefox Browser" is intentionally hardcoded -->
<string name="tip_firefox_preview_moved_button">Merrni Shfletuesin Firefox të Mozilla-s</string>
<!-- text for firefox preview moving tip button. "Firefox for Android Beta" is intentionally hardcoded -->
<string name="tip_firefox_preview_moved_button_2">Merrni Firefox-in për Android Beta</string>
<!-- text for firefox preview moving tip header. "Firefox Nightly" is intentionally hardcoded -->
<string name="tip_firefox_preview_moved_header_preview_installed">Firefox Nightly është lëvizur</string>
@ -923,18 +966,20 @@
<!-- text to display in the snackbar if automatic sign-in fails. user may try again -->
<string name="onboarding_firefox_account_automatic_signin_failed">Su arrit të hyhej</string>
<!-- text for the tracking protection onboarding card header -->
<string name="onboarding_tracking_protection_header">Mbroni veten</string>
<string name="onboarding_tracking_protection_header_2">Privatësi e vetvetishme</string>
<!-- text for the tracking protection card description
The first parameter is the name of the app (e.g. Firefox Preview) -->
<string name="onboarding_tracking_protection_description1">%s ndihmon të ndalen sajtet nga ndjekja juaj në internet.</string>
<string name="onboarding_tracking_protection_description_2">Rregullimet e privatësisë dhe sigurisë bllokojnë gjurmues, malware, dhe shoqëri që ju ndjekin.</string>
<!-- text for tracking protection radio button option for standard level of blocking -->
<string name="onboarding_tracking_protection_standard_button">Standarde</string>
<string name="onboarding_tracking_protection_standard_button_2">Standarde (parazgjedhje)</string>
<!-- text for standard blocking option button description -->
<string name="onboarding_tracking_protection_standard_button_description">Bllokon më pak gjurmues por lejon që faqet të ngarkohen normalisht</string>
<string name="onboarding_tracking_protection_standard_button_description_2">Bllokon më pak gjurmues. Faqet do të ngarkohen normalisht.</string>
<!-- text for tracking protection radio button option for strict level of blocking -->
<string name="onboarding_tracking_protection_strict_button">Strikte (e këshilluar)</string>
<!-- text for tracking protection radio button option for strict level of blocking -->
<string name="onboarding_tracking_protection_strict_option">Strikte</string>
<!-- text for strict blocking option button description -->
<string name="onboarding_tracking_protection_strict_button_description">Bllokon më shumë gjurmues për mbrojtje dhe punim më të mirë, por mund të bëjë që disa sajte të mos funksionojnë siç duhet</string>
<string name="onboarding_tracking_protection_strict_button_description_2">Bllokon më shumë gjurmues, reklama dhe flluska. Faqet ngarkohen më shpejt, por disa anë mund të mos punojnë.</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 -->
@ -1023,31 +1068,21 @@
<!-- Text displayed that links to website about enhanced tracking protection -->
<string name="preference_enhanced_tracking_protection_explanation_learn_more">Mësoni më tepër</string>
<!-- Preference for enhanced tracking protection for the standard protection settings -->
<string name="preference_enhanced_tracking_protection_standard_option">Standarde</string>
<!-- Preference for enhanced tracking protection for the standard protection settings -->
<string name="preference_enhanced_tracking_protection_standard">Standarde (e këshilluar)</string>
<string name="preference_enhanced_tracking_protection_standard_default_1">Standarde (parazgjedhje)</string>
<!-- Preference description for enhanced tracking protection for the standard protection settings -->
<string name="preference_enhanced_tracking_protection_standard_description">E baraspeshuar për mbrojtje dhe funksionim.</string>
<!-- Preference description for enhanced tracking protection for the standard protection settings -->
<string name="preference_enhanced_tracking_protection_standard_description_2">Faqet do të ngarkohen normalisht, por blloko më pak gjurmues.</string>
<string name="preference_enhanced_tracking_protection_standard_description_3">Bllokon më pak gjurmues. Faqet do të ngarkohen normalisht.</string>
<!-- Accessibility text for the Standard protection information icon -->
<string name="preference_enhanced_tracking_protection_standard_info_button">Çbllokohet nga mbrojtje standarde kundër gjurmimit</string>
<!-- Preference for enhanced tracking protection for the strict protection settings -->
<string name="preference_enhanced_tracking_protection_strict">Strikte</string>
<!-- Preference for enhanced tracking protection for the strict protection settings, default setting -->
<string name="preference_enhanced_tracking_protection_strict_default">Strikte (Parazgjedhje)</string>
<!-- Preference description for enhanced tracking protection for the strict protection settings -->
<string name="preference_enhanced_tracking_protection_strict_default_description">Mbrojtje më e fortë kundër gjurmimit dhe punim më i shpejtë, por disa sajte mund të mos funksionojnë siç duhet.</string>
<!-- Preference for enhanced tracking protection for the standard protection settings -->
<string name="preference_enhanced_tracking_protection_strict_recommended">Strikte (e këshilluar)</string>
<!-- Preference description for enhanced tracking protection for the strict protection settings -->
<string name="preference_enhanced_tracking_protection_strict_description">Mbrojtje më e fortë, mund të shkaktojë mosfunksionim për disa sajte ose lëndë.</string>
<string name="preference_enhanced_tracking_protection_strict_description_2">Bllokon më shumë gjurmues, reklama dhe flluska. Faqet ngarkohen më shpejt, por disa anë mund të mos punojnë.</string>
<!-- Accessibility text for the Strict protection information icon -->
<string name="preference_enhanced_tracking_protection_strict_info_button">Çbllokohet nga mbrojtje strikte kundër gjurmimit</string>
<!-- Preference for enhanced tracking protection for the custom protection settings -->
<string name="preference_enhanced_tracking_protection_custom">Vetjake</string>
<!-- Preference description for enhanced tracking protection for the strict protection settings -->
<string name="preference_enhanced_tracking_protection_custom_description">Zgjidhni cilët gjurmues dhe programthe të bllokohen</string>
<string name="preference_enhanced_tracking_protection_custom_description_2">Zgjidhni cilët gjurmues dhe programthe të bllokohen.</string>
<!-- Accessibility text for the Strict protection information icon -->
<string name="preference_enhanced_tracking_protection_custom_info_button">Çbllokohet nga mbrojtje vetjake kundër gjurmimit</string>
<!-- Header for categories that are being blocked by current Enhanced Tracking Protection settings -->
@ -1097,7 +1132,7 @@
<!-- Description of tracking content that can be blocked by Enhanced Tracking Protection -->
<string name="etp_tracking_content_description">Ndal ngarkim reklamash, videosh dhe lënde tjetër të jashtme që përmban kod gjurmimi. Mund të prekë punimin e disa sajteve.</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_message_2">Kur mburoja është e purpur, %s bllokon gjurmues në këtë sajt. Prekeni që të shihni se çështë e bllokuar.</string>
<string name="etp_onboarding_cfr_message">Sa herë që mburoja është e purpur, %s ka bllokuar gjurmues në një sajt. Për më tepër të dhëna, prekeni.</string>
<!-- Enhanced Tracking Protection message that protection is currently on for this site -->
<string name="etp_panel_on">Për këtë sajt, mbrojtjet janë ON</string>
<!-- Enhanced Tracking Protection message that protection is currently off for this site -->
@ -1379,4 +1414,37 @@
<string name="edit">Përpunoni</string>
<!-- The error message in edit login view when password field is blank. -->
<string name="saved_login_password_required">Lypset fjalëkalim</string>
<!-- Voice search button content description -->
<string name="voice_search_content_description">Kërkim zanor</string>
<!-- Voice search prompt description displayed after the user presses the voice search button -->
<string name="voice_search_explainer">Flisni</string>
<!-- The error message in edit login view when a duplicate username exists. -->
<string name="saved_login_duplicate">Ka tashmë një palë kredenciale me këtë emër përdoruesi</string>
<!-- Synced Tabs -->
<!-- Text displayed when user is not logged into a Firefox Account -->
<string name="synced_tabs_connect_to_sync_account">Lidhuni me Llogaritë Firefox.</string>
<!-- Text displayed to ask user to connect another device as no devices found with account -->
<string name="synced_tabs_connect_another_device">Lidhni pajisje tjetër.</string>
<!-- Text displayed asking user to re-authenticate -->
<string name="synced_tabs_reauth">Ju lutemi, ribëni mirëfilltësimin.</string>
<!-- Text displayed when user has disabled tab syncing in Firefox Sync Account -->
<string name="synced_tabs_enable_tab_syncing">Ju lutemi, aktivizoni njëkohësim skedash.</string>
<!-- Text displayed when user has no tabs that have been synced -->
<string name="synced_tabs_no_tabs">Skeni ndonjë skedë të hapur te Firefox-i në pajisje tuajat të tjera.</string>
<!-- Text displayed in the synced tabs screen when a user is not signed in to Firefox Sync describing Synced Tabs -->
<string name="synced_tabs_sign_in_message">Shihni një listë skedash nga pajisje tuajat të tjera.</string>
<!-- Text displayed on a button in the synced tabs screen to link users to sign in when a user is not signed in to Firefox Sync -->
<string name="synced_tabs_sign_in_button">Bëni hyrjen që të sjëkohësoni</string>
<!-- Top Sites -->
<!-- Title text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_title">U mbërrit te kufi sajtesh</string>
<!-- Content description text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_content">Që të shtoni një sajt të ri kryesues, hiqni një të tillë. Mbajeni të shtypur mbi sajtin dhe përzgjidhni Hiqe.</string>
<!-- Confirmation dialog button text when top sites limit is reached. -->
<string name="top_sites_max_limit_confirmation_button">OK, e mora vesh</string>
</resources>

View File

@ -658,18 +658,113 @@
<string name="quick_settings_sheet">Ñanj \'na\' sa ga\'ue nāgi\'iô\' hīo doj</string>
<!-- Label that indicates that this option it the recommended one -->
<string name="phone_feature_recommended">Sā sà\'a huin ânj</string>
<!-- button that allows editing site permissions settings -->
<string name="quick_settings_sheet_manage_site_permissions">Gānikāj ñu\'ūnj sa achín nì\'iaj nīkāj sîtio</string>
<!-- Button label for clearing all the information of site permissions-->
<string name="clear_permissions">Nādure\' nej sa achín nì\'iaj</string>
<!-- Button label for clearing a site permission-->
<string name="clear_permission">Nādure\' sa achín nì\'iaj</string>
<!-- Button label for clearing all the information on all sites-->
<string name="clear_permissions_on_all_sites">Nādure\' nej sa achín nì\'iaj riña daran\' nej sîtio</string>
<!-- Preference for altering video and audio autoplay for all websites -->
<string name="preference_browser_feature_autoplay">Nānùn ma\'an man</string>
<!-- Preference for altering the camera access for all websites -->
<string name="preference_phone_feature_camera">Kâmara</string>
<!-- Preference for altering the microphone access for all websites -->
<string name="preference_phone_feature_microphone">Aga\' uxun nanèe</string>
<!-- Preference for altering the location access for all websites -->
<string name="preference_phone_feature_location">Danè\' huin</string>
<!-- Preference for altering the notification access for all websites -->
<string name="preference_phone_feature_notification">Sa atāj nan\'ānj an</string>
<!-- Label that indicates that a permission must be asked always -->
<string name="preference_option_phone_feature_ask_to_allow">Gāchinj nì\'iaj</string>
<!-- Label that indicates that a permission must be blocked -->
<string name="preference_option_phone_feature_blocked">Nitāj si hūaj nāyi\'nïn</string>
<!-- Label that indicates that a permission must be allowed -->
<string name="preference_option_phone_feature_allowed">Gā\'ue</string>
<!--Label that indicates a permission is by the Android OS-->
<string name="phone_feature_blocked_by_android">Nitāj si hūaj nāyi\'nïn \'iaj Android</string>
<!-- Preference for showing a list of websites that the default configurations won't apply to them -->
<string name="preference_exceptions">Sa huā gi\'iát</string>
<!-- Summary of tracking protection preference if tracking protection is set to on -->
<string name="tracking_protection_on">Nāchrūn</string>
<!-- Summary of tracking protection preference if tracking protection is set to off -->
<string name="tracking_protection_off">Dūnâ\àj</string>
<!-- Label that indicates that all video and audio autoplay is allowed -->
<string name="preference_option_autoplay_allowed2">Gā\'nïn gī\'iaj sun sa unïnt ngà sa ni\'iājt</string>
<!-- Label that indicates that video and audio autoplay is only allowed over Wi-Fi -->
<string name="preference_option_autoplay_allowed_wifi_only2">Nārán riña sa unïnt ngà sa ni\'iājt ngà datô si āgâ\'t li</string>
<!-- Subtext that explains 'autoplay on Wi-Fi only' option -->
<string name="preference_option_autoplay_allowed_wifi_subtext">Nānùn sa unïnt ngà sa ni\'iājt ngà Wi-Fi</string>
<!-- Label that indicates that video autoplay is allowed, but audio autoplay is blocked -->
<string name="preference_option_autoplay_block_audio2">Màn riña sa unïnt nārán</string>
<!-- Label that indicates that all video and audio autoplay is blocked -->
<string name="preference_option_autoplay_blocked3">Nārán riña sa unïnt ngà sa ni\'iājt</string>
<!-- Summary of delete browsing data on quit preference if it is set to on -->
<string name="delete_browsing_data_quit_on">Nāchrūn</string>
<!-- Summary of delete browsing data on quit preference if it is set to off -->
<string name="delete_browsing_data_quit_off">Dūnâ\àj</string>
<!-- Collections -->
<!-- Collections header on home fragment -->
<string name="collections_header">Koleksiôn</string>
<!-- Content description (not visible, for screen readers etc.): Opens the collection menu when pressed -->
<string name="collection_menu_button_content_description">Si menû koleksiûn</string>
<!-- No Open Tabs Message Header -->
<string name="no_collections_header1">Nāran\' daran\' sa ni\'ñānj ruhuât</string>
<!-- Label to describe what collections are to a new user without any collections -->
<string name="no_collections_description1">Nāgi\'iaj chrē\' sa nana\'uî\'t, sitio nī nej rakïj ñanj guñā hua da\' ga\'ue gātū hìot ne\' rūkù.</string>
<!-- Title for the "select tabs" step of the collection creator -->
<string name="create_collection_select_tabs">Nāguī nej rakïj ñanj</string>
<!-- Title for the "select collection" step of the collection creator -->
<string name="create_collection_select_collection">Nāguī sa màn yì\'nïn\'ïn</string>
<!-- Title for the "name collection" step of the collection creator -->
<string name="create_collection_name_collection">Si yugui sa màn yì\'nïn\'ïn</string>
<!-- Button to add new collection for the "select collection" step of the collection creator -->
<string name="create_collection_add_new_collection">Nūtà\' \'ngō yi\'nïn\' nākàa</string>
<!-- Button to select all tabs in the "select tabs" step of the collection creator -->
<string name="create_collection_select_all">Gīda\'a daran\'anj</string>
<!-- Button to deselect all tabs in the "select tabs" step of the collection creator -->
<string name="create_collection_deselect_all">Nā\'nïnj ra\'a daran\'anj</string>
<!-- Text to prompt users to select the tabs to save in the "select tabs" step of the collection creator -->
<string name="create_collection_save_to_collection_empty">Nāguī nej rakïj ñanj ruhuât nā\'nïnj sà\'t</string>
<!-- Text to show users how many tabs they have selected in the "select tabs" step of the collection creator.
%d is a placeholder for the number of tabs selected. -->
<string name="create_collection_save_to_collection_tabs_selected">%d nej rakïj ñanj naguit</string>
<!-- Text to show users they have one tab selected in the "select tabs" step of the collection creator.
%d is a placeholder for the number of tabs selected. -->
<string name="create_collection_save_to_collection_tab_selected">%d rakïj ñanj naguit</string>
<!-- Text shown in snackbar when multiple tabs have been saved in a collection -->
<string name="create_collection_tabs_saved">Nej rakïj ñanj na\'nín sà\'t</string>
<!-- Text shown in snackbar when one tab has been saved in a collection -->
<string name="create_collection_tab_saved">Rakïj ñanj na\'nïnj sà\'t</string>
<!-- Content description (not visible, for screen readers etc.): button to close the collection creator -->
<string name="create_collection_close">Nārán</string>
<!-- Button to save currently selected tabs in the "select tabs" step of the collection creator-->
<string name="create_collection_save">Nā\'nïnj sà\'</string>
<!-- Snackbar action to view the collection the user just created or updated -->
<string name="create_collection_view">Ni\'iāj</string>
<!-- Default name for a new collection in "name new collection" step of the collection creator. %d is a placeholder for the number of collections-->
<string name="create_collection_default_name">Sa nachra chrē\'t %d</string>
<!-- Share -->
<!-- Share screen header -->
<string name="share_header">Gā\'nïnj gan\'an nī dūyinga\'</string>
<!-- Share screen header -->
<string name="share_header_2">Dūyingô\'</string>
<!-- Content description (not visible, for screen readers etc.):
"Share" button. Opens the share menu when pressed. -->
<string name="share_button_content_description">Dūyingô\'</string>
<!-- Sub-header in the dialog to share a link to another app -->
<string name="share_link_subheader">Dūyingô\' \'ngō lînk</string>
<!-- Sub-header in the dialog to share a link to another sync device -->
<string name="share_device_subheader">Gā\'nïnj riña aga\'</string>
<!-- Sub-header in the dialog to share a link to an app from the full list -->
<string name="share_link_all_apps_subheader">Daran\' sun huā</string>
<!-- Content description (not visible, for screen readers etc.): Close onboarding screen -->
<string name="onboarding_close">Nārán</string>

View File

@ -1432,4 +1432,9 @@
<!-- Top Sites -->
<!-- Title text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_title">Đã đạt đến giới hạn trang web hàng đầu</string>
</resources>
<!-- Content description text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_content">Để thêm một trang web hàng đầu mới, loại bỏ một cái cũ trước. Chạm và giữ trang web và chọn xóa.</string>
<!-- Confirmation dialog button text when top sites limit is reached. -->
<string name="top_sites_max_limit_confirmation_button">OK, đã hiểu</string>
</resources>

View File

@ -7,7 +7,7 @@
<dimen name="crash_reporter_close_tab_button_bottom_margin">24dp</dimen>
<dimen name="glyph_button_height">48dp</dimen>
<dimen name="glyph_button_width">48dp</dimen>
<dimen name="mozac_browser_menu_width_min" tools:ignore="UnusedResources">250dp</dimen>
<dimen name="mozac_browser_menu_width_min" tools:ignore="UnusedResources">112dp</dimen>
<dimen name="mozac_browser_menu_width_max" tools:ignore="UnusedResources">314dp</dimen>
<dimen name="mozac_browser_menu_corner_radius">8dp</dimen>
<dimen name="toolbar_elevation">7dp</dimen>

View File

@ -566,6 +566,8 @@
<string name="bookmark_select_folder">Select folder</string>
<!-- Confirmation message for a dialog confirming if the user wants to delete the selected folder -->
<string name="bookmark_delete_folder_confirmation_dialog">Are you sure you want to delete this folder?</string>
<!-- Confirmation message for a dialog confirming if the user wants to delete multiple items including folders. Parameter will be replaced by app name. -->
<string name="bookmark_delete_multiple_folders_confirmation_dialog">%s will delete the selected items.</string>
<!-- Snackbar title shown after a folder has been deleted. This first parameter is the name of the deleted folder -->
<string name="bookmark_delete_folder_snackbar">Deleted %1$s</string>
<!-- Screen title for adding a bookmarks folder -->
@ -620,8 +622,10 @@
<!-- Bookmark snackbar message on deletion
The first parameter is the host part of the URL of the bookmark deleted, if any -->
<string name="bookmark_deletion_snackbar_message">Deleted %1$s</string>
<!-- Bookmark snackbar message on deleting multiple bookmarks -->
<!-- Bookmark snackbar message on deleting multiple bookmarks not including folders-->
<string name="bookmark_deletion_multiple_snackbar_message_2">Bookmarks deleted</string>
<!-- Bookmark snackbar message on deleting multiple bookmarks including folders-->
<string name="bookmark_deletion_multiple_snackbar_message_3">Deleting selected folders</string>
<!-- Bookmark undo button for deletion snackbar action -->
<string name="bookmark_undo_deletion">UNDO</string>

View File

@ -0,0 +1,128 @@
/* 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.addons
import android.net.Uri
import android.text.method.LinkMovementMethod
import android.view.LayoutInflater
import android.view.View
import io.mockk.mockk
import io.mockk.verify
import kotlinx.android.synthetic.main.fragment_add_on_details.view.*
import mozilla.components.feature.addons.Addon
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.R
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
@RunWith(FenixRobolectricTestRunner::class)
class AddonDetailsViewTest {
private lateinit var view: View
private lateinit var interactor: AddonDetailsInteractor
private lateinit var detailsView: AddonDetailsView
private val baseAddon = Addon(
id = "",
translatableDescription = mapOf(
Addon.DEFAULT_LOCALE to "Some blank addon\nwith a blank line"
),
updatedAt = "2020-11-23T08:00:00Z"
)
@Before
fun setup() {
view = LayoutInflater.from(testContext).inflate(R.layout.fragment_add_on_details, null)
interactor = mockk(relaxed = true)
detailsView = AddonDetailsView(view, interactor)
}
@Test
fun `bind addons rating`() {
detailsView.bind(baseAddon.copy(
rating = null
))
assertEquals(0f, view.rating_view.rating)
detailsView.bind(baseAddon.copy(
rating = Addon.Rating(
average = 4.3f,
reviews = 100
)
))
assertEquals("4.30/5", view.rating_view.contentDescription)
assertEquals(4.5f, view.rating_view.rating)
assertEquals("100", view.users_count.text)
}
@Test
fun `bind addons website`() {
detailsView.bind(baseAddon.copy(
siteUrl = "https://mozilla.org"
))
view.home_page_label.performClick()
verify { interactor.openWebsite(Uri.parse("https://mozilla.org")) }
}
@Test
fun `bind addons last updated`() {
detailsView.bind(baseAddon)
assertEquals("Nov 23, 2020", view.last_updated_text.text)
}
@Test
fun `bind addons version`() {
detailsView.bind(baseAddon.copy(
version = "1.0.0",
installedState = null
))
assertEquals("1.0.0", view.version_text.text)
view.version_text.performLongClick()
verify(exactly = 0) { interactor.showUpdaterDialog(any()) }
detailsView.bind(baseAddon.copy(
version = "1.0.0",
installedState = Addon.InstalledState(
id = "",
version = "2.0.0",
optionsPageUrl = null
)
))
assertEquals("2.0.0", view.version_text.text)
view.version_text.performLongClick()
verify { interactor.showUpdaterDialog(any()) }
}
@Test
fun `bind addons authors`() {
val baseAuthor = Addon.Author("", "", "", "")
detailsView.bind(baseAddon.copy(
authors = listOf(
baseAuthor.copy(name = " Sarah Jane"),
baseAuthor.copy(name = "John Smith ")
)
))
assertEquals("Sarah Jane, John Smith", view.author_text.text)
}
@Test
fun `bind addons details`() {
detailsView.bind(baseAddon)
assertEquals(
"Some blank addon\nwith a blank line",
view.details.text.toString()
)
assertTrue(view.details.movementMethod is LinkMovementMethod)
}
}

View File

@ -42,7 +42,7 @@ class BookmarkControllerTest {
private val navController: NavController = mockk(relaxed = true)
private val showSnackbar: (String) -> Unit = mockk(relaxed = true)
private val deleteBookmarkNodes: (Set<BookmarkNode>, Event) -> Unit = mockk(relaxed = true)
private val deleteBookmarkFolder: (BookmarkNode) -> Unit = mockk(relaxed = true)
private val deleteBookmarkFolder: (Set<BookmarkNode>) -> Unit = mockk(relaxed = true)
private val invokePendingDeletion: () -> Unit = mockk(relaxed = true)
private val homeActivity: HomeActivity = mockk(relaxed = true)
@ -240,10 +240,10 @@ class BookmarkControllerTest {
@Test
fun `handleBookmarkDeletion for a folder should properly call the delete folder delegate`() {
controller.handleBookmarkFolderDeletion(subfolder)
controller.handleBookmarkFolderDeletion(setOf(subfolder))
verify {
deleteBookmarkFolder(subfolder)
deleteBookmarkFolder(setOf(subfolder))
}
}

View File

@ -196,7 +196,7 @@ class BookmarkFragmentInteractorTest {
interactor.onDelete(setOf(subfolder))
verify {
bookmarkController.handleBookmarkFolderDeletion(subfolder)
bookmarkController.handleBookmarkFolderDeletion(setOf(subfolder))
}
}

View File

@ -60,7 +60,9 @@ class HistoryFragmentStoreTest {
fun finishSync() = runBlocking {
val initialState = HistoryFragmentState(
items = listOf(),
mode = HistoryFragmentState.Mode.Syncing
mode = HistoryFragmentState.Mode.Syncing,
pendingDeletionIds = emptySet(),
isDeletingItems = false
)
val store = HistoryFragmentStore(initialState)
@ -71,16 +73,22 @@ class HistoryFragmentStoreTest {
private fun emptyDefaultState(): HistoryFragmentState = HistoryFragmentState(
items = listOf(),
mode = HistoryFragmentState.Mode.Normal
mode = HistoryFragmentState.Mode.Normal,
pendingDeletionIds = emptySet(),
isDeletingItems = false
)
private fun oneItemEditState(): HistoryFragmentState = HistoryFragmentState(
items = listOf(),
mode = HistoryFragmentState.Mode.Editing(setOf(historyItem))
mode = HistoryFragmentState.Mode.Editing(setOf(historyItem)),
pendingDeletionIds = emptySet(),
isDeletingItems = false
)
private fun twoItemEditState(): HistoryFragmentState = HistoryFragmentState(
items = listOf(),
mode = HistoryFragmentState.Mode.Editing(setOf(historyItem, newHistoryItem))
mode = HistoryFragmentState.Mode.Editing(setOf(historyItem, newHistoryItem)),
pendingDeletionIds = emptySet(),
isDeletingItems = false
)
}

View File

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